简体   繁体   English

如何使用 SwiftUI 在多个屏幕上获取键盘高度并移动按钮

[英]How to get the keyboard height on multiple screens with SwiftUI and move the button

The following code gets the keyboard height when the keyboard is displayed and moves the button by the keyboard height.以下代码获取键盘显示时的键盘高度,并按键盘高度移动按钮。

This movement is performed in the same way at the transition source (ContentView) and the transition destination (SecibdContentView), but the button does not move at the transition destination.此移动在转换源 (ContentView) 和转换目标 (SecibdContentView) 处以相同方式执行,但按钮不会在转换目标处移动。

How can I make the buttons move the same on multiple screens?如何使按钮在多个屏幕上移动相同?

import SwiftUI

struct ContentView: View {
    @ObservedObject private var keyboard = KeyboardResponder()

    var body: some View {
        NavigationView {
            VStack {
                Text("ContentView")

                Spacer()

                NavigationLink(destination: SecondContentView()) {
                    Text("Next")
                }
                .offset(x: 0, y: -keyboard.currentHeight)
            }
        }
    }
}

import SwiftUI

struct SecondContentView: View {
    @ObservedObject private var keyboard = KeyboardResponder()

    var body: some View {
        VStack {
            Text("SubContentView")

            Spacer()

            NavigationLink(destination: ThirdContentView()) {
                Text("Next")
            }
            .offset(x: 0, y: -keyboard.currentHeight)
        }
    }
}

class KeyboardResponder: ObservableObject {
    private var _center: NotificationCenter
    @Published var currentHeight: CGFloat = 0

    init(center: NotificationCenter = .default) {
        _center = center
        _center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        _center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    deinit {
        _center.removeObserver(self)
    }

    @objc func keyBoardWillShow(notification: Notification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            currentHeight = keyboardSize.height
        }
    }

    @objc func keyBoardWillHide(notification: Notification) {
        currentHeight = 0
    }
}

Using ViewModifier使用 ViewModifier

You can use ViewModifier of swiftui is much simpler你可以使用 swiftui 的 ViewModifier 就简单多了

import SwiftUI
import Combine

struct KeyboardAwareModifier: ViewModifier {
    @State private var keyboardHeight: CGFloat = 0

    private var keyboardHeightPublisher: AnyPublisher<CGFloat, Never> {
        Publishers.Merge(
            NotificationCenter.default
                .publisher(for: UIResponder.keyboardWillShowNotification)
                .compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue }
                .map { $0.cgRectValue.height },
            NotificationCenter.default
                .publisher(for: UIResponder.keyboardWillHideNotification)
                .map { _ in CGFloat(0) }
       ).eraseToAnyPublisher()
    }

    func body(content: Content) -> some View {
        content
            .padding(.bottom, keyboardHeight)
            .onReceive(keyboardHeightPublisher) { self.keyboardHeight = $0 }
    }
}

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

And in your view在你看来

struct SomeView: View {
    @State private var someText: String = ""

    var body: some View {
        VStack {
            Spacer()
            TextField("some text", text: $someText)
        }.KeyboardAwarePadding()
    }
}

KeyboardAwarePadding() will automatically add a padding in your view, It's more elegant. KeyboardAwarePadding()会自动在你的视图中添加一个 padding,它更优雅。

SwiftUI + Combine SwiftUI + 组合

@Published var keyboardHeight: CGFloat = 0 // if one is in ViewModel: ObservableObject

private var cancellableSet: Set<AnyCancellable> = []
    
init() {
        
     NotificationCenter.default.publisher(for: UIWindow.keyboardWillShowNotification)
       .map {
             guard
                 let info = $0.userInfo,
                 let keyboardFrame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
                 else { return 0 }

             return keyboardFrame.height
         }
         .assign(to: \.keyboardHeight, on: self)
         .store(in: &cancellableSet)
        
     NotificationCenter.default.publisher(for: UIWindow.keyboardDidHideNotification)
         .map { _ in 0 }
         .assign(to: \.keyboardHeight, on: self)
         .store(in: &cancellableSet)
    }
    

Your code has a couple of things missing.您的代码缺少一些东西。 There's no NavigationView, and no TextFields to make the keyboard appear.没有 NavigationView,也没有 TextFields 来显示键盘。 Consider updating your code.考虑更新您的代码。

Anyway, the problem can be solved easily by replacing: keyboardFrameBeginUserInfoKey with keyboardFrameEndUserInfoKey .无论如何,可以通过替换: keyboardFrameBeginUserInfoKeykeyboardFrameEndUserInfoKey轻松解决问题。

Update:更新:

You also need to use the same KeyboardResponder, otherwise you will creating multiple instances.您还需要使用相同的 KeyboardResponder,否则您将创建多个实例。 Alternatively, you can put it into your Evironment.或者,您可以将其放入您的环境中。

And you forgot to include TextFields to your code, so the keyboard will show up to test it.并且您忘记在代码中包含 TextFields,因此键盘将显示以进行测试。

The following code works:以下代码有效:

import SwiftUI

struct ContentView: View {
    @ObservedObject private var keyboard = KeyboardResponder()

    var body: some View {
        NavigationView {
            VStack {
                Text("ContentView")
                TextField("enter text", text: .constant(""))
                Spacer()

                NavigationLink(destination: SecondContentView(keyboard: keyboard)) {
                    Text("Next")
                }
                .offset(x: 0, y: -keyboard.currentHeight)
            }
        }
    }
}

import SwiftUI

struct SecondContentView: View {
    @ObservedObject var keyboard: KeyboardResponder

    var body: some View {
        VStack {
            Text("SubContentView")
            TextField("enter text", text: .constant(""))
            Spacer()

            NavigationLink(destination: Text("ThirdContentView()")) {
                Text("Next")
            }
            .offset(x: 0, y: -keyboard.currentHeight)
        }
    }
}

class KeyboardResponder: ObservableObject {
    private var _center: NotificationCenter
    @Published var currentHeight: CGFloat = 0

    init(center: NotificationCenter = .default) {
        _center = center
        _center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        _center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    deinit {
        _center.removeObserver(self)
    }

    @objc func keyBoardWillShow(notification: Notification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            currentHeight = keyboardSize.height
        }
    }

    @objc func keyBoardWillHide(notification: Notification) {
        currentHeight = 0
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM