iOS Swift: Constraints did not update on screen - ios

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()
}

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.

Keyboard hide sooner than view

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
}

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.

View not moving up for custom keyboard height - swift

I have looked around stackoverflow and I have been unsuccessful to find a fix to this issue. I have added code to shift my view up as per the height of the keyboard. This works perfectly for iOS default keyboard, however, this does not work for custom keyboards. Here's my code:
import UIKit
class AddCategoryViewController: UIViewController {
var partialView: CGFloat {
return UIScreen.main.bounds.height - 150
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == partialView {
let offset: CGSize = ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size)!
if keyboardSize.height == offset.height {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y -= keyboardSize.height
})
} else {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y += keyboardSize.height - offset.height
})
}
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != partialView {
self.view.frame.origin.y += keyboardSize.height
}
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 0.6, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [.allowUserInteraction], animations: {
let frame = self.view.frame
self.view.frame = CGRect(x: 0, y: self.partialView, width: frame.width, height: frame.height)
}, completion: nil)
NotificationCenter.default.addObserver(self, selector: #selector(AddCategoryViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(AddCategoryViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
}
Hoping that you could help me fix this so that the view pushes up the height of the custom keyboard's height.
dont change the Frame of theView.. just change the translation of View
UIView.animate(withDuration: 0.6, delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 0.2, options: [.allowUserInteraction], animations: {
self.view.transform = CGAffineTransform(translationX : 0 , y : partialView)
}, completion: nil)
And to Reset it just use
self.view.transform = CGAffineTransform.identity

Handle tableView contentInset while keyboard is presented

Im building a sample where i can post comments where i am using tableView to add a cell each time i post. its mostly handled but I'm suffering of one issue when i want to post a comment, the tableview bottom is the same as keyboard height but empty and if place it zero the view will move down then pushed top, and thats because I'm moving the whole view when i click on textview to write text.
This is a preview of the empty space under the tableView (keyboard height as inset) : Preview of image on Google Drive
Another image to see if i made the bottom (content inset) = 0 when keyboard is opening : Preview of second image on Google Drive
func keyboardWillShow(sender:NSNotification){
let userInfo: [NSObject : AnyObject] = sender.userInfo!
keyboardSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
var contentInsets = UIEdgeInsets()
if wasEmoj == true {
if (UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation)) {
contentInsets = UIEdgeInsetsMake(257.0, 0.0, (self.keyboardSize.height), 0.0);
} else {
contentInsets = UIEdgeInsetsMake(257.0, 0.0, (self.keyboardSize.width), 0.0);
}
self.mytableView.contentInset = contentInsets;
self.mytableView.scrollIndicatorInsets = contentInsets
}else {
if (UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation)) {
contentInsets = UIEdgeInsetsMake(215.0, 0.0,(self.keyboardSize.height), 0.0);
} else {
contentInsets = UIEdgeInsetsMake(215.0, 0.0, (self.keyboardSize.width), 0.0);
}
self.mytableView.contentInset = contentInsets;
self.mytableView.scrollIndicatorInsets = contentInsets
}
}
func textViewDidBeginEditing(textView: UITextView) {
self.animateTextField(commentText, up: true, movementDistance: -215, duration: 0.3)
}
///- animate the view up or down to adapt with keyboard
func animateTextField (textField:UITextView,up:Bool,movementDistance:Int,duration:NSTimeInterval){
let movement : Int = (up ? movementDistance : -movementDistance);
UIView.beginAnimations("animateTextField", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
if up == true {
UIView.setAnimationDuration(0.38)
}else {
UIView.setAnimationDuration(0.3)
}
self.view.frame = CGRectOffset(self.view.frame, 0, CGFloat(movement))
UIView.commitAnimations()
}
It's better to change the frame of the tableview in this case.
You can keep the instance of the constraint between the commentView's bottom and the bottom layout guide.
#IBOutlet weak var commentViewBottom: NSLayoutConstraint!
Then change the constant of the constraint when needed.
func keyboardWillShow(sender:NSNotification){
if let dic = sender.userInfo {
if let keyboardFrame = dic[UIKeyboardFrameEndUserInfoKey]?.CGRectValue {
if let duration = dic[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue {
commentViewBottom.constant = -keyboardFrame.height
UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .CurveLinear, animations: { () -> Void in
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
}
func keyboardWillHide(sender: NSNotification) {
if let dic = sender.userInfo, let duration = dic[UIKeyboardAnimationDurationUserInfoKey]?.doubleValue {
commentViewBottom.constant = 0
UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.5, options: .CurveLinear, animations: { () -> Void in
self.view.layoutIfNeeded()
}, completion: nil)
}
}

Resources