[ios] SwiftUI를 사용할 때 키보드를 숨기는 방법은 무엇입니까?

아래의 경우를 keyboard사용하여 숨기는 방법 SwiftUI?

사례 1

나는이 TextField와 나는를 숨길 필요가 keyboard사용자가 클릭 할 때 return버튼을 누릅니다.

사례 2

나는이 TextField와 나는를 숨길 필요가 keyboard사용자가 외부 탭 때.

어떻게 이것을 사용하여 할 수 SwiftUI있습니까?

노트 :

에 대해 질문하지 않았습니다 UITextField. 을 사용하여 수행하고 싶습니다 SwifUI.TextField.



답변

공유 응용 프로그램에 작업을 전송하여 첫 번째 응답자가 사임하도록 강제 할 수 있습니다.

extension UIApplication {
    func endEditing() {
        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
    }
}

이제이 방법을 사용하여 원할 때마다 키보드를 닫을 수 있습니다.

struct ContentView : View {
    @State private var name: String = ""

    var body: some View {
        VStack {
            Text("Hello \(name)")
            TextField("Name...", text: self.$name) {
                // Called when the user tap the return button
                // see `onCommit` on TextField initializer.
                UIApplication.shared.endEditing()
            }
        }
    }
}

탭 아웃으로 키보드를 닫으려면 탭 동작으로 전체 화면 흰색보기를 만들 수 있습니다. 그러면 다음이 트리거됩니다 endEditing(_:).

struct Background<Content: View>: View {
    private var content: Content

    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content()
    }

    var body: some View {
        Color.white
        .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        .overlay(content)
    }
}

struct ContentView : View {
    @State private var name: String = ""

    var body: some View {
        Background {
            VStack {
                Text("Hello \(self.name)")
                TextField("Name...", text: self.$name) {
                    self.endEditing()
                }
            }
        }.onTapGesture {
            self.endEditing()
        }
    }

    private func endEditing() {
        UIApplication.shared.endEditing()
    }
}


답변

많은 시도 끝에 나는 (현재) 어떤 컨트롤도 차단하지 않는 솔루션을 찾았습니다-제스처 인식기를 UIWindow.

  1. 드래그를 처리하지 않고 외부 탭에서만 키보드를 닫으려면 UITapGestureRecognizer3 단계 만 사용하면됩니다 .
  2. 모든 터치에서 작동하는 사용자 정의 제스처 인식기 클래스를 만듭니다.

    class AnyGestureRecognizer: UIGestureRecognizer {
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
            if let touchedView = touches.first?.view, touchedView is UIControl {
                state = .cancelled
    
            } else if let touchedView = touches.first?.view as? UITextView, touchedView.isEditable {
                state = .cancelled
    
            } else {
                state = .began
            }
        }
    
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
           state = .ended
        }
    
        override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
            state = .cancelled
        }
    }
    
  3. 에서 SceneDelegate.swift에서 func scene, 다음 코드를 추가합니다 :

    let tapGesture = AnyGestureRecognizer(target: window, action:#selector(UIView.endEditing))
    tapGesture.requiresExclusiveTouchType = false
    tapGesture.cancelsTouchesInView = false
    tapGesture.delegate = self //I don't use window as delegate to minimize possible side effects
    window?.addGestureRecognizer(tapGesture)
    
  4. UIGestureRecognizerDelegate동시 터치를 허용하도록 구현 합니다.

    extension SceneDelegate: UIGestureRecognizerDelegate {
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return true
        }
    }
    

이제 모든보기의 모든 키보드는 터치시 닫히거나 외부로 드래그됩니다.

추신 : 특정 TextField 만 닫으려면 TextField 콜백이 호출 될 때마다 창에 제스처 인식기를 추가하고 제거하십시오. onEditingChanged


답변

@RyanTCB의 대답은 좋습니다. 다음은 사용을 단순화하고 잠재적 인 충돌을 방지하는 몇 가지 개선 사항입니다.

struct DismissingKeyboard: ViewModifier {
    func body(content: Content) -> some View {
        content
            .onTapGesture {
                let keyWindow = UIApplication.shared.connectedScenes
                        .filter({$0.activationState == .foregroundActive})
                        .map({$0 as? UIWindowScene})
                        .compactMap({$0})
                        .first?.windows
                        .filter({$0.isKeyWindow}).first
                keyWindow?.endEditing(true)
        }
    }
}

‘버그 수정’은 단순히 keyWindow!.endEditing(true)적절해야한다는 것입니다 keyWindow?.endEditing(true)(예, 불가능하다고 주장 할 수 있습니다.)

더 흥미로운 것은 어떻게 사용할 수 있는지입니다. 예를 들어, 편집 가능한 필드가 여러 개인 양식이 있다고 가정합니다. 다음과 같이 포장하십시오.

Form {
    .
    .
    .
}
.modifier(DismissingKeyboard())

이제 키보드가없는 컨트롤을 탭하면 적절한 해제가 수행됩니다.

(베타 7로 테스트)


답변

NavigationView 내에서 TextField를 사용하는 동안 이것을 경험했습니다. 이것이 나의 해결책입니다. 스크롤을 시작할 때 키보드를 닫습니다.

NavigationView {
    Form {
        Section {
            TextField("Receipt amount", text: $receiptAmount)
            .keyboardType(.decimalPad)
           }
        }
     }
     .gesture(DragGesture().onChanged{_ in UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)})


답변

keyWindow속성에 액세스 할 필요가없는 키보드를 해제하는 다른 방법을 찾았습니다 . 사실 컴파일러는 다음을 사용하여 경고를 반환합니다.

UIApplication.shared.keyWindow?.endEditing(true)

‘keyWindow’는 iOS 13.0에서 더 이상 사용되지 않음 : 연결된 모든 장면에서 키 창을 반환하므로 여러 장면을 지원하는 응용 프로그램에 사용해서는 안됩니다.

대신 다음 코드를 사용했습니다.

UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)


답변

‘SceneDelegate.swift’파일의 SwiftUI는 다음을 추가합니다. .onTapGesture {window.endEditing (true)}

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Create the SwiftUI view that provides the window contents.
        let contentView = ContentView()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(
                rootView: contentView.onTapGesture { window.endEditing(true)}
            )
            self.window = window
            window.makeKeyAndVisible()
        }
    }

앱에서 키보드를 사용하는 각 뷰에 충분합니다.


답변

SwiftUI 2

다음은 업데이트 된 솔루션입니다. SwiftUI 2 / 아이폰 OS (14) (원래 제안 여기 미하일에 의해).

사용하지 않습니다 AppDelegate.SceneDelegate 당신이 SwiftUI 라이프 사이클을 사용하는 경우 누락 된을 :

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
        }
    }
}

extension UIApplication {
    func addTapGestureRecognizer() {
        guard let window = windows.first else { return }
        let tapGesture = UITapGestureRecognizer(target: window, action: #selector(UIView.endEditing))
        tapGesture.requiresExclusiveTouchType = false
        tapGesture.cancelsTouchesInView = false
        tapGesture.delegate = self
        window.addGestureRecognizer(tapGesture)
    }
}

extension UIApplication: UIGestureRecognizerDelegate {
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true // set to `false` if you don't want to detect tap during other gestures
    }
}

다음은 길게 누르기 제스처를 제외한 동시 제스처를 감지하는 방법의 예입니다.

extension UIApplication: UIGestureRecognizerDelegate {
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return !otherGestureRecognizer.isKind(of: UILongPressGestureRecognizer.self)
    }
}