ScrollView did not move up when click textfield inside tableView - ios

I tried this code :
in viewWillAppear :
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: Notification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
and :
#objc func keyboardWillAppear(notification:NSNotification) {
print("keyboard appear")
var info = notification.userInfo
let keyBoardSize = info![UIKeyboardFrameEndUserInfoKey] as! CGRect
scrollCreateEditContact.contentInset = UIEdgeInsetsMake(0.0, 0.0, keyBoardSize.height, 0.0)
scrollCreateEditContact.scrollIndicatorInsets = UIEdgeInsetsMake(0.0, 0.0, keyBoardSize.height, 0.0)
}
#objc func keyboardWillDisappear(notification:NSNotification) {
print("keyboard disappear")
scrollCreateEditContact.contentInset = UIEdgeInsets.zero
scrollCreateEditContact.scrollIndicatorInsets = UIEdgeInsets.zero
}
and result is :
what I want is that textfield did not covered by keyboard when keyboard appear like this :
That code only work on textfield that is not inside tableView.
But when I click textfield inside tableView and log it "keyboard appear" always detected.
What is the correct code for textfield inside tableView not covered by keyboard when keyboard appear?

Thats common behaviour, ios cannot adjust content above keyboard automatically, unlike android. My solution is, you can wrap all that views (photo, textfield, etc) inside tableView. And use TPKeyboardAvoiding library.
pod 'TPKeyboardAvoiding'
If you use storyboard, set tableView base-class to TPKeyboardAvoidingTableView.

The easiest way to handle this problem is installing pod for IQKeyboardManager:
Installation via cocoa pods:
pod 'IQKeyboardManagerSwift'
Usage:
import IQKeyboardManagerSwift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
IQKeyboardManager.shared.enable = true
return true
}
}
For more information on IQKeyboardManager refer this link:
https://github.com/hackiftekhar/IQKeyboardManager

Here are the most common and used libraries for managing such behavior:
TPKeyboardAvoiding
IQKeyboardManager
You can install them via CocoaPods as well.

Related

How to move UITextFeild with keyboard in order to avoid blocking [SWIFT 5+ ]

I know that there are several questions on this topic, but i have not been able to find any on [SWIFT 5.0] or more, and would really appreciate your help.
I have a UITextFeild at the bottom of the screen which gets hidden every time the user clicks on it. Is there a recent method on which i can solve this hiding issue, by either the textFeild following the keyboard to the top or a sample field appearing on top of the keyboard. Any help would be greatly appreciated, thank you!
You can use the following code:
First, add these two lines of code in your controller viewDidLoad() method:
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
Second, add these two methods inside your controller:
#objc func keyboardWillShow(notification: NSNotification) {
if yourTextfield.isEditing == true
{
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y = 64
}
}
Enjoy :).
If you use AutoLayout you may try Keyboard and KeyboardLayoutGuide from my collection of handy Swift extensions and classes called SwiftToolkit
Add this code to your controller viewDidLoad() method:
let keyboardGuide = KeyboardLayoutGuide()
view.addLayoutGuide(keyboardGuide)
/**
Your textField vertical position constraint
must have lower priority than this constraint
*/
keyboardGuide.topAnchor.constraint(greaterThanOrEqualTo: textField.bottomAnchor, constant: 8).isActive = true
When the keyboard appears it will push your UITextField up like this:

How to solve keyboard problems in Swift 3?

The problem is that when I try to write in the text fields the keyboard cover them up. How can I scroll the text field up to see what am I writing. I have below lines of code to enable the return key and to hide the keyboard when you touch in a different place:
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.view.endEditing(true)
return false
}
How can I scroll the text field up to see what am I writing
This can be achieved in two ways:
You can manage your frame programmatically on keyboard hide and show like #Taichi Kato
You can integrate libraries which serves the same purpose. One such library is IQKeyBoardManager with its Swift variant as IQKeyboardManagerSwift You can find it on GitHub and cocoapods
To achieve follow the below steps :
SWIFT 3
Just install IQKeyboardManagerSwift from Github or cocoapods
Import IQKeyboardManagerSwift in Appdelegate
Add the below lines of code in AppDelegate didFinishLaunchingWithOptions method.
IQKeyboardManager.sharedManager().shouldResignOnTouchOutside = true;
Objective-C
Install IQKeyBoardManager via any medium
Import #import "IQKeyboardManager.h" in Appdelegate
Add the below lines of code in AppDelegate didFinishLaunchingWithOptions method.
IQKeyboardManager.sharedManager.shouldResignOnTouchOutside = true;
And this is Done. This is the only code which you need to write.
The easiest solution to this problem is to put all the elements into one scrollview and then add the keyboard height to the constant of the bottom of the view to superview.
When the keyboard is shown or hidden, iOS sends out the following notifications to any registered observers:
UIKeyboardWillShowNotification
UIKeyboardDidShowNotification
UIKeyboardWillHideNotification
UIKeyboardDidHideNotification
So here is what you can do:
Get the size of the keyboard.
Adjust the bottom content inset of
your scroll view by the keyboard height.
Scroll the target text
field into view.
Something like this:
func keyboardWillShow(notification: NSNotification) {
print("KEYBOARD WILL SHOW")
let userInfo:NSDictionary = notification.userInfo! as NSDictionary
let keyboardFrame:NSValue = userInfo.value(forKey: UIKeyboardFrameEndUserInfoKey) as! NSValue
let keyboardRectangle = keyboardFrame.cgRectValue
let keyboardHeight = keyboardRectangle.height
bottomConstraint.constant = keyboardHeight + 8
UIView.animate(withDuration: 0.5, animations: { [weak self] in
self?.view.layoutIfNeeded() ?? ()
})
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
bottomConstraint.constant = 8
UIView.animate(withDuration: 0.5, animations: { [weak self] in
self?.view.layoutIfNeeded() ?? ()
})
}
You can use this two framework for keyboard problem solved:
IQKeyboardManager : Link
iOSUtilitiesSource: Link

iOS - Floating Single Button

I need a floating button in my iPhone app, I saw many libraries online like this one: https://cocoapods.org/pods/LiquidFloatingActionButton
But I need it to be just a single button, not a menu - Do you know of any other libraries that do this? Or a way to customise one of the current libraries to do what I need?
Thanks!
import UIKit
final class AppDelegate: NSObject, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let buttonDiameter: CGFloat = 44.0
let floatingButton = UIButton(frame: CGRect(x: 0.0, y: 0.0, width: buttonDiameter, height: buttonDiameter))
floatingButton.layer.cornerRadius = buttonDiameter / 2.0
floatingButton.backgroundColor = .redColor()
window?.addSubview(floatingButton)
return true
}
}
This example would add a floating button to the top left corner of your app.
The important aspect of this code is that I am adding the button the UIWindow.
You can access the window of the app from any UIViewController whose view is added to the window via the property window.
To get notifications when a view controller is added to the window you can override the function:
didMoveToWindow()
There is an approach to this by using KCFloating action button and adding a tapGestureRecognizer to a view containing this button.
So, just adding KCFloatingActionButton's pod and adding another view containing it it's possible to have a single floating action button just as androids.
If you require more detail to solve this issue tell me and I'll try to help
Sample image
only copy and paste this code
override func viewDidAppear(_ animated: Bool) {
layoutFAB()
}
func layoutFAB() {
let item = KCFloatingActionButton()
item.buttonColor = UIColor(red: 188/255, green: 46/255, blue: 35/255, alpha: 1)
}
override func viewDidLoad() {
super.viewDidLoad()
let Fab = KCFloatingActionButton()
Fab.addItem("a", icon: UIImage(named: "a")){ item in
print("a")
}
}
self.view.addSubview(Fab)
}

Handle another app's obscuring keyboard on iPad split view (iOS 9 multitasking)

Previously if one presented a keyboard on one's own app one would embed everything in a UIScrollView and adjust the contentInset to keep content from being obscured by the keyboard.
Now with split view multitasking on iOS 9 the keyboard may appear at any moment and stay visible even while the user is no longer interacting with the other app.
Question
Is there an easy way to adapt all view controllers that were not expecting the keyboard to be visible and without start embedding everything in scrollviews?
The secret is to listen to the UIKeyboardWillChangeFrame notification that is triggered whenever the keyboard is shown/hidden from your app or from another app running side by side with yours.
I created this extension to make it easy to start/stop observing those events (I call them in viewWillAppear/Disappear), and easily get the obscuredHeight that is usually used to adjust the bottom contentInset of your table/collection/scrollview.
#objc protocol KeyboardObserver
{
func startObservingKeyboard() // Call this in your controller's viewWillAppear
func stopObservingKeyboard() // Call this in your controller's viewWillDisappear
func keyboardObscuredHeight() -> CGFloat
#objc optional func adjustLayoutForKeyboardObscuredHeight(_ obscuredHeight: CGFloat, keyboardFrame: CGRect, keyboardWillAppearNotification: Notification) // Implement this in your controller and adjust your bottom inset accordingly
}
var _keyboardObscuredHeight:CGFloat = 0.0;
extension UIViewController: KeyboardObserver
{
func startObservingKeyboard()
{
NotificationCenter.default.addObserver(self, selector: #selector(observeKeyboardWillChangeFrameNotification(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func stopObservingKeyboard()
{
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
func observeKeyboardWillChangeFrameNotification(_ notification: Notification)
{
guard let window = self.view.window else {
return
}
let animationID = "\(self) adjustLayoutForKeyboardObscuredHeight"
UIView.beginAnimations(animationID, context: nil)
UIView.setAnimationCurve(UIViewAnimationCurve(rawValue: (notification.userInfo![UIKeyboardAnimationCurveUserInfoKey]! as AnyObject).intValue)!)
UIView.setAnimationDuration((notification.userInfo![UIKeyboardAnimationCurveUserInfoKey]! as AnyObject).doubleValue)
let keyboardFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue
_keyboardObscuredHeight = window.convert(keyboardFrame!, from: nil).intersection(window.bounds).size.height
let observer = self as KeyboardObserver
observer.adjustLayoutForKeyboardObscuredHeight!(_keyboardObscuredHeight, keyboardFrame: keyboardFrame!, keyboardWillAppearNotification: notification)
UIView.commitAnimations()
}
func keyboardObscuredHeight() -> CGFloat
{
return _keyboardObscuredHeight
}
}

Get height of iOS keyboard without displaying keyboard

I'm trying to get the height of the iOS keyboard. I've gone through and used the method involving subscribing to a notification such as detailed here:
https://gist.github.com/philipmcdermott/5183731
- (void)viewDidAppear:(BOOL) animated {
[super viewDidAppear:animated];
// Register notification when the keyboard will be show
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
// Register notification when the keyboard will be hide
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
CGRect keyboardBounds;
[[notification.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardBounds];
// Do something with keyboard height
}
- (void)keyboardWillHide:(NSNotification *)notification {
CGRect keyboardBounds;
[[notification.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardBounds];
// Do something with keyboard height
}
This works fine for when the user actually displays the keyboard.
My problem: I have another view, let's call it micView, that may be presented before the keyboard appears. The user may choose to use the microphone before typing. I would like the micView to be the same height as the keyboard, which is why I need the keyboard's height, but I need it before the keyboard was forced to appear. Thus the UIKeyboardWillShowNotification is not reached before I need to read the value of the height.
My question is: how do I get the height of the keyboard through Notifications, or some other method without ever having the keyboard appear.
I considered explicitly forcing the keyboard to appear in viewDidLoad, so that I can set an instance variable to that value, then hiding it and getting rid of the animations for both things. But is that really the only way to do that?
This Swift class provides a turn-key solution that manages all the necessary notifications and initializations, letting you simply call a class method and have returned the keyboard size or height.
Calling from Swift:
let keyboardHeight = KeyboardService.keyboardHeight()
let keyboardSize = KeyboardService.keyboardSize()
Calling from Objective-C:
CGFloat keyboardHeight = [KeyboardService keyboardHeight];
CGRect keyboardSize = [KeyboardService keyboardSize];
If wanting to use this for initial view layout, call this from the viewWillAppear method of a class where you want the keyboard height or size before the keyboard appears. It should not be called in viewDidLoad, as a correct value relies on your views having been laid out. You can then set an autolayout constraint constant with the value returned from the KeyboardService, or use the value in other ways. For instance, you might want to obtain the keyboard height in prepareForSegue to assist in setting a value associated with the contents of a containerView being populated via an embed segue.
Note re safe area, keyboard height, and iPhone X:
The value for keyboard height returns the full height of the keyboard, which on the iPhone X extends to the edge of the screen itself, not just to the safe area inset. Therefore, if setting an auto layout constraint value with the returned value, you should attach that constraint to the superview bottom edge, not to the safe area.
Note re hardware keyboard in Simulator:
When a hardware keyboard is attached, this code will provide the on-screen height of that hardware keyboard, that is, no height. This state does need to be accounted for, of course, as this simulates what will occur if you have a hardware keyboard attached to an actual device. Therefore, your layout that is expecting a keyboard height needs to respond appropriately to a keyboard height of zero.
KeyboardService class:
As usual, if calling from Objective-C, you simply need to import the app's Swift bridging header MyApp-Swift.h in your Objective-C class.
import UIKit
class KeyboardService: NSObject {
static var serviceSingleton = KeyboardService()
var measuredSize: CGRect = CGRect.zero
#objc class func keyboardHeight() -> CGFloat {
let keyboardSize = KeyboardService.keyboardSize()
return keyboardSize.size.height
}
#objc class func keyboardSize() -> CGRect {
return serviceSingleton.measuredSize
}
private func observeKeyboardNotifications() {
let center = NotificationCenter.default
center.addObserver(self, selector: #selector(self.keyboardChange), name: .UIKeyboardDidShow, object: nil)
}
private func observeKeyboard() {
let field = UITextField()
UIApplication.shared.windows.first?.addSubview(field)
field.becomeFirstResponder()
field.resignFirstResponder()
field.removeFromSuperview()
}
#objc private func keyboardChange(_ notification: Notification) {
guard measuredSize == CGRect.zero, let info = notification.userInfo,
let value = info[UIKeyboardFrameEndUserInfoKey] as? NSValue
else { return }
measuredSize = value.cgRectValue
}
override init() {
super.init()
observeKeyboardNotifications()
observeKeyboard()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
Head nod:
The observeKeyboard method here based on the original approach outlined by Peres in the Objective-C answer to this question.
A quick solution that you could use, is the same one used when you want to cache the keyboard (the first time you show it, you get a slight delay...). The library is here. The interesting bits:
[[[[UIApplication sharedApplication] windows] lastObject] addSubview:field];
[field becomeFirstResponder];
[field resignFirstResponder];
[field removeFromSuperview];
So basically is showing it and then hiding it. You could listen for notifications and just get the height without actually seeing it. Bonus: you get to cache it. :)
Looks like this solution did stop working.
I modified it:
adding a callback to know when the notification arrives with the real height,
moving the textfield to another window to avoid showing it, and
setting a timeout for the case when is used in the simulator and the software keyboard is setted up to now show.
Using Swift 4:
import UIKit
public class KeyboardSize {
private static var sharedInstance: KeyboardSize?
private static var measuredSize: CGRect = CGRect.zero
private var addedWindow: UIWindow
private var textfield = UITextField()
private var keyboardHeightKnownCallback: () -> Void = {}
private var simulatorTimeout: Timer?
public class func setup(_ callback: #escaping () -> Void) {
guard measuredSize == CGRect.zero, sharedInstance == nil else {
return
}
sharedInstance = KeyboardSize()
sharedInstance?.keyboardHeightKnownCallback = callback
}
private init() {
addedWindow = UIWindow(frame: UIScreen.main.bounds)
addedWindow.rootViewController = UIViewController()
addedWindow.addSubview(textfield)
observeKeyboardNotifications()
observeKeyboard()
}
public class func height() -> CGFloat {
return measuredSize.height
}
private func observeKeyboardNotifications() {
let center = NotificationCenter.default
center.addObserver(self, selector: #selector(self.keyboardChange), name: UIResponder.keyboardDidShowNotification, object: nil)
}
private func observeKeyboard() {
let currentWindow = UIApplication.shared.keyWindow
addedWindow.makeKeyAndVisible()
textfield.becomeFirstResponder()
currentWindow?.makeKeyAndVisible()
setupTimeoutForSimulator()
}
#objc private func keyboardChange(_ notification: Notification) {
textfield.resignFirstResponder()
textfield.removeFromSuperview()
guard KeyboardSize.measuredSize == CGRect.zero, let info = notification.userInfo,
let value = info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue
else { return }
saveKeyboardSize(value.cgRectValue)
}
private func saveKeyboardSize(_ size: CGRect) {
cancelSimulatorTimeout()
KeyboardSize.measuredSize = size
keyboardHeightKnownCallback()
KeyboardSize.sharedInstance = nil
}
private func setupTimeoutForSimulator() {
#if targetEnvironment(simulator)
let timeout = 2.0
simulatorTimeout = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false, block: { (_) in
print(" KeyboardSize")
print(" .keyboardDidShowNotification did not arrive after \(timeout) seconds.")
print(" Please check \"Toogle Software Keyboard\" on the simulator (or press cmd+k in the simulator) and relauch your app.")
print(" A keyboard height of 0 will be used by default.")
self.saveKeyboardSize(CGRect.zero)
})
#endif
}
private func cancelSimulatorTimeout() {
simulatorTimeout?.invalidate()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
Is used in the following way:
let splashVC = some VC to show in the key window during the app setup (just after the didFinishLaunching with options)
window.rootViewController = splashVC
KeyboardSize.setup() { [unowned self] in
let kbHeight = KeyboardSize.height() // != 0 :)
// continue loading another things or presenting the onboarding or the auth
}
For iOS 14.0, I noticed that this solution stopped working on around the 10th call as NotificationCenter stopped broadcasting keyboardChange notification. I was not able to fully figure out why that was happening.
So, I tweaked the solution to make KeyboardSize a singleton and added a method updateKeyboardHeight() as such:
static let shared = KeyboardSize()
/**
Height of keyboard after the class is initialized
*/
private(set) var keyboardHeight: CGFloat = 0.0
private override init() {
super.init()
observeKeyboardNotifications()
observeKeyboard()
}
func updateKeyboardHeight() {
observeKeyboardNotifications()
observeKeyboard()
}
and used it as
KeyboardSize.shared.updateKeyboardHeight()
let heightOfKeyboard = KeyboardSize.shared.keyboardHeight

Resources