Skip to content

Instantly share code, notes, and snippets.

@AndyDentFree
Created February 9, 2022 06:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AndyDentFree/ab19aed699661feb9ae01af73d89b538 to your computer and use it in GitHub Desktop.
Save AndyDentFree/ab19aed699661feb9ae01af73d89b538 to your computer and use it in GitHub Desktop.
Dynamically sizing a text entry field in an iOS app, depending on keyboard being shown.
// Created by Andy Dent on 9/2/22.
// Copyright © 2022 Touchgram Pty Ltd. All rights reserved.
import Foundation
import UIKit
/// portions showing how the sizing works
class SimpleTextNodeDetailViewController: UIViewController {
@IBOutlet var labelEntryWidthToOverall: NSLayoutConstraint!
@IBOutlet var labelEntryWidthToPreview: NSLayoutConstraint!
// connected to adjust item height when keyboard shown
@IBOutlet var bottomOutletToAdjust: NSLayoutConstraint!
var saveOutletOffset: CGFloat {get set}
override public func viewDidLoad() {
super.viewDidLoad()
// ...
setupScrollForKeyboard()
entryWidthConfiguredFor(keyboardShown: false)
}
// helper ensures only one width constraint active at a time
private func entryWidthConfiguredFor(keyboardShown: Bool) {
if keyboardShown {
NSLayoutConstraint.deactivate([labelEntryWidthToPreview])
NSLayoutConstraint.activate([labelEntryWidthToOverall])
} else {
NSLayoutConstraint.deactivate([labelEntryWidthToOverall])
NSLayoutConstraint.activate([labelEntryWidthToPreview])
}
}
func setupScrollForKeyboard() {
saveOutletOffset = bottomOutletToAdjust?.constant ?? 0.0
// note do NOT try using #selector as you cannot within extensions, this is the closure-based alternative
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue:nil) {
[weak self] notification in
self?.adjustForKeyboardHide()
}
notificationCenter.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue:nil) {
[weak self] notification in
self?.adjustForKeyboardResize(notification:notification)
}
}
func adjustForKeyboardHide() {
guard let bot = bottomOutletToAdjust else {
os_log("Unable to adjustForKeyboardHide as no bound outlet", log:tgEnv.logImUI, type:.debug)
return
}
os_log("Keyboard hidden, resetting layout constant", log:tgEnv.logImUI, type:.debug)
bot.constant = saveOutletOffset
entryWidthConfiguredFor(keyboardShown: false)
}
func adjustForKeyboardResize(notification: Notification) {
guard let bot = bottomOutletToAdjust else {
os_log("Unable to adjustForKeyboardResize as no bound outlet", log:tgEnv.logImUI, type:.debug)
return
}
guard let userInfo = notification.userInfo else { return }
// cannot cache keyboard sizes as user may change keyboard types to trigger this
let keyboardScreenEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
os_log("Keyboard resized, updating layout constant", log:tgEnv.logImUI, type:.debug)
entryWidthConfiguredFor(keyboardShown: true)
bot.constant = max(keyboardViewEndFrame.height + 4, saveOutletOffset) // when we are using beyond the normal scrollview adjustment, may have something that's already above the keyboard so don't want to drag it down.
}
private func entryWidthConfiguredFor(keyboardShown: Bool) {
if keyboardShown {
NSLayoutConstraint.deactivate([labelEntryWidthToPreview])
NSLayoutConstraint.activate([labelEntryWidthToOverall])
} else {
NSLayoutConstraint.deactivate([labelEntryWidthToOverall])
NSLayoutConstraint.activate([labelEntryWidthToPreview])
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment