Keyboard hide sooner than view - ios

The keyboard adds as a subview to the container at the bottom of the collection view controller, but when trying to hide it, it seems keyboard load sooner than increasing the size of the view (a dark viw appear just after the keyboard is going to hide).
Also, when running the project for the first time in a day, the keyboard doesn't show up until I press "command + K" then a dark view as the size of the keyboard appears above the keyboard and below the input.
#objc func handleShowKeyboard(_ notification: Notification) {
if let keyboaradSize: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue? {
let keyboardFram = keyboaradSize.cgRectValue
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: {
self.view.frame.origin.y -= self.view.safeAreaInsets.bottom
self.view.frame.origin.y -= keyboardFram.height
self.view.layoutIfNeeded()
}) { (completion) in
}
}
}
#objc func handleHideKeyboard(_ notification: Notification) {
if let keyboaradSize: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue? {
let keyboardFram = keyboaradSize.cgRectValue
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: {
self.view.frame.origin.y += self.view.safeAreaInsets.bottom
self.view.frame.origin.y += keyboardFram.height
self.view.layoutIfNeeded()
}) { (completion) in
}
}
}

Read this key
UIKeyboardAnimationDurationUserInfoKey
value and use it as animation duration
guard let userInfo = notification.userInfo else {
return
}
guard let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
return
}
UIView.animate(withDuration: duration, delay: 0, options: [UIView.AnimationOptions(rawValue: curve)], animations: {
///
}) { (completed) in
}

Related

Synchronize UIView animation with keyboard animation

I have a UIView that will change its height and position depending so it's always "stuck" to the keyboard. The problem is that there is a asynchronous animation when changing the textField, the rest works fine. Here is a video for a better understanding:
Animation
This happen even though I used UIResponder.keyboardAnimationCurveUserInfoKey for the animations.
Here are all the parts inside my code where I animate:
Notification-Handler in ViewController:
var keyboardHeight = CGFloat(0)
var duration: Any?
var curve: NSNumber?
//MARK: keyboardWillShow
#objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
self.keyboardHeight = keyboardRectangle.height
let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey]
self.duration = duration
let curve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey]
self.curve = curve as? NSNumber
if self.wishViewIsVisible {
UIView.animate(withDuration: self.duration as! TimeInterval, delay: 0, options: UIView.AnimationOptions(rawValue: UIView.AnimationOptions.RawValue(truncating: self.curve!)), animations: {
self.wishConstraint.constant = -(self.wishView.frame.height + self.keyboardHeight)
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
Closure-Handler in ViewController:
self.wishView.onPriceButtonTapped = { [unowned self] height, isHidden in
if isHidden {
UIView.animate(withDuration: self.duration as! TimeInterval, delay: 0, options: UIView.AnimationOptions(rawValue: UIView.AnimationOptions.RawValue(truncating: self.curve!)), animations: {
self.wishConstraint.constant -= height
self.wishView.priceTextField.becomeFirstResponder()
self.view.layoutIfNeeded()
}, completion: nil)
} else {
UIView.animate(withDuration: self.duration as! TimeInterval, delay: 0, options: UIView.AnimationOptions(rawValue: UIView.AnimationOptions.RawValue(truncating: self.curve!)), animations: {
self.wishConstraint.constant += height
self.wishView.wishNameTextField.becomeFirstResponder()
self.view.layoutIfNeeded()
}, completion: nil)
}
}
Closure inside UIView:
#objc func priceButtonTapped(){
let priceViewIsHidden = priceView.isHidden
if priceViewIsHidden {
UIView.animate(withDuration: 0.25) {
self.priceView.alpha = 1
self.priceView.isHidden = false
self.theStackView.layoutIfNeeded()
}
self.onPriceButtonTapped?(priceView.frame.height, true)
} else {
UIView.animate(withDuration: 0.25) {
self.priceView.alpha = 0
self.priceView.isHidden = true
self.theStackView.layoutIfNeeded()
}
self.onPriceButtonTapped?(priceView.frame.height, false)
}
}
Does anyone know how I can fix this issue?
I know I am hard-coding the values inside the UIView but that is just to hide/show the arrangedSubview inside the StackView and this is not where the animation is off as you can see in the video. It only looks off when changing the textFields.

UITableView "jump up" during custom presentation animation

I am trying to create custom transition but I have a problem with "jumping" tableView. As you can see, just before transition, tableView hide under navigationBar which is wrong. How can I fix it?
Animation code:
UIView.animate(withDuration: transitionDuration, delay: 0, options: animationOptions, animations: { [weak self] in
guard let self = self else { return }
fromVC.view.transform = CGAffineTransform(scaleX: 0.9, y: 0.855)
fromVC.view.layer.cornerRadius = self.cornerRadius
fromVC.view.clipsToBounds = true
self.containerView.frame.origin.y -= self.containerHeight
self.topView.frame.origin.y -= self.containerHeight
self.backdropView.alpha = self.backdropAlpha
}, completion: { _ in
transitionContext.completeTransition(true)
})
and how it looks right now:

Manage Keyboard in Chat View

I have a controller like chat view, with one textfield and button and table view. I want to when user tap to textfield textfield go up and scroll to end of list. there is my code :
#objc func keyboardWillChangeFrame(_ notification: Notification) {
let keyboardFrame = ((notification as NSNotification).userInfo![UIKeyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue
print("*****************")
print((keyboardFrame?.height)!)
//emoji keyboard size is 258
if (keyboardFrame?.height)! == 258 {
textBackViewBottom.constant = 52
}
else {
textBackViewBottom.constant = 10
}
}
#objc func keyboardWillShow(_ notification: Notification) {
// Check for double invocation
if _keyboardShown {
return
}
_keyboardShown = true
// Reducing size of table
let baseView = self.view
let keyboardFrame = ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey]! as AnyObject).cgRectValue
print("////////////////////")
print((keyboardFrame?.height)!)
let keyboardDuration = ((notification as NSNotification).userInfo![UIKeyboardAnimationDurationUserInfoKey]! as AnyObject).doubleValue
let visibleRows = tableView.indexPathsForVisibleRows
var lastIndexPath : IndexPath? = nil
if (visibleRows != nil) && visibleRows!.count > 0 {
lastIndexPath = visibleRows![visibleRows!.count-1] as IndexPath
}
UIView.animate(withDuration: keyboardDuration!, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
baseView!.frame = CGRect(x: baseView!.frame.origin.x, y: baseView!.frame.origin.y, width: baseView!.frame.size.width, height: baseView!.frame.size.height - (keyboardFrame?.size.height)!)
}, completion: {
(finished: Bool) in
if lastIndexPath != nil {
// Scroll down the table so that the last
// visible row remains visible
self.tableView.scrollToRow(at: lastIndexPath!, at: UITableViewScrollPosition.bottom, animated: true)
}
})
}
#objc func keyboardWillHide(_ notification: Notification) {
// Check for double invocation
if !_keyboardShown {
return
}
_keyboardShown = false
// Expanding size of table
//
let baseView = self.view
let keyboardFrame = ((notification as NSNotification).userInfo![UIKeyboardFrameBeginUserInfoKey]! as AnyObject).cgRectValue
let keyboardDuration = ((notification as NSNotification).userInfo![UIKeyboardAnimationDurationUserInfoKey]! as AnyObject).doubleValue
UIView.animate(withDuration: keyboardDuration!, delay: 0.0, options: UIViewAnimationOptions.curveEaseOut, animations: {
baseView!.frame = CGRect(x: baseView!.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.size.width, height: self.view.frame.size.height + (keyboardFrame?.size.height)!)
}, completion: nil)
}
every thing work well with Iphone default keyboard, but when I'm using custom keyboard like Swiftkey my code is wrong and view broken.
How can get Update view when keyboard size and keyboard type change.
thanks for reply.

Drawing a dismiss transition beneath the TabBar in Swift 4

I'm trying to implement CardViews similar to the ones used in the iOS 11 App Store. In order to do so, I'm using a GitHub project (https://github.com/PaoloCuscela/Cards) and tweaked it a bit.
The problem is that when transitioning back from the presented Detail View to the initial view (which is placed inside a TabBarController) the card is drawn in front of the TabBar (see video https://youtu.be/qDb3JoISTdw) which gives the whole transition a kind of 'glitchy' look.
This is the Code of the transitioning class I use:
import UIKit
class Animator: NSObject, UIViewControllerAnimatedTransitioning {
fileprivate var presenting: Bool
fileprivate var velocity = 0.6
var bounceIntensity: CGFloat = 0.07
var card: Card
init(presenting: Bool, from card: Card) {
self.presenting = presenting
self.card = card
super.init()
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// Animation Context Setup
let container = transitionContext.containerView
let to = transitionContext.viewController(forKey: .to)!
let from = transitionContext.viewController(forKey: .from)!
container.addSubview(to.view)
container.addSubview(from.view)
guard presenting else {
// Detail View Controller Dismiss Animations
card.isPresenting = false
let detailVC = from as! DetailViewController
let cardBackgroundFrame = detailVC.scrollView.convert(card.backgroundIV.frame, to: nil)
let bounce = self.bounceTransform(cardBackgroundFrame, to: card.originalFrame)
// Blur and fade with completion
UIView.animate(withDuration: velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.blurView.alpha = 0
}, completion: nil)
UIView.animate(withDuration: velocity, delay: 0, options: .curveEaseOut, animations: {
detailVC.snap.alpha = 0
self.card.backgroundIV.layer.cornerRadius = self.card.cardRadius
}, completion: { _ in
detailVC.layout(self.card.originalFrame, isPresenting: false, isAnimating: false)
self.card.addSubview(detailVC.card.backgroundIV)
transitionContext.completeTransition(true)
})
// Layout with bounce effect
UIView.animate(withDuration: velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.layout(self.card.originalFrame, isPresenting: false, transform: bounce)
self.card.delegate?.cardIsHidingDetail?(card: self.card)
}) { _ in UIView.animate(withDuration: self.velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.layout(self.card.originalFrame, isPresenting: false)
self.card.delegate?.cardIsHidingDetail?(card: self.card)
})
}
return
}
// Detail View Controller Present Animations
card.isPresenting = true
let detailVC = to as! DetailViewController
let bounce = self.bounceTransform(card.originalFrame, to: card.backgroundIV.frame)
container.bringSubview(toFront: detailVC.view)
detailVC.card = card
detailVC.layout(card.originalFrame, isPresenting: false)
// Blur and fade with completion
UIView.animate(withDuration: velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.blurView.alpha = 1
}, completion: nil)
UIView.animate(withDuration: velocity, delay: 0, options: .curveEaseOut, animations: {
self.card.transform = CGAffineTransform.identity // Reset card identity after push back on tap
detailVC.snap.alpha = 1
self.card.backgroundIV.layer.cornerRadius = 0
}, completion: { _ in
detailVC.layout(self.card.originalFrame, isPresenting: true, isAnimating: false, transform: .identity)
transitionContext.completeTransition(true)
})
// Layout with bounce effect
UIView.animate(withDuration: velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.layout(detailVC.view.frame, isPresenting: true, transform: bounce)
self.card.delegate?.cardIsShowingDetail?(card: self.card)
}) { _ in UIView.animate(withDuration: self.velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.layout(detailVC.view.frame, isPresenting: true)
self.card.delegate?.cardIsShowingDetail?(card: self.card)
})
}
}
private func bounceTransform(_ from: CGRect, to: CGRect ) -> CGAffineTransform {
let old = from.center
let new = to.center
let xDistance = old.x - new.x
let yDistance = old.y - new.y
let xMove = -( xDistance * bounceIntensity )
let yMove = -( yDistance * bounceIntensity )
return CGAffineTransform(translationX: xMove, y: yMove)
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return velocity
}
}
I haven't worked with transitioning in iOS and hope someone can tell me how to achieve what I want here.
UITabBarController does all of its layout using autoresizing masks. That being the case you can grab the tabBar add it to the container view, perform the animation then add it back to it's root view. For example using the Cards animation you can change the animateTransition(using transitionContext: UIViewControllerContextTransitioning) to:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// Animation Context Setup
let container = transitionContext.containerView
let to = transitionContext.viewController(forKey: .to)!
let from = transitionContext.viewController(forKey: .from)!
container.addSubview(to.view)
container.addSubview(from.view)
// If going to tab bar controller
// Add tab bar above view controllers
// Turn off interactions
if !presenting, let tabController = to as? UITabBarController {
tabController.tabBar.isUserInteractionEnabled = false
container.addSubview(tabController.tabBar)
}
guard presenting else {
// Detail View Controller Dismiss Animations
card.isPresenting = false
let detailVC = from as! DetailViewController
let cardBackgroundFrame = detailVC.scrollView.convert(card.backgroundIV.frame, to: nil)
let bounce = self.bounceTransform(cardBackgroundFrame, to: card.originalFrame)
// Blur and fade with completion
UIView.animate(withDuration: velocity, delay: 0, options: .curveEaseOut, animations: {
detailVC.blurView.alpha = 0
detailVC.snap.alpha = 0
self.card.backgroundIV.layer.cornerRadius = self.card.cardRadius
}, completion: { _ in
detailVC.layout(self.card.originalFrame, isPresenting: false, isAnimating: false)
self.card.addSubview(detailVC.card.backgroundIV)
// Add tab bar back to tab bar controller's root view
if let tabController = to as? UITabBarController {
tabController.tabBar.isUserInteractionEnabled = true
tabController.view.addSubview(tabController.tabBar)
}
transitionContext.completeTransition(true)
})
// Layout with bounce effect
UIView.animate(withDuration: velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.layout(self.card.originalFrame, isPresenting: false, transform: bounce)
self.card.delegate?.cardIsHidingDetail?(card: self.card)
}) { _ in UIView.animate(withDuration: self.velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.layout(self.card.originalFrame, isPresenting: false)
self.card.delegate?.cardIsHidingDetail?(card: self.card)
})
}
return
}
// Detail View Controller Present Animations
card.isPresenting = true
let detailVC = to as! DetailViewController
let bounce = self.bounceTransform(card.originalFrame, to: card.backgroundIV.frame)
container.bringSubview(toFront: detailVC.view)
detailVC.card = card
detailVC.layout(card.originalFrame, isPresenting: false)
// Blur and fade with completion
UIView.animate(withDuration: velocity, delay: 0, options: .curveEaseOut, animations: {
self.card.transform = CGAffineTransform.identity // Reset card identity after push back on tap
detailVC.blurView.alpha = 1
detailVC.snap.alpha = 1
self.card.backgroundIV.layer.cornerRadius = 0
}, completion: { _ in
detailVC.layout(self.card.originalFrame, isPresenting: true, isAnimating: false, transform: .identity)
transitionContext.completeTransition(true)
})
// Layout with bounce effect
UIView.animate(withDuration: velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.layout(detailVC.view.frame, isPresenting: true, transform: bounce)
self.card.delegate?.cardIsShowingDetail?(card: self.card)
}) { _ in UIView.animate(withDuration: self.velocity/2, delay: 0, options: .curveEaseOut, animations: {
detailVC.layout(detailVC.view.frame, isPresenting: true)
self.card.delegate?.cardIsShowingDetail?(card: self.card)
})
}
}
Which produces an animation like:

iOS Swift: Constraints did not update on screen

evening,
I have a problem with a scrollview constraint.
This is my code:
// when the keyboard is shown, move up some elements
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
// Bring up the scrol lview
print(scrollViewBottomConstraint.constant)
self.scrollViewBottomConstraint.constant = keyboardSize.height
print(scrollViewBottomConstraint.constant)
self.view.updateConstraints()
self.view.updateConstraintsIfNeeded()
self.view.needsUpdateConstraints()
self.view.layoutIfNeeded()
// Bring up the next arrow with animation
self.nextButtonBottomConstraint.constant = keyboardSize.height + 8
UIView.animate(
withDuration: 1.0,
delay: 0.0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 6.0,
options: [.allowUserInteraction,
.curveEaseInOut],
animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
the console print 0 and than 253, so the constraint constant is updated. But on screen I can't see the change and I have this log:
[Snapshotting] Snapshotting a view (0x7fa041471bc0,
UIInputSetHostView) that has not been rendered at least once requires
afterScreenUpdates:YES.
Weird, just view.layoutIfNeeded() after changing constraints should do the job.
try removing:
self.view.updateConstraints()
self.view.updateConstraintsIfNeeded()
self.view.needsUpdateConstraints()
Just:
view.layoutIfNeeded()
Try just this:
#objc func keyboardWillShow(notification: NSNotification) {
guard let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else{
return
}
scrollViewBottomConstraint.constant = keyboardSize.height
nextButtonBottomConstraint.constant = keyboardSize.height + 8
view.layoutIfNeeded()
}

Resources