-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
160 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// | ||
// KeyboardLayoutGuide.swift | ||
// SketchKit | ||
// | ||
// Created by Diogo Autilio on 04/10/19. | ||
// Copyright (c) 2021 Anykey Entertrainment. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import UIKit | ||
|
||
final class SKKeyboard { | ||
static let shared = SKKeyboard() | ||
var currentHeight: CGFloat = 0 | ||
|
||
private init() { | ||
// Safe singleton | ||
} | ||
} | ||
|
||
final class KeyboardLayoutGuide: LayoutGuide { | ||
|
||
private var bottomConstraint: NSLayoutConstraint? | ||
|
||
init(notificationCenter: NotificationCenter = NotificationCenter.default) { | ||
super.init() | ||
// Observe keyboardWillChangeFrame notifications | ||
notificationCenter.addObserver(self, | ||
selector: #selector(adjustKeyboard(_:)), | ||
name: UIResponder.keyboardWillChangeFrameNotification, | ||
object: nil) | ||
|
||
// Observe keyboardWillHide notifications | ||
notificationCenter.addObserver(self, | ||
selector: #selector(adjustKeyboard(_:)), | ||
name: UIResponder.keyboardWillHideNotification, | ||
object: nil) | ||
} | ||
|
||
@available(*, unavailable) | ||
required init?(coder aDecoder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
deinit { | ||
NotificationCenter.default.removeObserver(self) | ||
} | ||
|
||
func setUp() { | ||
guard let owningView else { return } | ||
|
||
self.layout.applyConstraint { | ||
$0.heightAnchor(equalToConstant: SKKeyboard.shared.currentHeight) | ||
$0.leftAnchor(equalTo: owningView.leftAnchor) | ||
$0.rightAnchor(equalTo: owningView.rightAnchor) | ||
} | ||
|
||
updateBottomAnchor() | ||
} | ||
|
||
func updateBottomAnchor() { | ||
guard let owningView else { return } | ||
|
||
bottomConstraint?.isActive = false | ||
bottomConstraint = bottomAnchor.constraint(equalTo: owningView.safeBottomAnchor) | ||
bottomConstraint?.isActive = true | ||
} | ||
|
||
@objc | ||
private func adjustKeyboard(_ notification: Notification) { | ||
if var height = notification.keyboardHeight, let duration = notification.animationDuration { | ||
if height > 0, let bottom = owningView?.safeAreaInsets.bottom { | ||
height -= bottom | ||
} | ||
heightConstraint?.constant = height | ||
if duration > 0.0 { | ||
animate(notification) | ||
} | ||
SKKeyboard.shared.currentHeight = height | ||
} | ||
} | ||
|
||
private func animate(_ notification: Notification) { | ||
if let owningView, isVisible(view: owningView) { | ||
owningView.layoutIfNeeded() | ||
} else { | ||
UIView.performWithoutAnimation { [weak self] in | ||
self?.owningView?.layoutIfNeeded() | ||
} | ||
} | ||
} | ||
|
||
private func isVisible(view: UIView) -> Bool { | ||
func isVisible(view: UIView, inView: UIView?) -> Bool { | ||
guard let inView else { return true } | ||
let viewFrame = inView.convert(view.bounds, from: view) | ||
if viewFrame.intersects(inView.bounds) { | ||
return isVisible(view: view, inView: inView.superview) | ||
} | ||
return false | ||
} | ||
return isVisible(view: view, inView: view.superview) | ||
} | ||
} | ||
|
||
// MARK: - Helpers | ||
|
||
extension UILayoutGuide { | ||
internal var heightConstraint: NSLayoutConstraint? { | ||
return owningView?.constraints.first { | ||
$0.firstItem as? UILayoutGuide == self && $0.firstAttribute == .height | ||
} | ||
} | ||
} | ||
|
||
extension Notification { | ||
var keyboardHeight: CGFloat? { | ||
guard let keyboardFrame = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { | ||
return nil | ||
} | ||
|
||
if name == UIResponder.keyboardWillHideNotification { | ||
return 0.0 | ||
} else { | ||
// Weirdly enough UIKeyboardFrameEndUserInfoKey doesn't have the same behaviour | ||
// in ios 10 or iOS 11 so we can't rely on v.cgRectValue.width | ||
let screenHeight = UIApplication.shared.keyWindow?.bounds.height ?? UIScreen.main.bounds.height | ||
return screenHeight - keyboardFrame.cgRectValue.minY | ||
} | ||
} | ||
|
||
var animationDuration: CGFloat? { | ||
return self.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? CGFloat | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters