View not moving up for custom keyboard height - swift - ios

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

Related

Handling keyboard with bottom constraint

I'm building a chat app with a regular design and UI of a chat app.
I have a "toolbar" that my app needs (0,0 375x66) and I want it to stay where it is when the keyboard is shown. It has 4 constraints: top: 0, leading: 0, trailing: 0, aspect-ratio: 125:22
I have a UICollectionView (0,66 375x530) that I want to be scrolled like any other chat app when the keyboard is shown. It has 4 constraints: top (to the toolbar): 0, leading: 0, trailing: 0, bottom (to the newMessageView I'll explain shortly): 0
I have a UIView that has a UITextField inside it. let's call it newMessageView (0,596 375x71) and it has 4 constraints: bottom: 0, leading: 0, trailing: 0, aspect-ratio: 375:71
Now, I'm trying to bring the newMessageView app when the keyboard is shown. I'm trying to to it like this:
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.newMessageViewBottomConstraint.constant += keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.newMessageViewBottomConstraint.constant -= keyboardSize.height
}
}
}
but it completly doesn't work. The view jumps and hides and I really can't understand why.
Am I doing it right? Can anyone help me and guide me with this?
You have to use self.view.layoutIfNeeded() after Update constraint
i write complete solution for you
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: Notification) {
self.keyboardControl(notification, isShowing: true)
}
#objc func keyboardWillHide(notification: Notification) {
self.keyboardControl(notification, isShowing: false)
}
private func keyboardControl(_ notification: Notification, isShowing: Bool) {
/* Handle the Keyboard property of Default*/
var userInfo = notification.userInfo!
let keyboardRect = (userInfo[UIKeyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue
let curve = (userInfo[UIKeyboardAnimationCurveUserInfoKey]! as AnyObject).uint32Value
let convertedFrame = self.view.convert(keyboardRect!, from: nil)
let heightOffset = self.view.bounds.size.height - convertedFrame.origin.y
let options = UIViewAnimationOptions(rawValue: UInt(curve!) << 16 | UIViewAnimationOptions.beginFromCurrentState.rawValue)
let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey]! as AnyObject).doubleValue
var pureheightOffset : CGFloat = -heightOffset
if isShowing { /// Wite space of save area in iphonex ios 11
if #available(iOS 11.0, *) {
pureheightOffset = pureheightOffset + view.safeAreaInsets.bottom
}
}
// Here change you Consrant
// self.actionBarPaddingBottomConstranit?.update(offset:pureheightOffset)
UIView.animate(
withDuration: duration!,
delay: 0,
options: options,
animations: {
self.view.layoutIfNeeded()
},
completion: { bool in
})
}

How to fix position of UIView when keyboard moved up?

How do I fix the position of UIView with image and label when the keyboard is moved up?
I made a code to move up my textField when the keyboard shows, but instead this moves everything.
func textFieldDidEndEditing(_ textField: UITextField) {
moveTextField(textField: rasstoyanietextField, moveDistance: -215, up: false)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
moveTextField(textField: rasstoyanietextField, moveDistance: -215, up: true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
rasstoyanietextField.resignFirstResponder()
return true
}
func moveTextField(textField: UITextField, moveDistance: Int, up: Bool){
let moveDuration = 0.1
let movement: CGFloat = CGFloat(up ? moveDistance : -moveDistance)
UIView.beginAnimations("animateTextField", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(moveDuration)
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()
}
How could I fix it?
Because you move whole view, instead of textview only. Change line:
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
with:
textField.frame = textField.frame.offsetBy(dx: 0, dy: movement)
Add keybaord observers
NotificationCenter.default.addObserver(
self,
selector: #selector(handleKeyboardDidShow),
name: NSNotification.Name.UIKeyboardDidShow,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(handleKeyboardWillHide),
name:NSNotification.Name.UIKeyboardWillHide,
object: nil)
I think it's better to use scrollviews in viewControllers that have textfeilds/textViews to move up all items when showing but here you may Hook the bottom constraint of the textfeild or it's superview and drag it as IBOutlet and do this
#objc func handleKeyboardDidShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.textFeilfBottomCon.constant = -1 * keyboardSize.height
UIView.animate(withDuration: 0.5, animations: {
self.view.layoutIfNeeded()
})
}
}
#objc func handleKeyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.textFeilfBottomCon.constant = 0
UIView.animate(withDuration: 0.5, animations: {
self.view.layoutIfNeeded()
})
}
}

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

Pausing and resuming UIView animation with alpha doesn't work when user presses Home button

Question:
How can I pause a nested UIView animation that animates alpha/opacity when the user presses the Home button and returns back to the app?
What have I missed in the code below? Thank you.
Background:
I have a simple nested UIView animation that adjusts the alpha of a view slowly at different times from alpha = 0 to alpha = 1 over 1 minute.
When a user presses the Home button, the UIView animation does not pause, so when the app resumes, the UIView animation fasts forward to the very end of the animation, the alpha changes instantly from 0 to 1 on resuming the app.
So, I’m trying to pause the UIView animation when the user presses the device Home button and temporarily suspends the app.
I have a function that pauses the animation (pauseLayer) and then recommences the animation (resumeLayer). These two functions work great when calling them from a UIButton. It pauses the alpha and resumes the animation as expected. (However, if the Home button is pressed while the animation is paused, when it resumes the alpha changes instantly from 0 to 1.)
When I try calling the pauseLayer when the user presses the Home button (receiving the WillResignActive notification), and then returns back to the app (receiving the WillEnterForeground notification), the animation doesn’t pause and resume, instead the alpha changes instantly from 0 to 1 on resuming the app.
It would seem it should work, but it doesn’t.
Code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myView1: UIView!
#IBOutlet weak var myView2: UIView!
override func viewDidLoad() {
super.viewDidLoad()
setNotifications()
myAnimation()
}
#IBAction func myPauseButton(sender: UIButton) {
let layer = self.view.layer
pauseLayer(layer)
}
#IBAction func myResumeButton(sender: UIButton) {
let layer = self.view.layer
resumeLayer(layer)
}
func setNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.WillEnterForeground(_:)), name: UIApplicationWillEnterForegroundNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.WillResignActive(_:)), name: UIApplicationWillResignActiveNotification, object: nil)
}
func WillEnterForeground(notification : NSNotification) {
let layer = self.view.layer
resumeLayer(layer)
}
func WillResignActive(notification : NSNotification) {
let layer = self.view.layer
pauseLayer(layer)
}
func pauseLayer(layer: CALayer) {
let pausedTime: CFTimeInterval = layer.convertTime(CACurrentMediaTime(), fromLayer: nil)
layer.speed = 0.0
layer.timeOffset = pausedTime
}
func resumeLayer(layer: CALayer) {
let pausedTime: CFTimeInterval = layer.timeOffset
layer.speed = 1.0
layer.timeOffset = 0.0
layer.beginTime = 0.0
let timeSincePause: CFTimeInterval = layer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
layer.beginTime = timeSincePause
}
func myAnimation() {
self.myView1.alpha = 0
UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut], animations: {
self.myView1.alpha = 0.1
}, completion: {(finished: Bool) in
UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut], animations: {
self.myView1.alpha = 0.2
}, completion: {(finished: Bool) in
UIView.animateWithDuration(30, delay: 0, options: [.CurveEaseInOut], animations: {
self.myView1.alpha = 1
}, completion: nil)
})
})
self.myView2.alpha = 0
UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut], animations: {
self.myView2.alpha = 0.1
}, completion: {(finished: Bool) in
UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut], animations: {
self.myView2.alpha = 0.2
}, completion: {(finished: Bool) in
UIView.animateWithDuration(30, delay: 0, options: [.CurveEaseInOut], animations: {
self.myView2.alpha = 1
}, completion: nil)
})
})
}
}
EDIT:
If I add if (finished) { to each section, then press the Home button, then return to the app, the animation only progresses to the next section and stops, no further. This is better, but the issue then is that the resumeLayer doesn’t seem to work, so the animation remains stopped and doesn’t continue.
self.myView2.alpha = 0
UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut, .LayoutSubviews, .AllowUserInteraction, .BeginFromCurrentState], animations: {
self.myView2.alpha = 0.1
}, completion: {(finished: Bool) in
if (finished) {
UIView.animateWithDuration(15, delay: 0, options: [.CurveEaseInOut, .LayoutSubviews, .AllowUserInteraction, .BeginFromCurrentState], animations: {
self.myView2.alpha = 0.2
}, completion: {(finished: Bool) in
if (finished) {
UIView.animateWithDuration(30, delay: 0, options: [.CurveEaseInOut, .LayoutSubviews, .AllowUserInteraction, .BeginFromCurrentState], animations: {
self.myView2.alpha = 1
}, completion: nil)
}
})
}
})
You do not need to use UIView Animation to accomplish a simple fade.
You could use an Timer object and a little Math to manually animate it.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = colorScheme.red
NotificationCenter.default.addObserver(self, selector: #selector(self.didBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.didResignActive), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
}
func didBecomeActive() {
if case min_t..<max_t = t {
beginAnimation(at: t)
}
}
func didResignActive() {
_ = pauseAnimation()
}
var t = TimeInterval(0.0)
let min_t = TimeInterval(0)
let max_t = TimeInterval(100)
let delta = TimeInterval(0.1)
var timerObj: Timer?
func timer(_ aTimer: Timer) {
if t < max_t {
// Using a parabola
view.alpha = CGFloat(0.0001 * t * t)
} else {
_ = pauseAnimation()
}
t = t + 1
}
func beginAnimation(at t: TimeInterval = 0) {
self.t = t
timerObj = Timer.scheduledTimer(timeInterval: delta, target: self, selector: #selector(self.timer), userInfo: nil, repeats: true)
}
func pauseAnimation() -> TimeInterval {
timerObj?.invalidate()
return t
}
override func viewDidAppear(_ animated: Bool) {
beginAnimation()
}
}
However, there is a possibility that your application is actually more complicated and your example is a simplification. Well, how about this?
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
NotificationCenter.default.addObserver(self, selector: #selector(self.WillEnterForeground(notification:)), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.WillResignActive(notification:)), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
}
func WillEnterForeground(notification : NSNotification) {
if let timegap = endDate?.timeIntervalSince(startDate)
, timegap < duration {
beginAnimation(with: timegap/duration)
}
}
func WillResignActive(notification : NSNotification) {
let layer = self.view.layer
layer.removeAllAnimations()
}
var duration = TimeInterval(10)
var percentComplete = 0.0
var startDate: Date = Date()
var endDate: Date?
func beginAnimation(with percent: Double = 0.0) {
view.alpha = CGFloat(percent)
startDate = Date()
UIView.animateKeyframes(withDuration: duration * (1 - percent)
, delay: 0, options: UIViewKeyframeAnimationOptions.calculationModeLinear,
animations: {
if percent < 0.2 {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.2, animations: {
self.view.alpha = 0.1
})
}
if percent < 0.5 {
UIView.addKeyframe(withRelativeStartTime: 0.2, relativeDuration: 0.3, animations: {
self.view.alpha = 0.2
})
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.7, animations: {
self.view.alpha = 1.0
})
}) { (b: Bool) in
if b == false {
self.endDate = Date()
if let timegap = self.endDate?.timeIntervalSince(self.startDate)
, timegap < self.duration {
self.view.alpha = CGFloat(timegap / self.duration)
}
}
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
beginAnimation()
}
}
The main idea is to get the time before the animation starts and the time when it ends. The completion will tell us true if the animation was successful, but if the user presses the home button the completion block will tell us false. If it is false we can capture the date and find out the time difference and calculate the percent of completion. then we can use the percentage to determine the amount of time left.
Hope that helps

How to make UITextField move up when keyboard is present?

How do I prevent a UITextField from being hidden by the keyboard?
I assume this is happening on a UIViewController. If so, you can setup the following two functions to be called when the keyboard will show/hide, and respond appropriately in their blocks.
Setting up the UIViewController:
class ViewController: UIViewController, UITextFieldDelegate... {
var frameView: UIView!
First, in viewDidLoad():
override func viewDidLoad() {
self.frameView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height))
// Keyboard stuff.
let center: NotificationCenter = NotificationCenter.default
center.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
center.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
Then implement the following two functions to respond to your NotificationCenter functions defined in viewDidLoad() above. I give you an example of moving the entire view, but you can also animate just the UITextFields.
#objc func keyboardWillShow(notification: NSNotification) {
let info:NSDictionary = notification.userInfo! as NSDictionary
let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let keyboardHeight: CGFloat = keyboardSize.height
let _: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
UIView.animate(withDuration: 0.25, delay: 0.25, options: .curveEaseInOut, animations: {
self.frameView.frame = CGRect(x: 0, y: (self.frameView.frame.origin.y - keyboardHeight), width: self.view.bounds.width, height: self.view.bounds.height)
}, completion: nil)
}
#objc func keyboardWillHide(notification: NSNotification) {
let info: NSDictionary = notification.userInfo! as NSDictionary
let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let keyboardHeight: CGFloat = keyboardSize.height
let _: CGFloat = info[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
UIView.animate(withDuration: 0.25, delay: 0.25, options: .curveEaseInOut, animations: {
self.frameView.frame = CGRect(x: 0, y: (self.frameView.frame.origin.y + keyboardHeight), width: self.view.bounds.width, height: self.view.bounds.height)
}, completion: nil)
}
Don't forget to remove the notifications when leaving your view
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
Here is the simple solution in Swift. I translated some Objective-C code that worked for me in the past.
func textFieldDidBeginEditing(textField: UITextField) { // became first responder
//move textfields up
let myScreenRect: CGRect = UIScreen.mainScreen().bounds
let keyboardHeight : CGFloat = 216
UIView.beginAnimations( "animateView", context: nil)
var movementDuration:NSTimeInterval = 0.35
var needToMove: CGFloat = 0
var frame : CGRect = self.view.frame
if (textField.frame.origin.y + textField.frame.size.height + /*self.navigationController.navigationBar.frame.size.height + */UIApplication.sharedApplication().statusBarFrame.size.height > (myScreenRect.size.height - keyboardHeight)) {
needToMove = (textField.frame.origin.y + textField.frame.size.height + /*self.navigationController.navigationBar.frame.size.height +*/ UIApplication.sharedApplication().statusBarFrame.size.height) - (myScreenRect.size.height - keyboardHeight);
}
frame.origin.y = -needToMove
self.view.frame = frame
UIView.commitAnimations()
}
func textFieldDidEndEditing(textField: UITextField) {
//move textfields back down
UIView.beginAnimations( "animateView", context: nil)
var movementDuration:NSTimeInterval = 0.35
var frame : CGRect = self.view.frame
frame.origin.y = 0
self.view.frame = frame
UIView.commitAnimations()
}
Swift 4 code, It is very simple instead of using many things like NSNotificationCenter, then calculating the height of everything and making conditions makes this more complicated,
The Simple way to do this is coded below, it will work to move up the view.
func textFieldDidBeginEditing(_ textField: UITextField) {
moveTextField(textfield: textField, moveDistance: -250, up: true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
moveTextField(textfield: textField, moveDistance: -250, up: false)
}
func moveTextField(textfield: UITextField, moveDistance: Int, up: Bool) {
let moveDuration = 0.3
let movement: CGFloat = CGFloat(up ? moveDistance: -moveDistance)
UIView.beginAnimations("animateTextField", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(moveDuration)
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()
}
you can change that -250 value according to the placement of your textfields.
In case you are using a UIScrollView or any of its subclasses, e.g., UITableView, you can also manipulate the contentInset property. That way you do not have to mess with frame, bounds, NSLayoutConstraint or NSLayoutAnchor.
func keyboardWillShow(notification: Notification) {
let info = notification.userInfo!
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let keyboardHeight: CGFloat = keyboardSize.height
let duration = info[UIKeyboardAnimationDurationUserInfoKey] as! TimeInterval
UIView.animate(withDuration: duration, delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
self.tableView?.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
}, completion: nil)
}
func keyboardWillHide(notification: Notification) {
let info = notification.userInfo!
let duration = info[UIKeyboardAnimationDurationUserInfoKey] as! TimeInterval
UIView.animate(withDuration: duration, delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
self.tableView?.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}, completion: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
Swift 3.0
var activeField: UITextField?
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ProfileViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ProfileViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func textFieldDidBeginEditing(_ textField: UITextField){
activeField = textField
}
func textFieldDidEndEditing(_ textField: UITextField){
activeField = nil
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if (self.activeField?.frame.origin.y)! >= keyboardSize.height {
self.view.frame.origin.y = keyboardSize.height - (self.activeField?.frame.origin.y)!
} else {
self.view.frame.origin.y = 0
}
}
}
func keyboardWillHide(notification: NSNotification) {
self.view.frame.origin.y = 0
}
In Swift 3 use this code
override func viewDidLoad() {
super.viewDidLoad()
let center: NotificationCenter = NotificationCenter.default
center.addObserver(self, selector: #selector(RFLogInViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
center.addObserver(self, selector: #selector(RFLogInViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
let info:NSDictionary = notification.userInfo! as NSDictionary
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let keyboardHeight: CGFloat = keyboardSize.height
let _: CGFloat = info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
UIView.animate(withDuration: 0.25, delay: 0.25, options: .curveEaseInOut, animations: {
self.view.frame = CGRect(x: 0, y: (self.view.frame.origin.y - keyboardHeight), width: self.view.bounds.width, height: self.view.bounds.height)
}, completion: nil)
}
func keyboardWillHide(notification: NSNotification) {
let info: NSDictionary = notification.userInfo! as NSDictionary
let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let keyboardHeight: CGFloat = keyboardSize.height
let _: CGFloat = info[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber as! CGFloat
UIView.animate(withDuration: 0.25, delay: 0.25, options: .curveEaseInOut, animations: {
self.view.frame = CGRect(x: 0, y: (self.view.frame.origin.y + keyboardHeight), width: self.view.bounds.width, height: self.view.bounds.height)
}, completion: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
Just add 'IQKeyboardManager' library into your project, and Done. You have not to do anything else. For reference please check this url.
https://github.com/hackiftekhar/IQKeyboardManager
Swift 4
I have seen numerous of answer and plenty of them did not work for me where I have a UIViewController With numerous of text fields.
According to the Apple documentation I have translate the example to Swift 4
Your content needs to be embedded within an scrollview.
Add the notification listeners for when the keyboard will appear or dissappear.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: .UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
Implement UITextField Delegates
func textFieldDidBeginEditing(_ textField: UITextField) {
currentTextField = textField
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
currentTextField = nil
}
Selectors
#objc func keyboardDidShow(notification: NSNotification) {
print("\(logClassName): keyboardWDidShow")
let keyboardFrame = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let keyboardSize:CGSize = keyboardFrame!.size
let contentInsets:UIEdgeInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
trackScrollView.contentInset = contentInsets
trackScrollView.scrollIndicatorInsets = contentInsets
var aRect:CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
if !(aRect.contains(currentTextField!.frame.origin)){
trackScrollView.scrollRectToVisible(currentTextField!.frame, animated: true)
}
}
#objc func keyboardWillHide(notification: NSNotification){
print("\(logClassName): keyboardWillHide")
let contentInsents:UIEdgeInsets = UIEdgeInsets.zero
trackScrollView.contentInset = contentInsents
trackScrollView.scrollIndicatorInsets = contentInsents
}
first of all you should have scrollview
step 1:find the height of key board
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo ? [UIKeyboardFrameBeginUserInfoKey] as ? NSValue) ? .cgRectValue {
let keyboardHeight = keyboardSize.height
a = keyboardHeight
}
}
step 2: bind the delegete methods of textfield
func textFieldDidBeginEditing(_ textField: UITextField) {
utility.setUserDefaultBool(value: false, key: "FrameMoveFlag") //flag
let b = view1.frame.height - textField.frame.origin.y
if (b < 350) {
view1.frame.origin.y = view1.frame.origin.y + (view1.frame.height - textField.frame.origin.y) - (a + 50)
utility.setUserDefaultBool(value: true, key: "FrameMoveFlag") //flag
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if (utility.getUserDefaultBOOLForKey(key: "FrameMoveFlag") == true) {
view1.frame.origin.y = 0
}
}
Swift 3 code for Geiger answer
func textFieldDidBeginEditing(_ textField: UITextField) { // became first responder
//move textfields up
let myScreenRect: CGRect = UIScreen.main.bounds
let keyboardHeight : CGFloat = 216
UIView.beginAnimations( "animateView", context: nil)
var movementDuration:TimeInterval = 0.35
var needToMove: CGFloat = 0
var frame : CGRect = self.view.frame
if (textField.frame.origin.y + textField.frame.size.height + UIApplication.shared.statusBarFrame.size.height > (myScreenRect.size.height - keyboardHeight - 30)) {
needToMove = (textField.frame.origin.y + textField.frame.size.height + UIApplication.shared.statusBarFrame.size.height) - (myScreenRect.size.height - keyboardHeight - 30);
}
frame.origin.y = -needToMove
self.view.frame = frame
UIView.commitAnimations()
}
func textFieldDidEndEditing(_ textField: UITextField) {
//move textfields back down
UIView.beginAnimations( "animateView", context: nil)
var movementDuration:TimeInterval = 0.35
var frame : CGRect = self.view.frame
frame.origin.y = 0
self.view.frame = frame
UIView.commitAnimations()
}
If you don't want to manually work with appearing and disappearing of the keyboard, just use the UITableViewController and it will handle all text fields in the table view.
Check out my gist, I use a scrollView for my case, but it works for any kind of view, you only have to remove the scrollView part and replace it with your view.
The gist is very well commented so you will also understand how this case is handled.
https://gist.github.com/Sjahriyar/916e93153a29dc602b45f29d39182352
I created KeyboardController to handle the keyboard issue. All that needs to be done is call setUpKeyBoardListeners() and set the lastElement as whatever the last element in your view is.
Gist: https://gist.github.com/espitia/ef830cf677fa1bc33ffdf16ac12d0204

Resources