Last active
February 17, 2020 05:16
-
-
Save Erkened/750f7bf058947f2663b16e30f9bee95f to your computer and use it in GitHub Desktop.
UIScrollView example, with a textfield that's not covered when the keyboard appears, fully programmatic with AutoLayout. Swift 4.0
This file contains hidden or 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
// | |
// ViewController.swift | |
// Repnote | |
// | |
// Created by John Neumann on 05/07/2017. | |
// Copyright © 2017 Audioy. All rights reserved. | |
// | |
import UIKit | |
class ViewController: UIViewController { | |
fileprivate var activeField: UITextField? | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
view.backgroundColor = UIColor.white | |
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) | |
addSubviewsAndSetupContraints() | |
} | |
deinit { | |
NotificationCenter.default.removeObserver(self) | |
} | |
override func viewDidLayoutSubviews() { | |
super.viewDidLayoutSubviews() | |
view.layoutIfNeeded() // Give the containerView a size | |
scrollView.contentSize = containerView.bounds.size // Set the content size to that of the container | |
} | |
// UI | |
fileprivate lazy var scrollView: UIScrollView = { | |
let sv = UIScrollView(frame: .zero) | |
self.automaticallyAdjustsScrollViewInsets = false // Set as false or there will be padding above the container view | |
sv.contentInset = .zero // ScrollViews have insets naturally - remove them | |
sv.backgroundColor = UIColor.green | |
return sv | |
}() | |
fileprivate lazy var containerView: UIView = { | |
let v = UIView() | |
v.backgroundColor = UIColor.red | |
return v | |
}() | |
fileprivate lazy var testTextField: UITextField = { | |
let tf = UITextField() | |
tf.backgroundColor = UIColor.white | |
tf.placeholder = "HELLO WORLD" | |
tf.delegate = self | |
return tf | |
}() | |
} | |
extension ViewController: AutoLayoutType{ | |
func addSubviewsAndSetupContraints() { | |
containerView.addSubview(testTextField) | |
scrollView.addSubview(containerView) | |
view.addSubview(scrollView) | |
NSLayoutConstraint.useAndActivate([ | |
scrollView.topAnchor.constraint(equalTo: view.topAnchor), | |
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor), | |
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor), | |
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor) | |
]) | |
NSLayoutConstraint.useAndActivate([ | |
containerView.topAnchor.constraint(equalTo: scrollView.topAnchor), | |
containerView.leftAnchor.constraint(equalTo: scrollView.leftAnchor), | |
containerView.widthAnchor.constraint(equalTo: view.widthAnchor), // The scrollView fits its content, so set the width to that of the view | |
containerView.bottomAnchor.constraint(equalTo: testTextField.bottomAnchor, constant: 30) // Set to the bottom of the last element | |
]) | |
NSLayoutConstraint.useAndActivate([ | |
testTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor), | |
testTextField.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 900) | |
]) | |
} | |
} | |
extension ViewController: UITextFieldDelegate{ | |
func textFieldDidBeginEditing(_ textField: UITextField) { | |
activeField = textField | |
} | |
func textFieldDidEndEditing(_ textField: UITextField) { | |
activeField = nil | |
} | |
func textFieldShouldReturn(_ textField: UITextField) -> Bool{ | |
textField.resignFirstResponder() | |
return false | |
} | |
// Automatically adjust the scroll view IF the keyboard covers the textfield | |
@objc fileprivate func keyboardWillShow(notification: NSNotification) { | |
if let userInfo = notification.userInfo, | |
let keyboardSize = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, | |
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue, | |
let animationCurveRaw = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber | |
{ | |
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: UIViewAnimationOptions.RawValue(truncating: animationCurveRaw)) | |
let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height, 0.0) | |
UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: { | |
self.scrollView.contentInset = contentInsets | |
self.scrollView.scrollIndicatorInsets = contentInsets | |
var aRect : CGRect = self.view.frame | |
aRect.size.height -= keyboardSize.height | |
if let activeField = self.activeField { | |
if (!aRect.contains(activeField.frame.origin)){ | |
self.scrollView.scrollRectToVisible(activeField.frame, animated: false) | |
} | |
} | |
}, completion: nil) | |
} | |
} | |
@objc fileprivate func keyboardWillHide(notification: NSNotification) { | |
if let userInfo = notification.userInfo, | |
let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue, | |
let animationCurveRaw = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber | |
{ | |
let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: UIViewAnimationOptions.RawValue(truncating: animationCurveRaw)) | |
UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: { | |
self.scrollView.contentInset = .zero | |
self.scrollView.scrollIndicatorInsets = .zero | |
self.view.endEditing(true) | |
}, completion: nil) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment