Skip to content

Instantly share code, notes, and snippets.

@Erkened
Last active February 17, 2020 05:16
Show Gist options
  • Save Erkened/750f7bf058947f2663b16e30f9bee95f to your computer and use it in GitHub Desktop.
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
//
// 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