hackiftekhar/IQKeyboardManager

View on GitHub
IQKeyboardManagerSwift/LIsteners/Info/IQKeyboardInfo.swift

Summary

Maintainability
A
0 mins
Test Coverage
//
//  IQKeyboardInfo.swift
//  https://github.com/hackiftekhar/IQKeyboardManager
//  Copyright (c) 2013-24 Iftekhar Qurashi.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import UIKit

@available(iOSApplicationExtension, unavailable)
@MainActor
public struct IQKeyboardInfo: Equatable {
    nonisolated public static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.frame.equalTo(rhs.frame)
    }

    @objc public enum Name: Int {
        case willShow
        case didShow
        case willHide
        case didHide
        case willChangeFrame
        case didChangeFrame
    }

    public let name: Name

    /** To save keyboard frame. */
    public let frame: CGRect

    /** To save keyboard animation duration. */
    public let animationDuration: TimeInterval

    /** To mimic the keyboard animation */
    public let animationCurve: UIView.AnimationCurve

    public var keyboardShowing: Bool {
        frame.height > 0
    }

    public init(notification: Notification?, name: Name) {
        self.name = name

        let screenBounds: CGRect
        if let screen: UIScreen = notification?.object as? UIScreen {
            screenBounds = screen.bounds
        } else {
            screenBounds = UIScreen.main.bounds
        }

        if let info: [AnyHashable: Any] = notification?.userInfo {

            //  Getting keyboard animation.
            if let curveValue: Int = info[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int,
               let curve: UIView.AnimationCurve = UIView.AnimationCurve(rawValue: curveValue) {
                animationCurve = curve
            } else {
                animationCurve = .easeOut
            }

            //  Getting keyboard animation duration
            if let duration: TimeInterval = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval,
               duration != 0.0 {
                animationDuration = duration
            } else {
                animationDuration = 0.25
            }

            //  Getting UIKeyboardSize.
            if var kbFrame: CGRect = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {

                // If this is floating keyboard
                if kbFrame.width < screenBounds.width,
                   kbFrame.maxY < screenBounds.height {
                    kbFrame.size = CGSize(width: kbFrame.size.width, height: 0)
                } else {
                    // (Bug ID: #469) (Bug ID: #381) (Bug ID: #1506)
                    // Calculating actual keyboard covered size respect to window,
                    // keyboard frame may be different when hardware keyboard is attached
                    let keyboardHeight = CGFloat.maximum(screenBounds.height - kbFrame.minY, 0)
                    kbFrame.size = CGSize(width: kbFrame.size.width, height: keyboardHeight)
                }

                frame = kbFrame
            } else {
                frame = CGRect(x: 0, y: screenBounds.height, width: screenBounds.width, height: 0)
            }
        } else {
            animationCurve = .easeOut
            animationDuration = 0.25
            frame = CGRect(x: 0, y: screenBounds.height, width: screenBounds.width, height: 0)
        }
    }

    public func animate(alongsideTransition transition: @escaping () -> Void, completion: (() -> Void)? = nil) {

//        if let timing = UIView.AnimationCurve.RawValue(exactly: animationCurve.rawValue),
//           let curve = UIView.AnimationCurve(rawValue: timing) {
//            let animator = UIViewPropertyAnimator(duration: animationDuration, curve: curve) {
//                transition()
//            }
//            animator.addCompletion { _ in
//                completion?()
//            }
//            animator.isUserInteractionEnabled = true
//            animator.startAnimation()
//        } else {
        var animationOptions: UIView.AnimationOptions = .init(rawValue: UInt(animationCurve.rawValue << 16))
        animationOptions.formUnion(.allowUserInteraction)
        animationOptions.formUnion(.beginFromCurrentState)
        UIView.animate(withDuration: animationDuration, delay: 0,
                       options: animationOptions,
                       animations: transition,
                       completion: { _ in
            completion?()
        })
//        }
    }
}