Created
February 9, 2022 06:57
-
-
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.
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
// 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