Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

throwing error while archiving on m1 chip resolved #70

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 136 additions & 71 deletions Sources/KeyboardAvoiding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import UIKit
}
}
public static var keyboardAvoidingMode = KeyboardAvoidingMode.minimum
@objc public static var avoidingBlock: ((Bool, CGFloat, CGFloat, UIView.AnimationOptions)->Void)? {
@objc public static var avoidingBlock: ((Bool, CGFloat, CGFloat, UIView.AnimationOptions) -> Void)? {
willSet {
self.initialise()
}
Expand All @@ -55,74 +55,88 @@ import UIKit
self.setAvoidingView(newValue, withOptionalTriggerView: newValue)
}
}

class func didChange(_ notification: Foundation.Notification) {
var isKeyBoardShowing = false
// isKeyBoardShowing and is it merged and docked.

let isPortrait = UIApplication.shared.statusBarOrientation.isPortrait
let isPortrait = UIWindow.getOrientation() == .portrait
// get the keyboard & window frames

let keyboardFrame = notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
return
}
// keyboardHeightDiff used when user is switching between different keyboards that have different heights
var keyboardFrameBegin = notification.userInfo![UIResponder.keyboardFrameBeginUserInfoKey] as! CGRect

var mutablekeyboardFrameBegin: CGRect!
let strKeyboardFrame = UIResponder.keyboardFrameBeginUserInfoKey
guard let keyboardFrameBegin = notification.userInfo?[strKeyboardFrame] as? CGRect else {
return
}
mutablekeyboardFrameBegin = keyboardFrameBegin
// hack for bug in iOS 11.2
let keyboardFrameEnd = notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
if keyboardFrameEnd.size.height > keyboardFrameBegin.size.height {
keyboardFrameBegin = CGRect(x: keyboardFrameBegin.origin.x, y: keyboardFrameBegin.origin.y, width: keyboardFrameBegin.size.width, height: keyboardFrameEnd.size.height)
if let keyboardFrameEnd = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
if keyboardFrameEnd.size.height > keyboardFrameBegin.size.height {
let kbX = keyboardFrameBegin.origin.x
let kbY = keyboardFrameBegin.origin.y
let kbWidth = keyboardFrameBegin.size.width
let kbHeight = keyboardFrameBegin.size.height
mutablekeyboardFrameBegin = CGRect(x: kbX, y: kbY, width: kbWidth, height: kbHeight)
}
}
var keyboardHeightDiff:CGFloat = 0.0
if keyboardFrameBegin.size.height > 0 {
keyboardHeightDiff = keyboardFrameBegin.size.height - keyboardFrame.size.height

var keyboardHeightDiff: CGFloat = 0.0
if mutablekeyboardFrameBegin.size.height > 0 {
keyboardHeightDiff = mutablekeyboardFrameBegin.size.height - keyboardFrame.size.height
}
let screenSize = UIScreen.main.bounds.size
let animationCurve = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as! Int
let animationOptions = animationCurve << 16
var animationOptions = 0
if let animationCurve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int {
animationOptions = animationCurve << 16
}

// if split keyboard is being dragged, then skip notification
if keyboardFrame.size.height == 0 && (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad) {

if keyboardFrame.size.height == 0 && (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad) {
if isPortrait && keyboardFrameBegin.origin.y + keyboardFrameBegin.size.height == screenSize.height {
return
}
else if !isPortrait && keyboardFrameBegin.origin.x + keyboardFrameBegin.size.width == screenSize.width {
} else if !isPortrait && keyboardFrameBegin.origin.x + keyboardFrameBegin.size.width == screenSize.width {
return
}
}
let kbfY = keyboardFrame.origin.y
let kbfHeight = keyboardFrame.size.height
// calculate if we are to move up the avoiding view
if !keyboardFrame.isEmpty && (keyboardFrame.origin.y == 0 || (keyboardFrame.origin.y + keyboardFrame.size.height == screenSize.height)) {
if !keyboardFrame.isEmpty && (kbfY == 0 || (kbfY + kbfHeight == screenSize.height)) {
isKeyBoardShowing = true
self.lastNotification = notification
}

// get animation duration
var animationDuration = notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as! CGFloat
var animationDuration: CGFloat!
let strKeyboardAnimation = UIResponder.keyboardAnimationDurationUserInfoKey
if let animationDurationTemp = notification.userInfo?[strKeyboardAnimation] as? CGFloat {
animationDuration = animationDurationTemp
}
if animationDuration == 0 {
// custom keyboards often dont animate, its too clanky so have to manually set this
animationDuration = 0.1
}

if isKeyBoardShowing {
self.showingAnimationCount = 0
for triggerView in self.triggerViews {
//showing and docked

// showing and docked
var diff: CGFloat = 0.0
if keyboardHeightDiff != 0 {
// if keyboard height is changing and avoidingView is currently moved
diff = keyboardHeightDiff
}
else {
} else {
let originInWindow = triggerView.convert(triggerView.bounds.origin, to: nil)
switch UIApplication.shared.statusBarOrientation {
switch UIWindow.getOrientation() {
case .portrait, .landscapeLeft:
diff = keyboardFrame.origin.y
diff = diff - (originInWindow.y + triggerView.frame.size.height)
break
diff -= (originInWindow.y + triggerView.frame.size.height)
case .portraitUpsideDown, .landscapeRight:
diff = screenSize.height - keyboardFrame.size.height
diff = diff - (originInWindow.y + triggerView.frame.size.height)
break
diff -= (originInWindow.y + triggerView.frame.size.height)
default:
break
}
Expand All @@ -133,27 +147,30 @@ import UIKit
switch self.keyboardAvoidingMode {
case .maximum:
self.minimumAnimationDuration = animationDuration
break
case .minimumDelayed:
let minimumDisplacement = max(displacement, diff)
self.minimumAnimationDuration = animationDuration * (minimumDisplacement / displacement)
self.minimumAnimationDuration = animationDuration * CGFloat(minimumDisplacement / displacement)
displacement = minimumDisplacement - self.paddingForCurrentAvoidingView
delay = (animationDuration - self.minimumAnimationDuration)
animationDuration = self.minimumAnimationDuration
break
default:
let minimumDisplacement = max(displacement, diff)
displacement = minimumDisplacement - (keyboardHeightDiff == 0 ? self.paddingForCurrentAvoidingView : 0)

displacement = minimumDisplacement - (keyboardHeightDiff == 0
? self.paddingForCurrentAvoidingView
: 0)
}

if self.avoidingView != nil && self.avoidingView!.superview != nil {
if self.avoidingViewUsesAutoLayout {
// if view uses constraints
var hasFoundFirstConstraint = false
for constraint: NSLayoutConstraint in self.avoidingView!.superview!.constraints {

if let secondItem = constraint.secondItem as? NSObject, secondItem == self.avoidingView! && (constraint.secondAttribute == .centerY || constraint.secondAttribute == .top || constraint.secondAttribute == .bottom) {
let cntrSecAttr = constraint.secondAttribute
let condition2 = (cntrSecAttr == .centerY
|| cntrSecAttr == .top
|| cntrSecAttr == .bottom)
if let secondItem = constraint.secondItem as? NSObject,
secondItem == self.avoidingView! && condition2 {
if !self.updatedConstraints.contains(constraint) {
self.updatedConstraints.append(constraint)
self.updatedConstraintConstants.append(constraint.constant)
Expand All @@ -162,11 +179,16 @@ import UIKit
hasFoundFirstConstraint = true
}
}

if !hasFoundFirstConstraint {
// if the constraint.secondItem wasn't found, sometimes its the constraint.firstItem that needs to be updated
// if the constraint.secondItem wasn't found,
// sometimes its the constraint.firstItem that needs to be updated
for constraint: NSLayoutConstraint in self.avoidingView!.superview!.constraints {
if constraint.firstItem as! NSObject == self.avoidingView! && (constraint.firstAttribute == .centerY || constraint.firstAttribute == .top || constraint.firstAttribute == .bottom) {
let cntrFirstAttr = constraint.firstAttribute
let condition2 = ( cntrFirstAttr == .centerY
|| cntrFirstAttr == .top
|| cntrFirstAttr == .bottom)
if constraint.firstItem as? NSObject == self.avoidingView! && condition2 {
if !self.updatedConstraints.contains(constraint) {
self.updatedConstraints.append(constraint)
self.updatedConstraintConstants.append(constraint.constant)
Expand All @@ -178,40 +200,44 @@ import UIKit
self.avoidingView!.superview!.setNeedsUpdateConstraints()
}
self.showingAnimationCount += 1

UIView.animate(withDuration: TimeInterval(animationDuration), delay: TimeInterval(delay), options: UIView.AnimationOptions(rawValue: UInt(animationOptions)), animations: {() -> Void in
let animationOption = UIView.AnimationOptions(rawValue: UInt(animationOptions))
UIView.animate(withDuration: TimeInterval(animationDuration),
delay: TimeInterval(delay),
options: animationOption,
animations: {() -> Void in

if self.avoidingViewUsesAutoLayout {
self.avoidingView!.superview!.layoutIfNeeded()
// to animate constraint changes
}
else {
} else {
var transform = self.avoidingView!.transform
transform = transform.translatedBy(x: 0, y: displacement)
self.avoidingView!.transform = transform
}

}, completion: { _ in
self.showingAnimationCount -= 1
})
}
}
if self.avoidingBlock != nil {
self.avoidingBlock!(isKeyBoardShowing, animationDuration, displacement, UIView.AnimationOptions(rawValue: UInt(animationOptions)))
self.avoidingBlock!(isKeyBoardShowing,
animationDuration,
displacement,
UIView.AnimationOptions(rawValue: UInt(animationOptions)))
}
}

}
else if self.isKeyboardVisible {
} else if self.isKeyboardVisible {
// hiding, undocking or splitting
switch self.keyboardAvoidingMode {
case .maximum:
break
case .minimumDelayed:
animationDuration = self.minimumAnimationDuration
break
default:
break
}

// restore state
if self.avoidingView != nil && self.avoidingView!.superview != nil {
if self.avoidingViewUsesAutoLayout {
Expand All @@ -222,13 +248,17 @@ import UIKit
}
self.avoidingView!.superview!.setNeedsUpdateConstraints()
}
UIView.animate(withDuration: TimeInterval(animationDuration + CGFloat(0.075)), delay: 0, options: UIView.AnimationOptions(rawValue: UInt(animationOptions)), animations: {() -> Void in
UIView.animate(withDuration: TimeInterval(animationDuration + CGFloat(0.075)),
delay: 0,
options: UIView.AnimationOptions(rawValue: UInt(animationOptions)),
animations: {() -> Void in

if self.avoidingViewUsesAutoLayout {
self.avoidingView!.superview!.layoutIfNeeded()
}
else {
} else {
self.avoidingView!.transform = CGAffineTransform.identity
}

}, completion: {(_ finished: Bool) -> Void in
if self.showingAnimationCount <= 0 {
self.updatedConstraints.removeAll()
Expand All @@ -237,63 +267,98 @@ import UIKit
})
}
if self.avoidingBlock != nil {
self.avoidingBlock!(isKeyBoardShowing, animationDuration + 0.075, 0, UIView.AnimationOptions(rawValue: UInt(animationOptions)))
self.avoidingBlock!(isKeyBoardShowing,
animationDuration + 0.075,
0,
UIView.AnimationOptions(rawValue: UInt(animationOptions)))
}
}
self.isKeyboardVisible = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(screenSize.width), height: CGFloat(screenSize.height)).intersects(keyboardFrame)
self.isKeyboardVisible = CGRect(x: CGFloat(0),
y: CGFloat(0),
width: CGFloat(screenSize.width),
height: CGFloat(screenSize.height)).intersects(keyboardFrame)
}

// The triggerView is required if the avoidingView isn't nil
@objc public class func setAvoidingView(_ avoidingView: UIView?, withTriggerView triggerView: UIView) {
self.setAvoidingView(avoidingView, withOptionalTriggerView: triggerView)
}

private class func setAvoidingView(_ avoidingView: UIView?, withOptionalTriggerView triggerView: UIView?) {
self.initialise()

self._avoidingView = avoidingView
self.avoidingViewUsesAutoLayout = (avoidingView != nil && avoidingView!.superview != nil) ? avoidingView!.superview!.constraints.count > 0 : false

self.avoidingViewUsesAutoLayout = (avoidingView != nil
&& avoidingView!.superview != nil)
? avoidingView!.superview!.constraints.count > 0
: false

self.triggerViews.removeAll()
if triggerView != nil {
self.triggerViews.append(triggerView!)
}

self.paddingForCurrentAvoidingView = self.padding
self.avoidingBlock = nil
if self.isKeyboardVisible && avoidingView != nil && self.lastNotification != nil {
// perform avoiding immediately
self.didChange(self.lastNotification!)
}
}

public class func addTriggerView(_ triggerView: UIView) {
self.triggerViews.append(triggerView)
}

public class func removeTriggerView(_ triggerView: UIView) {
if let index = triggerViews.firstIndex(of: triggerView) {
self.triggerViews.remove(at: index)
}
}

public class func removeAll() {
self.triggerViews.removeAll()
self.avoidingView = nil
self.avoidingBlock = nil
}

private class func initialise() {
// make sure we only add this once
if self.avoidingBlock == nil && self.avoidingView == nil {
NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: OperationQueue.main, using: { notification in
let notificationName = UIApplication.didEnterBackgroundNotification
NotificationCenter.default.addObserver(forName: notificationName,
object: nil,
queue: OperationQueue.main,
using: { _ in
// Autolayout is reset when app goes into background, so we need to dismiss the keyboard too
UIApplication.shared.windows.first?.rootViewController?.view.endEditing(true)
})
NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: OperationQueue.main, using: { notification in
let notificationNameKeyboardWillChange = UIResponder.keyboardWillChangeFrameNotification
NotificationCenter.default.addObserver(forName: notificationNameKeyboardWillChange,
object: nil,
queue: OperationQueue.main,
using: { notification in
self.didChange(notification)
})
}
}
}

extension UIWindow {
class func getOrientation() -> UIInterfaceOrientation {
if #available(iOS 13.0, *) {
let sceneFirst = UIApplication.shared.connectedScenes.first
guard let windowScene = sceneFirst as? UIWindowScene, windowScene.activationState == .foregroundActive else {
return .unknown
}

if windowScene.windows.first != nil {
return windowScene.interfaceOrientation
}
return .unknown
} else {
// Fallback on earlier versions
return UIApplication.shared.statusBarOrientation
}

}
}
Loading