SwiftUI का उपयोग करते समय कीबोर्ड को कैसे छिपाएं?


96

नीचे के मामलों के लिए keyboardउपयोग करके कैसे छिपाएं SwiftUI?

मामला एक

मेरे पास उपयोगकर्ता को बटन क्लिक TextFieldकरने पर मुझे छिपाने की आवश्यकता है ।keyboardreturn

केस 2

मेरे पास है TextFieldऔर मुझे उस keyboardसमय छिपाने की आवश्यकता है जब उपयोगकर्ता बाहर टैप करता है।

मैं इसका उपयोग कैसे कर सकता हूं SwiftUI?

ध्यान दें:

मैंने एक सवाल नहीं पूछा है UITextField। मैं इसका उपयोग करके करना चाहता हूं SwifUI.TextField


29
@DannyBuonocore मेरे सवाल को फिर से ध्यान से पढ़ें!
हितेश सुरानी

9
@DannyBuonocore यह उल्लेखित प्रश्न का डुप्लिकेट नहीं है। यह सवाल स्विफ्टयूआई के बारे में है, और अन्य सामान्य UIKit है
जॉनीकुट्टी

1
@DannyBuonocore ने UIKit और SwiftUI के बीच अंतर जानने के लिए developer.apple.com/documentation/swiftui को देखा है । धन्यवाद
हितेश Surani

मैंने अपना समाधान यहां जोड़ा मुझे आशा है कि यह आपकी मदद करेगा।
विक्टर कुश्नरोव

यहां अधिकांश समाधान वांछित के रूप में काम नहीं करते हैं, क्योंकि वे अन्य नियंत्रण नल पर वांछित प्रतिक्रियाओं को अक्षम करते हैं। एक काम कर समाधान यहां पाया जा सकता: forums.developer.apple.com/thread/127196
हार्डी

जवाबों:


86

आप साझा आवेदन पर कार्रवाई भेजकर पहले उत्तरदाता को इस्तीफा देने के लिए मजबूर कर सकते हैं:

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()
    }
}

2
.keyWindowअब पदावनत हो गया है। लोरेंजो सेंटिनी का जवाब देखें ।
लिनसग्राफर्थ

4
इसके अलावा, .tapActionइसका नाम बदल दिया गया है.onTapGesture
21us पर LinusGeffarth

जब वैकल्पिक नियंत्रण सक्रिय हो जाता है तो क्या कीबोर्ड को खारिज कर दिया जा सकता है? stackoverflow.com/questions/58643512/…
यर्म

1
क्या व्हाइटबैक ग्राउंड के बिना ऐसा करने का एक तरीका है, मैं स्पेसर्स का उपयोग कर रहा हूं और मुझे स्पेसर पर एक टैप जेस्चर का पता लगाने के लिए इसकी आवश्यकता है। इसके अलावा सफेद पृष्ठभूमि की रणनीति नए iPhones पर एक समस्या पैदा करती है जहां अब अतिरिक्त स्क्रीन स्थान है। किसी भी मदद की सराहना की!
जोसेफ अस्त्रहन

2
शायद यह भी ध्यान देने योग्य है कि UIApplicationUIKit का हिस्सा है, इसलिए किसी को इसकी आवश्यकता है import UIKit
एलियनबश

63

बहुत प्रयासों के बाद मुझे एक समाधान मिला (जो वर्तमान में) किसी भी नियंत्रण को अवरुद्ध नहीं करता है - इशारे पहचानकर्ता को जोड़ना UIWindow

  1. यदि आप केवल टैप आउट (बिना ड्रग्स को हैंडल किए) पर कीबोर्ड बंद करना चाहते हैं - तो यह सिर्फ UITapGestureRecognizerऔर सिर्फ स्टेप 3 का उपयोग करने के लिए पर्याप्त है :
  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
        }
    }
    

अब किसी भी दृश्य का कोई भी कीबोर्ड टच पर बंद या बाहर की ओर खींचा जाएगा।

PS यदि आप केवल विशिष्ट TextFields बंद करना चाहते हैं - तो जब भी TextField के कॉलबैक को कॉल करें तो विंडो में जेस्चर पहचानकर्ता को जोड़ें और हटा दें onEditingChanged


3
यह उत्तर सबसे ऊपर होना चाहिए। दृश्य में अन्य नियंत्रण होने पर अन्य उत्तर विफल हो जाते हैं।
इम्तहाथ

1
इस व्यवहार को ठीक करने के लिए @RolandLariotte अपडेटेड उत्तर, AnyGestureRecognizer का नया कार्यान्वयन देखें
मिखाइल

2
बहुत बढ़िया जवाब। निर्दोष रूप से काम करता है। @ मिखाइल को वास्तव में यह जानने में दिलचस्पी है कि आप विशेष रूप से कुछ टेक्स्टफिल्ड के लिए जेस्चर पहचानकर्ता को कैसे हटाते हैं (मैंने टैग के साथ एक स्वत: पूर्ण बनाया है, इसलिए हर बार जब मैं सूची में एक तत्व को टैप करता हूं, तो मैं नहीं चाहता कि यह विशिष्ट टेक्स्टफील्ड फोकस खो जाए)
पास्ता

1
यह समाधान वास्तव में बहुत अच्छा है, लेकिन इसे 3 महीने तक उपयोग करने के बाद, दुर्भाग्य से मुझे एक बग मिला है, सीधे इस तरह के हैक के कारण। कृपया, आपके साथ भी ऐसा ही होने के बारे में अवगत
रहें

1
शानदार जवाब! मुझे आश्चर्य है कि यह बिना डेजर्टलेगेट के आईओएस 14 के साथ कैसे लागू किया जाएगा?
डोम

27

@ रयानटीसीबी का जवाब अच्छा है; यहाँ कुछ परिशोधन किए गए हैं जो संभावित दुर्घटना से बचने और उपयोग करने के लिए सरल बनाते हैं:

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 के साथ परीक्षण किया गया)


6
हम्म - अन्य नियंत्रणों पर टैपिंग अब रजिस्टर नहीं करता है। घटना को निगला जाता है।
यम

मैं यह नहीं दोहरा सकता कि - यह अभी भी मेरे लिए काम कर रहा है Apple से नवीनतम बूंदों का उपयोग 11/1 के रूप में। क्या यह काम करता है और फिर आपके लिए काम करना बंद कर देता है, या ??
फिलाडुर

यदि आपके पास फॉर्म में एक DatePicker है, तो DatePicker को कोई भी अधिक नहीं दिखाया जाएगा
अल्बर्ट

@ अल्बर्ट - यह सच है; इस दृष्टिकोण का उपयोग करने के लिए, आपको उन चीजों को तोड़ना होगा जहां आइटम डिस्मिसिंगकेयबोर्ड () के साथ एक महीन दानेदार स्तर तक सजाए गए हैं जो उन तत्वों पर लागू होते हैं जिन्हें खारिज करना चाहिए और डेटपिकर से बचना चाहिए।
फेल्डर

इस कोड का उपयोग चेतावनी को पुन: पेश करेगाCan't find keyplane that supports type 4 for keyboard iPhone-PortraitChoco-NumberPad; using 25686_PortraitChoco_iPhone-Simple-Pad_Default
np2314

25

मैंने एक नेविगेशन दृश्य के अंदर एक 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)})

1
यह अजीब व्यवहार करने के लिए onDelete (हटाने के लिए स्वाइप) का नेतृत्व करेगा।
तारेक हल्क

यह अच्छा है, लेकिन नल के बारे में क्या?
डैनियलजैन्ची

22

स्विफ्टयूआई 2

यहाँ के लिए एक अद्यतन समाधान है SwiftUI 2 / iOS 14 (मूल रूप से प्रस्तावित यहाँ मिखाइल द्वारा)।

यदि आप SwiftUI जीवनचक्र का उपयोग करते हैं तो यह न तो उपयोग करता है AppDelegateऔर न ही SceneDelegateगायब है।

@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)
    }
}

2
यह शीर्ष पर होना चाहिए क्योंकि नए SwiftUI जीवनचक्र को ध्यान में रखता है।
carlosobedgomez

यह बहुत अच्छा काम करता है। हालाँकि अगर मैं टेक्स्ट फ़ील्ड में डबल टैप करता हूँ, तो टेक्स्ट को चुनने के बजाय अब कीबोर्ड गायब हो जाता है। किसी भी विचार मैं चयन के लिए डबल टैप की अनुमति कैसे दे सकता हूं?
गैरी

@ गैरी नीचे के विस्तार में, यदि आप अन्य इशारों के दौरान टैप का पता नहीं लगाना चाहते हैं तो आप टिप्पणी के साथ लाइन को गलत पर सेट कर सकते हैं । बस इसे सेट करें return false
pawello2222

इसे झूठे कामों के लिए सेट करना, लेकिन तब कीबोर्ड भी खारिज नहीं करता है अगर कोई व्यक्ति लंबे क्षेत्र को दबाता है या पाठ क्षेत्र के बाहर स्क्रॉल या स्क्रॉल करता है। क्या केवल डबल क्लिक के लिए इसे सेट करने का कोई तरीका है (पाठ क्षेत्र के अंदर अधिमानतः डबल क्लिक, लेकिन यहां तक ​​कि सभी डबल क्लिक ही करेंगे)।
गैरी

2
अपने स्वयं के प्रश्न का उत्तर देने के लिए, मैंने इसे सही पर सेट किया, और फिर मैंने टैपग्योरचर = एनीग्योरुरेकोगेंज़र (...) सेट किया, जिसे मिखाइल ने टैपग्रेचर के बजाय अपने उत्तर में बनाया था = यूआईटीएपग्योरुरेक्रोगाइज़र (...)। यह पाठ क्षेत्र के भीतर पाठ को चुनने के लिए डबल टैप की अनुमति देता है, जबकि विभिन्न इशारों को पाठ क्षेत्र के बाहर कीबोर्ड को छिपाने की अनुमति देता है।
गैरी

21

मुझे कीबोर्ड को खारिज करने का एक और तरीका मिला जिसमें keyWindowसंपत्ति तक पहुंचने की आवश्यकता नहीं है ; तथ्य यह है कि संकलक एक चेतावनी का उपयोग करके वापस देता है

UIApplication.shared.keyWindow?.endEditing(true)

'keyWindow' को iOS 13.0 में पदावनत किया गया: इसका उपयोग उन अनुप्रयोगों के लिए नहीं किया जाना चाहिए जो कई दृश्यों का समर्थन करते हैं क्योंकि यह सभी कनेक्टेड दृश्यों में एक महत्वपूर्ण विंडो देता है

इसके बजाय मैंने इस कोड का उपयोग किया:

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

15

'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()
        }
    }

यह आपके ऐप में कीबोर्ड का उपयोग करने वाले प्रत्येक दृश्य के लिए पर्याप्त है ...


4
यह एक और समस्या देता है - मेरे पास टेक्स्टफील्ड के साथ फॉर्म {} में एक पिकर है, यह अनुत्तरदायी बन गया। मुझे इस विषय में सभी उत्तरों का उपयोग करके इसका हल नहीं मिला। लेकिन आपका जवाब कहीं और नल के साथ कीबोर्ड को खारिज करने के लिए अच्छा है - यदि आप पिकर का उपयोग नहीं करते हैं।
नालोव

नमस्ते। मेरा कोड `` `वैर बॉडी: कुछ दृश्य {नेवीगेशन व्यू {फॉर्म {सेक्शन {TextField (" टाइपोमेलेबल ", टेक्स्ट: $ c)} सेक्शन {पिकर (" नाम ", चयन: $ sel) {फॉरएच (0) [200] ) {पाठ ("(self.array [$ 0])%")}}} `` `कीबोर्ड कहीं और टैप करने पर खारिज कर दिया जाता है, लेकिन पिकर अनुत्तरदायी हो गया। मुझे इसे बनाने का कोई तरीका नहीं मिला।
नलोव

2
हाय फिर, इस समय मेरे पास दो समाधान हैं: पहला - रिटर्न बटन पर खारिज किए गए देशी कीबोर्ड का उपयोग करना, दूसरा - थोड़ा सा दोहन को बदलना है (उर्फ 'костыль') - window.rootViewController = UIHostingController (rootView) : contentView.onTapGesture (गणना: 2, प्रदर्शन: {window.endEditing (true)})) आशा है कि यह आपकी मदद करता है ...
Dim Novo

नमस्ते। धन्यवाद। दूसरा रास्ता इसे हल किया। मैं संख्यात्मक पैड का उपयोग कर रहा हूं, इसलिए उपयोगकर्ता केवल संख्या दर्ज कर सकते हैं, इसमें वापसी कुंजी नहीं है। टैपिंग के साथ खारिज करना वह था जो मैं खोज रहा था।
नालोव

यह कारण होगा सूची नेविगेट नहीं किया जा सकता है।
कुई मिंग्डा

11

मेरा समाधान सॉफ्टवेयर कीबोर्ड को छिपाने का तरीका है जब उपयोगकर्ता बाहर टैप करते हैं। आप उपयोग करने की आवश्यकता contentShapeके साथ onLongPressGestureपूरे दृश्य कंटेनर पता लगाने के लिए। onTapGestureअवरुद्ध ध्यान से बचने के लिए आवश्यक है TextField। आप onTapGestureइसके बजाय उपयोग कर सकते हैं , onLongPressGestureलेकिन नेविगेशनबार आइटम काम नहीं करेंगे।

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

struct KeyboardAvoiderDemo: View {
    @State var text = ""
    var body: some View {
        VStack {
            TextField("Demo", text: self.$text)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .contentShape(Rectangle())
        .onTapGesture {}
        .onLongPressGesture(
            pressing: { isPressed in if isPressed { self.endEditing() } },
            perform: {})
    }
}

यह बहुत अच्छा काम किया, मैंने इसे थोड़ा अलग तरीके से इस्तेमाल किया और यह सुनिश्चित करना पड़ा कि इसे मुख्य धागे पर बुलाया गया था।
keegan3d

7

इस संशोधक को उस दृश्य में जोड़ें जिसे आप उपयोगकर्ता के टैप का पता लगाना चाहते हैं

.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)

        }

7

मैं इसका उपयोग करना पसंद .onLongPressGesture(minimumDuration: 0)करता हूं , जिसके कारण कीबोर्ड TextViewसक्रिय नहीं होता है जब दूसरा सक्रिय होता है (साइड इफेक्ट .onTapGesture)। छिपा कीबोर्ड कोड एक पुन: प्रयोज्य कार्य हो सकता है।

.onTapGesture(count: 2){} // UI is unresponsive without this line. Why?
.onLongPressGesture(minimumDuration: 0, maximumDistance: 0, pressing: nil, perform: hide_keyboard)

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

फिर भी इस विधि का उपयोग करके एक झिलमिलाहट।
डैनियल रयान

यह बहुत अच्छा काम किया, मैंने इसे थोड़ा अलग तरीके से इस्तेमाल किया और यह सुनिश्चित करना पड़ा कि इसे मुख्य धागे पर बुलाया गया था।
keegan3d

6

क्योंकि keyWindowपदावनत है।

extension View {
    func endEditing(_ force: Bool) {
        UIApplication.shared.windows.forEach { $0.endEditing(force)}
    }
}

1
forceपैरामीटर नहीं किया जाता है। यह होना चाहिए{ $0.endEditing(force)}
Davide

5

लगता है जैसे endEditingसमाधान केवल एक ही है जैसे @rraphael ने बताया।
अब तक मैंने देखा सबसे साफ उदाहरण यह है:

extension View {
    func endEditing(_ force: Bool) {
        UIApplication.shared.keyWindow?.endEditing(force)
    }
}

और फिर में इसका उपयोग कर onCommit:


2
.keyWindowअब पदावनत हो गया है। लोरेंजो सेंटिनी का जवाब देखें ।
लिनसग्राफर्थ

यह iOS 13+
अहमदेज़ा

4

@ फेल्डर (जो @ रेयानटीसीबी पर आधारित था) द्वारा जवाब पर विस्तार से, यहां एक और भी अधिक स्पष्ट और शक्तिशाली समाधान है जो आपको अन्य इशारों पर कीबोर्ड को खारिज करने की अनुमति देता है onTapGesture , आप निर्दिष्ट कर सकते हैं कि आप फ़ंक्शन कॉल में क्या चाहते हैं।

प्रयोग

// MARK: - View
extension RestoreAccountInputMnemonicScreen: View {
    var body: some View {
        List(viewModel.inputWords) { inputMnemonicWord in
            InputMnemonicCell(mnemonicInput: inputMnemonicWord)
        }
        .dismissKeyboard(on: [.tap, .drag])
    }
}

या उपयोग All.gestures(बस चीनी के लिए Gestures.allCasesfor)

.dismissKeyboard(on: All.gestures)

कोड

enum All {
    static let gestures = all(of: Gestures.self)

    private static func all<CI>(of _: CI.Type) -> CI.AllCases where CI: CaseIterable {
        return CI.allCases
    }
}

enum Gestures: Hashable, CaseIterable {
    case tap, longPress, drag, magnification, rotation
}

protocol ValueGesture: Gesture where Value: Equatable {
    func onChanged(_ action: @escaping (Value) -> Void) -> _ChangedGesture<Self>
}
extension LongPressGesture: ValueGesture {}
extension DragGesture: ValueGesture {}
extension MagnificationGesture: ValueGesture {}
extension RotationGesture: ValueGesture {}

extension Gestures {
    @discardableResult
    func apply<V>(to view: V, perform voidAction: @escaping () -> Void) -> AnyView where V: View {

        func highPrio<G>(
             gesture: G
        ) -> AnyView where G: ValueGesture {
            view.highPriorityGesture(
                gesture.onChanged { value in
                    _ = value
                    voidAction()
                }
            ).eraseToAny()
        }

        switch self {
        case .tap:
            // not `highPriorityGesture` since tapping is a common gesture, e.g. wanna allow users
            // to easily tap on a TextField in another cell in the case of a list of TextFields / Form
            return view.gesture(TapGesture().onEnded(voidAction)).eraseToAny()
        case .longPress: return highPrio(gesture: LongPressGesture())
        case .drag: return highPrio(gesture: DragGesture())
        case .magnification: return highPrio(gesture: MagnificationGesture())
        case .rotation: return highPrio(gesture: RotationGesture())
        }

    }
}

struct DismissingKeyboard: ViewModifier {

    var gestures: [Gestures] = Gestures.allCases

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

        return gestures.reduce(content.eraseToAny()) { $1.apply(to: $0, perform: action) }
    }
}

extension View {
    dynamic func dismissKeyboard(on gestures: [Gestures] = Gestures.allCases) -> some View {
        return ModifiedContent(content: self, modifier: DismissingKeyboard(gestures: gestures))
    }
}

सावधानी का शब्द

कृपया ध्यान दें कि यदि आप सभी इशारों का उपयोग करते हैं तो वे संघर्ष कर सकते हैं और मैं इसे हल करने वाले किसी भी समाधान के साथ नहीं आया हूं।


इसका क्या मतलब है eraseToAny()
रवींद्र_भाटी 19

2

इस विधि आप के लिए अनुमति देता कीबोर्ड छिपाने पर स्पैसर !

सबसे पहले इस फ़ंक्शन को जोड़ें (क्रेडिट दिया गया: कैस्पर ज़ैंडबर्गेन , स्विफ्टुई से स्पेसर के स्टेक्स में टैप नहीं कर सकते )

extension Spacer {
    public func onTapGesture(count: Int = 1, perform action: @escaping () -> Void) -> some View {
        ZStack {
            Color.black.opacity(0.001).onTapGesture(count: count, perform: action)
            self
        }
    }
}

इसके बाद अगले 2 कार्य जोड़ें (क्रेडिट गिव टू रिप्रेल, इस सवाल से)

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

नीचे दिए गए फ़ंक्शन को आपके दृश्य वर्ग में जोड़ा जाएगा, बस अधिक विवरण के लिए यहां से शीर्ष उत्तर को रैफेल से संदर्भित करें।

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

अंत में, अब आप बस कॉल कर सकते हैं ...

Spacer().onTapGesture {
    self.endEditing()
}

यह किसी भी स्पेसर क्षेत्र को अब कीबोर्ड को बंद कर देगा। अब एक बड़े सफेद पृष्ठभूमि दृश्य के लिए कोई ज़रूरत नहीं है!

आप extensionTapGestures का समर्थन करने की आवश्यकता वाले किसी भी नियंत्रण की इस तकनीक को काल्पनिक रूप से लागू कर सकते हैं जो वर्तमान में ऐसा नहीं करते हैं और किसी भी स्थिति में कीबोर्ड को बंद करने की इच्छा के onTapGestureसाथ संयोजन में फ़ंक्शन को कॉल करते हैं self.endEditing()


मेरा सवाल अब यह है कि जब आप कीबोर्ड को इस तरह से दूर जाने के लिए एक टेक्स्टफील्ड पर कमिट करते हैं तो आप उसे कैसे ट्रिगर करते हैं? वर्तमान में 'कमिट' केवल तभी चालू होता है जब आप iOS कीबोर्ड पर रिटर्न कुंजी मारते हैं।
जोसेफ एस्ट्राहन

2

कृपया https://github.com/michaelhenry/KeyboardAvoider देखें

बस KeyboardAvoider {}अपने मुख्य दृश्य के शीर्ष पर शामिल करें और बस इतना ही।

KeyboardAvoider {
    VStack { 
        TextField()
        TextField()
        TextField()
        TextField()
    }

}

यह Textfields के साथ एक फॉर्म दृश्य के लिए काम नहीं करता है। पर्चा नहीं दिखा।
नेल्स

2

@ सज्जन के जवाब के आधार पर, यहां एक समाधान है जो आपको अपनी पसंद के अनुसार नल, लंबे प्रेस, ड्रैग, आवर्धन और रोटेशन इशारों पर कीबोर्ड को खारिज करने की अनुमति देता है।

यह समाधान XCode 11.4 में काम कर रहा है

@IMHiteshSurani द्वारा पूछे गए व्यवहार को प्राप्त करने के लिए उपयोग

struct MyView: View {
    @State var myText = ""

    var body: some View {
        VStack {
            DismissingKeyboardSpacer()

            HStack {
                TextField("My Text", text: $myText)

                Button("Return", action: {})
                    .dismissKeyboard(on: [.longPress])
            }

            DismissingKeyboardSpacer()
        }
    }
}

struct DismissingKeyboardSpacer: View {
    var body: some View {
        ZStack {
            Color.black.opacity(0.0001)

            Spacer()
        }
        .dismissKeyboard(on: Gestures.allCases)
    }
}

कोड

enum All {
    static let gestures = all(of: Gestures.self)

    private static func all<CI>(of _: CI.Type) -> CI.AllCases where CI: CaseIterable {
        return CI.allCases
    }
}

enum Gestures: Hashable, CaseIterable {
    case tap, longPress, drag, magnification, rotation
}

protocol ValueGesture: Gesture where Value: Equatable {
    func onChanged(_ action: @escaping (Value) -> Void) -> _ChangedGesture<Self>
}

extension LongPressGesture: ValueGesture {}
extension DragGesture: ValueGesture {}
extension MagnificationGesture: ValueGesture {}
extension RotationGesture: ValueGesture {}

extension Gestures {
    @discardableResult
    func apply<V>(to view: V, perform voidAction: @escaping () -> Void) -> AnyView where V: View {

        func highPrio<G>(gesture: G) -> AnyView where G: ValueGesture {
            AnyView(view.highPriorityGesture(
                gesture.onChanged { _ in
                    voidAction()
                }
            ))
        }

        switch self {
        case .tap:
            return AnyView(view.gesture(TapGesture().onEnded(voidAction)))
        case .longPress:
            return highPrio(gesture: LongPressGesture())
        case .drag:
            return highPrio(gesture: DragGesture())
        case .magnification:
            return highPrio(gesture: MagnificationGesture())
        case .rotation:
            return highPrio(gesture: RotationGesture())
        }
    }
}

struct DismissingKeyboard: ViewModifier {
    var gestures: [Gestures] = Gestures.allCases

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

        return gestures.reduce(AnyView(content)) { $1.apply(to: $0, perform: action) }
    }
}

extension View {
    dynamic func dismissKeyboard(on gestures: [Gestures] = Gestures.allCases) -> some View {
        return ModifiedContent(content: self, modifier: DismissingKeyboard(gestures: gestures))
    }
}

2

आप UIKit के साथ बातचीत से पूरी तरह से बच सकते हैं और इसे शुद्ध स्विफ्टयूआई में लागू कर सकते हैं । जब भी आप कीबोर्ड (स्वाइप, व्यू टैप, बटन एक्शन, ..) को खारिज करना चाहते हैं, तो .id(<your id>)अपने में एक संशोधक जोड़ें TextFieldऔर उसका मूल्य बदलें।

नमूना कार्यान्वयन:

struct MyView: View {
    @State private var text: String = ""
    @State private var textFieldId: String = UUID().uuidString

    var body: some View {
        VStack {
            TextField("Type here", text: $text)
                .id(textFieldId)

            Spacer()

            Button("Dismiss", action: { textFieldId = UUID().uuidString })
        }
    }
}

ध्यान दें कि मैंने केवल नवीनतम Xcode 12 बीटा में इसका परीक्षण किया था, लेकिन इसे बिना किसी समस्या के पुराने संस्करणों (यहां तक ​​कि Xcode 11) के साथ काम करना चाहिए।


1

कीबोर्ड की Returnकुंजी

टेक्स्टफिल्ड के बाहर टैपिंग के बारे में सभी उत्तरों के अलावा, आप कीबोर्ड को खारिज करना चाह सकते हैं जब उपयोगकर्ता कीबोर्ड पर रिटर्न कुंजी टैप करता है:

इस वैश्विक समारोह को परिभाषित करें:

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

और onCommitतर्क में उपयोग जोड़ें :

TextField("title", text: $text, onCommit:  {
    resignFirstResponder()
})

लाभ

  • आप इसे कहीं से भी कॉल कर सकते हैं
  • यह UIKit या SwiftUI (मैक ऐप्स में इस्तेमाल किया जा सकता है) पर निर्भर नहीं है
  • यह iOS 13 में भी काम करता है

डेमो

डेमो


0

अभी तक उपरोक्त विकल्पों ने मेरे लिए काम नहीं किया, क्योंकि मेरे पास फॉर्म और अंदर बटन, लिंक, पिकर हैं ...

मैं नीचे दिए गए कोड का निर्माण कर रहा हूं जो उपरोक्त उदाहरणों की मदद से काम कर रहा है।

import Combine
import SwiftUI

private class KeyboardListener: ObservableObject {
    @Published var keyabordIsShowing: Bool = false
    var cancellable = Set<AnyCancellable>()

    init() {
        NotificationCenter.default
            .publisher(for: UIResponder.keyboardWillShowNotification)
            .sink { [weak self ] _ in
                self?.keyabordIsShowing = true
            }
            .store(in: &cancellable)

       NotificationCenter.default
            .publisher(for: UIResponder.keyboardWillHideNotification)
            .sink { [weak self ] _ in
                self?.keyabordIsShowing = false
            }
            .store(in: &cancellable)
    }
}

private struct DismissingKeyboard: ViewModifier {
    @ObservedObject var keyboardListener = KeyboardListener()

    fileprivate func body(content: Content) -> some View {
        ZStack {
            content
            Rectangle()
                .background(Color.clear)
                .opacity(keyboardListener.keyabordIsShowing ? 0.01 : 0)
                .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
                .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)
                }
        }
    }
}

extension View {
    func dismissingKeyboard() -> some View {
        ModifiedContent(content: self, modifier: DismissingKeyboard())
    }
}

उपयोग:

 var body: some View {
        NavigationView {
            Form {
                picker
                button
                textfield
                text
            }
            .dismissingKeyboard()

-2

Xcode 12 और iOS 14 के साथ जून / 2020 को जारी की गई स्विफ्टयूआई HideKeyboardOnTap () संशोधक को जोड़ता है। यह आपके केस नंबर को हल करना चाहिए। आपके केस नंबर 1 का समाधान Xcode 12 और iOS 14 के साथ मुफ्त में आता है: TextField के लिए डिफ़ॉल्ट कीबोर्ड स्वचालित रूप से छुपाता है जब रिटर्न बटन दबाया जाता है।


1
वहाँ iOS14 में कोई hideKeyboardOnTap संशोधक है
टेओ सर्टोरी

संभवतः भ्रमित हो गए: github.com/jeroenzonneveld/SwiftUIFormHelper/blob/develop/…
Jessy
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.