SwiftUI: How to dismiss the keyboard?

Hi,

I have two TextFields and a button.

When i tap on this button i need to dismiss the keyboard, how?


Also, how to get the size of the keyboard, i just trying to shift up all elements when the keyboard appears.

Thanks in advance.

Replies

Hi,

in order to resize the view to shift up the keybord you can use this code:

import Foundation
import SwiftUI
import Combine
import UIKit

struct AdaptsToSoftwareKeyboard: ViewModifier {
    
    @State var currentHeight: CGFloat = 0


    func body(content: Content) -> some View {
        content
            .padding(.bottom, currentHeight).animation(.easeOut(duration: 0.25))
            .edgesIgnoringSafeArea(currentHeight == 0 ? Edge.Set() : .bottom)
            .onAppear(perform: subscribeToKeyboardChanges)
    }

    //MARK: - Keyboard Height
    private let keyboardHeightOnOpening = NotificationCenter.default
        .publisher(for: UIResponder.keyboardWillShowNotification)
        .map { $0.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect }
        .map { $0.height }

    
    private let keyboardHeightOnHiding = NotificationCenter.default
        .publisher(for: UIResponder.keyboardWillHideNotification)
        .map {_ in return CGFloat(0) }
    
    //MARK: - Subscriber to Keyboard's changes
    
    private func subscribeToKeyboardChanges() {
        
        _ = Publishers.Merge(keyboardHeightOnOpening, keyboardHeightOnHiding)
            .subscribe(on: RunLoop.main)
            .sink { height in
                if self.currentHeight == 0 || height == 0 {
                    self.currentHeight = height
                }
        }
    }
}

And then you add to the view you want to shift the following modifier:

.modifier(AdaptsToSoftwareKeyboard())


In order to dismiss the keyboard you can use this code:

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

Good solution. What it doesn't do is clean up the publishers when the views go away, so if you add a .print() to the Publishers.Merge chain, attach this to a view that comes in goes in a navigation stack, you will see that every time this views shows the number of prints increases by one because the publisher(s) are not canceled. Adding a .onDisapper to the body doesn't get called, so I don't see an obvious cleanup point.


My solution was similar except that I create one of these that gets passed down through the environment. Each view can use that value as padding as needed. No cleanup is needed for the publishers since only one is created.


I prefer your solution, though if we could figure out how to stop the notifications when they aren't needed anymore.