0

I have the list which contains a form of 5 Textfields, One Image & One button. Following is the ref from AppCoda. The issue I am facing here is I cannot show the last textfield when the keyboard is up. Found many relevant answers, but none of them is helpful. Also, I want to remove the animation from the list when clicked or long pressed.

Video Here

Any Help will be appreciated!

Ravi B
  • 1,574
  • 2
  • 14
  • 31

1 Answers1

1

I have checkout link which you have added in question. Blaaahhhh, very irritating due to some syntax has been changed. Anyways, I'm not going to explain whole tutorial that you already have done.

I have took reference from here: https://stackoverflow.com/a/56721268/6144643

Update LabelTextField code as follows.

struct LabelTextField: View {

    var label: String = ""
    var placeHolder: String = ""
    var tag: Int? = nil
    var kGuardian: KeyboardGuardian
    @State var textField: String = ""

    var body: some View {
        VStack(alignment: .leading) {
            Text(label)
                .font(.headline)
            TextField(placeHolder, text: $textField, onEditingChanged: {
                if $0 { self.kGuardian.showField = self.tag! }
            })
                .padding(.all)
                .background(Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0))
                .cornerRadius(5.0)
            }
        .padding()
        .listRowInsets(EdgeInsets())
    }
}

Update code in ContentView to initialise LabelTextField as follow:

import SwiftUI
import Combine

struct ContentView: View {

    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: 5)

    var body: some View {

        NavigationView {
            List {

                VStack(alignment: .leading) {
                    LabelTextField(label: "NAME", placeHolder: "Fill in the restaurant name", tag: 0, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[0]).foregroundColor(.clear))

                    LabelTextField(label: "TYPE", placeHolder: "Fill in the restaurant type", tag: 1, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[1]).foregroundColor(.clear))

                    LabelTextField(label: "ADDRESS", placeHolder: "Fill in the restaurant address", tag: 2, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[2]).foregroundColor(.clear))

                    LabelTextField(label: "PHONE", placeHolder: "Fill in the restaurant phone", tag: 3, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[3]).foregroundColor(.clear))

                    LabelTextField(label: "DESCRIPTION", placeHolder: "Fill in the restaurant description", tag: 4, kGuardian: kGuardian, textField: "")
                        .background(GeometryGetter(rect: $kGuardian.rects[4]).foregroundColor(.clear))

                    RoundedButton().padding(.top, 20)
                }
                .padding(.top, 20)
                .listRowInsets(EdgeInsets())
                .offset(y: kGuardian.slide).animation(Animation.linear(duration: 0.3))
            }
            .navigationBarTitle(Text("New Restaurant"), displayMode: .inline)
            .navigationBarItems(trailing:
                Button(action: {

                }, label: {
                    Text("Cancel")
                })
            )
        }
    }
}

struct RoundedButton: View {
    var body: some View {
        Button(action: {}) {
            HStack {
                Spacer()
                Text("Save")
                    .font(.headline)
                    .foregroundColor(.white)
                Spacer()
            }
        }
        .padding(.vertical, 10.0)
        .background(Color.red)
        .cornerRadius(4.0)
        .padding(.horizontal, 50)
    }
}

Add following code which are view that absorbs the size and position of its parent view.

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { geometry in
            Group { () -> ShapeView<Rectangle, Color> in
                DispatchQueue.main.async {
                    self.rect = geometry.frame(in: .global)
                }

                return Rectangle().fill(Color.clear) as! ShapeView<Rectangle, Color>
            }
        }
    }
}

Then add following code for keyboard hide/show notification.

final class KeyboardGuardian: ObservableObject {

    let objectWillChange = PassthroughSubject<Void, Never>()

    public var rects: Array<CGRect>
    public var keyboardRect: CGRect = CGRect()

    // keyboardWillShow notification may be posted repeatedly,
    // this flag makes sure we only act once per keyboard appearance
    public var keyboardIsHidden = true

    public var slide: CGFloat = 0 {
        didSet {
            objectWillChange.send()
        }
    }

    public var showField: Int = 0 {
        didSet {
            updateSlide()
        }
    }

    init(textFieldCount: Int) {
        self.rects = Array<CGRect>(repeating: CGRect(), count: textFieldCount)

        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyBoardDidHide(notification:)), name: UIResponder.keyboardDidHideNotification, object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc func keyBoardWillShow(notification: Notification) {
        if keyboardIsHidden {
            keyboardIsHidden = false
            if let rect = notification.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect {
                keyboardRect = rect
                updateSlide()
            }
        }
    }

    @objc func keyBoardDidHide(notification: Notification) {
        keyboardIsHidden = true
        updateSlide()
    }

    func updateSlide() {
        if keyboardIsHidden {
            slide = 0
        } else {
            let tfRect = self.rects[self.showField]
            let diff = keyboardRect.minY - tfRect.maxY
            print("tfRect", tfRect, "\nself.showField", self.showField)
            if diff > 0 {
                slide += diff
            } else {
                slide += min(diff, 0)
            }
        }
    }
}
Sagar Chauhan
  • 5,715
  • 2
  • 22
  • 56