【问题标题】:Adjusting a view for keyboard appearing/disappearing: problem with animation curve调整键盘出现/消失的视图:动画曲线问题
【发布时间】:2020-04-04 01:32:32
【问题描述】:

我正在尝试创建一个 SwiftUI 视图修改器,它添加底部填充以响应屏幕键盘的出现或消失。这是我正在使用的方法:

struct KeyboardAdjustingModifier: ViewModifier {
    @ObservedObject private var responder = KeyboardResponder()

    func body(content: Content) -> some View {
        GeometryReader { proxy in
            content
                .padding(.bottom, self.bottomPadding(in: proxy))
        }
    }

    func bottomPadding(in proxy: GeometryProxy) -> CGFloat {
        if responder.endFrame == .zero {
            return 0
        } else {
            return max(0, proxy.frame(in: .global).maxY - self.responder.endFrame.minY)
        }
    }
}

fileprivate final class KeyboardResponder: ObservableObject {
    @Published var endFrame: CGRect = .zero

    init(notificationCenter: NotificationCenter = .default) {
        notificationCenter.addObserver(self,
                                       selector: #selector(keyboardWillShowOrChangeFrame(notification:)),
                                       name: UIResponder.keyboardWillShowNotification, object: nil)
        notificationCenter.addObserver(self,
                                       selector: #selector(keyboardWillShowOrChangeFrame(notification:)),
                                       name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
        notificationCenter.addObserver(self,
                                       selector: #selector(keyboardWillHide(notification:)),
                                       name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    @objc private func keyboardWillShowOrChangeFrame(notification: Notification) {
        withAnimation(Animation(curve: notification.animationCurve, duration: notification.duration)) {
            endFrame = notification.endFrame
        }
    }

    @objc private func keyboardWillHide(notification: Notification) {
        withAnimation(Animation(curve: notification.animationCurve, duration: notification.duration)) {
            endFrame = .zero
        }
    }

    struct Info {
        let curve: UIView.AnimationCurve
        let duration: TimeInterval
        let endFrame: CGRect
    }
}

fileprivate extension Animation {
    init(curve: UIView.AnimationCurve, duration: TimeInterval) {
        switch curve {
        case .easeInOut:
            self = .easeInOut(duration: duration)
        case .easeIn:
            self = .easeIn(duration: duration)
        case .easeOut:
            self = .easeOut(duration: duration)
        case .linear:
            self = .linear(duration: duration)
        @unknown default:
            self = .easeInOut(duration: duration)
        }
    }
}

fileprivate extension Notification {
    var animationCurve: UIView.AnimationCurve {
        guard let rawValue = (userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int) else {
            return UIView.AnimationCurve.linear
        }

        return UIView.AnimationCurve(rawValue: rawValue)!
    }

    var duration: TimeInterval {
        userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval ?? 0
    }

    var endFrame: CGRect {
        userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect ?? .zero
    }
}

我遇到的问题是 SwiftUI 动画曲线似乎与键盘的动画曲线完全不同:

有谁知道是否可以像这样将 SwiftUI 动画与 UIKit 同步?我将通知的 userInfo 中的动画曲线信息映射到 SwiftUI 动画曲线的方式很可能存在错误。还是我应该考虑另一种解决这个问题的方法?

我尝试的另一件事是使用UIHostingController 直接在 UIKit 中处理动画。它与键盘正确同步,但如果 SwiftUI 也在同时为内容制作动画,我在我的动画块中调用 layoutIfNeeded 时遇到了崩溃。

【问题讨论】:

    标签: ios swiftui


    【解决方案1】:

    这是我正在使用的,它几乎可以完美运行:

    演示

    代码

    public extension View {
        func adaptToKeyboard() -> some View {
            modifier(AdaptToSoftwareKeyboard())
        }
    }
    
    public struct AdaptToSoftwareKeyboard: ViewModifier {
        @State var currentHeight: CGFloat = 0
    
        public func body(content: Content) -> some View {
            content
                .padding(.bottom, currentHeight)
                .edgesIgnoringSafeArea(.bottom)
                .animation(.spring())
                .onAppear(perform: subscribeToKeyboardEvents)
        }
    
        private func subscribeToKeyboardEvents() {
            NotificationCenter.Publisher(
                center: NotificationCenter.default,
                name: UIResponder.keyboardWillShowNotification
            )
                .compactMap { $0.userInfo?["UIKeyboardFrameEndUserInfoKey"] as? CGRect }
                .map { $0.height }
                .subscribe(Subscribers.Assign(object: self, keyPath: \.currentHeight))
    
            NotificationCenter.Publisher(
                center: NotificationCenter.default,
                name: UIResponder.keyboardWillHideNotification
            )
                .compactMap { _ in CGFloat.zero }
                .subscribe(Subscribers.Assign(object: self, keyPath: \.currentHeight))
        }
    }
    
    

    用法

    someView()
        .adaptToKeyboard()
    

    在此处使用演示进行测试:https://github.com/youjinp/SwiftUIKit

    【讨论】:

      猜你喜欢
      • 2015-01-12
      • 1970-01-01
      • 2013-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-22
      • 2020-08-20
      相关资源
      最近更新 更多