I am creating a iOS application for a student registration. My problem is I cannot get the auto layout to work for all the devices. I want to layout my interface with even spacing between each label and textview. Any help will be much appreciated. storyboard capture here
#IBOutlet weak var bottomconst: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
//
// NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyBoardDidShow:", name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardWillShowNotification, object: nil)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
func keyboardWasShown(notification: NSNotification) {
let info = notification.userInfo!
let keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
//bottomspace = self.bottomconst.constant;
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.view.layoutIfNeeded()
self.bottomconst.constant = keyboardFrame.size.height + 20
})
}
func keyboardWillHide(sender: NSNotification) {
if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.bottomconst?.constant -= keyboardSize.height
}
}
I am attaching a screenshot with constraints so that you can basic idea how to put proper constraints, but in your case it would be better to go with table view if your number of fields increases then all your fields would come automatically inside scrollView and it would be easliy visible in smaller devices, But you can have look into this screenshot if you don't want to use tableview and have less number of fields.
A UITableViewController would be the best approach.
Just keep in mind that UIStackView provides a streamlined way to automatically lay out a collection of views. It handles the alignment and spacing for you:
Related
I am developing an iOS app and
I have a messaging view where I want to handle this situation:
-> I have a input view at the bottom of the view, that needs to be visible all the time except for some criteria where user is blocked/restricted to send message.
-> when the input view is focused, keyboard appears, I want to move the view along with keyboard frame.
-> I want keyboard to dismiss interactively with table view scrolling. With this being said, the view should respond to keyboard pan gesture and move along with as well
-> I tried using input accessory view but problem with that was when keyboard gets dismissed with table view scrolling, input view gets dismissed as well.
-> I also tried using willShow/willHide/willChangeFrame observers but with this, response is not to the point and it doesn't respond to keyboard interactive dismissal.
Anybody got solution to this...
Thanks for your time.
Swift 3+:
I have take a view into textview background and set the constraint of view (leading, trailing, bottom, fixed height). Create a #IBOutlet for bottom constraint and manage that below code:
class ViewController: UIViewController {
#IBOutlet var bottomConstraint: NSLayoutConstraint!
#IBOutlet var view_TextViewBg: UIView!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardDidShow(_:)),
name: NSNotification.Name.UIKeyboardWillShow,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardDidHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(sender:)))
// tap.delegate = self
view.addGestureRecognizer(tap)
}
func handleTap(sender: UITapGestureRecognizer? = nil) {
//dissmiss your keyboard here
}
//MARK: Keyboard show
func keyboardDidShow(_ notification: Notification) {
let params = notification.userInfo
let rect: CGRect? = (params?[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue
bottomConstraint.constant = (rect?.size.height)!
}
//MARK: Keyboard hide
func keyboardDidHide(_ notification: Notification) {
bottomConstraint.constant = 0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am trying to make my code more organised and reusable. I have some functions and notification that allows the scroll view to be moved up when keyboard shows up and scroll down when keyboard hides. It is all functioning. However, I would imaging these function will be used in multiple parts of my project that has scrollView inside UIViewcontroller. so I want to create a more resuable code rather than writing the same codes in multiple view controllers.
Currently, inside one of my view controller, I have
var keyboard = CGRect()
override func viewDidLoad() {
// Check notifications of keyboard activity
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PostVC.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PostVC.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
// Tap to hide keyboard
let hideTap = UITapGestureRecognizer(target: self, action: #selector(PostVC.hideKeyboard))
hideTap.numberOfTapsRequired = 1
self.view.userInteractionEnabled = true
self.view.addGestureRecognizer(hideTap)
}
func hideKeyboard() {
self.view.endEditing(true)
}
func keyboardWillShow(notification: NSNotification) {
keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
UIView.animateWithDuration(0.4) {
self.scrollView.contentSize.height = self.view.frame.size.height + self.keyboard.height / 2 + UITabBarController().tabBar.frame.size.height
}
}
func keyboardWillHide(notification: NSNotification) {
UIView.animateWithDuration(0.4) {
self.scrollView.contentSize.height = 0
}
}
I am abit new to trying to make code more re-usable. I am not sure if I need to create a new class or just create a extension of UIViewcontroller and put it in there. I have tried creating an extension of UIViewcontroller and do something like
func keyboardWillShow(notification: NSNotification, _scrollView: UIScrollView) { }
and pass an instance of the scrollview (#IBOutlet weak var scrollView: UIScrollView!) into the function. However, I then run into trouble with doing #selector(keyboardWillShow(_:, keyboard: keyboard, scrollView: scrollView). It gives me an error saying expected expression in list of expressions (I think it is complaining about _:). I might be on a totally wrong path of doing this. Can anyone please help.
Thanks,
I would recommend creating a protocol and adding the default implementation as a protocol extension. Into the protocol you can add that classes that implement it should have a scrollView and a keyboard. Have in mind that protocol extensions are more flexible than base classes and that's why are often preferred in Swift.
Here is an example
protocol Scrollable : class {
var scrollView: UIScrollView { get }
var keyboardRect: CGRect { get set }
}
extension Scrollable where Self : UIViewController {
func registerForKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillShowNotification, object: nil, queue: nil, usingBlock: { (notification) in
self.keyboardWillShow(notification)
})
NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillHideNotification, object: nil, queue: nil, usingBlock: { (notification) in
self.keyboardWillHide(notification)
})
}
func deregisterForKeyboardNotification() {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
self.keyboardRect = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
UIView.animateWithDuration(0.4) {
self.scrollView.contentSize.height = self.view.frame.size.height + self.keyboardRect.height / 2 + UITabBarController().tabBar.frame.size.height
}
}
func keyboardWillHide(notification: NSNotification) {
UIView.animateWithDuration(0.4) {
self.scrollView.contentSize.height = 0
}
}
}
Have in mind that I've used addObserverForName instead of addObserver because the later cannot be used with protocol extensions easily. More about that you can read here - Swift make protocol extension a Notification observer
I've been trying to set up my keyboard since a few days ago, but I can't manage to do it. I just want the imageView to move up when I am writing in the bottom text field. In fact, it's working, but it also moves up when I try to write at the topTextField. I don't want that, because when that happens, I can't see the textField at the top, and I can't see what I'm writing.
I'll include my screenshots and my code.
In this image, I pressed the topTextField to write something, but as you can see, the topTextField is lost. I mean the view moves up when I press the topTextField and I don't want that. What I want is so that when I press the topTextField the keyboard should appear but the view should be at the same place.
And in the last one I pressed the textFieldBottom, and as you can see, it works. The view moves up so I can see what I'm writing inside the textFieldBottom.
Here is my code:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.subscribeToKeyboardNotifications()
self.subscribeToKeyboardNotificationsDown()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.unsubscribeToKeyBoardNotifications()
self.unsubscribeToKeyBoardNotificationsDown()
}
func subscribeToKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardWillShow(_:)) , name: UIKeyboardWillShowNotification, object: nil)
}
func unsubscribeToKeyBoardNotifications() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
view.frame.origin.y -= getKeyboardHeight(notification)
}
func subscribeToKeyboardNotificationsDown() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func unsubscribeToKeyBoardNotificationsDown() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillHide(notification: NSNotification) {
view.frame.origin.y += getKeyboardHeight(notification)
}
func getKeyboardHeight(notification:NSNotification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
return keyboardSize.CGRectValue().height
}
// do one thing maintain one globle varible to save current textfield which is on editing mode based on that varible u put condition to move imageview
following example code will help you.
var currentTextField: UITextField
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
currentTextField = textField
return true
}
func keyboardWillShow(notification: NSNotification) {
if currentTextField == textFieldBottom {
view.frame.origin.y -= getKeyboardHeight(notification)
}
}
When topTextField becomes first responder, you're changing view.frame.origin, this is moving your topTextField up. Instead of changing view frame, you can set constraints for bottomTextField and you can change constraints for bottomTextField programmatically in keyboardWillShow method. This way your view frame will not change and topTextField will not move up.
Hi I am using this code to move my view when a textView is selected, this is to make sure my texView is visible for when the keyboard pops up.
override func viewDidLoad() {
super.viewDidLoad() NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: self.view.window)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: self.view.window)
}
func keyboardWillHide(sender: NSNotification) {
let userInfo: [NSObject : AnyObject] = sender.userInfo!
let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
self.view.frame.origin.y += keyboardSize.height
}
func keyboardWillShow(sender: NSNotification) {
let userInfo: [NSObject : AnyObject] = sender.userInfo!
let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
let offset: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue.size
if keyboardSize.height == offset.height {
if self.view.frame.origin.y == 0 {
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.view.frame.origin.y -= keyboardSize.height
})
}
} else {
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.view.frame.origin.y += keyboardSize.height - offset.height
})
}
print(self.view.frame.origin.y)
}
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: self.view.window)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: self.view.window)
}
How can i only move the view if the bottom textView is selected? Because currently if you select the uppermost textView it moves half of it off screen.
I appreciate any help, thanks in advance.
In the notification callbacks check for bottomTextView.isFirstResponder() and move the view only if its true. Otherwise don't move the view.
Make your class a UITextViewDelegate like so:
class ViewController: UIViewController, UITextViewDelegate {
Then in viewDidLoad set only the bottom textView to be controlled by the delegate:
bottomTextView.delegate = self
Then you can use these functions, changing the values to suit your needs:
func textViewDidBeginEditing(textView: UITextView) {
self.view.frame.origin.y -= 150
}
func textViewDidEndEditing(textView: UITextView) {
self.view.frame.origin.y += 150
}
Rather than only moving the keyboard for a single text field, I suggest a more flexible approach.
What I do is to keep track of which text field is being selected and do some calculations to move the view controller's content view up just enough to expose the text field that is becoming active.
To do that you need to set up your view controller to be the text fields' delegates.
I have a development blog post that explains this in detail:
Shifting views to make room for the keyboard
The code in that post is in Objective-C, but the concepts are identical. I'm not sure I have the same code in Swift that I can share (The only code I've found is in a project I did for a client.)
That blog post references a project called RandomBlobs on Github.
I would suggest using a delegate. For example, you can set you bottom textView's delegate to self (add UITextViewDelegate first of course) and implement the
textViewDidBeginEditing(_:) method. When that's triggered, you will know that the user started editing this particular view.
I was first having problems with textfields on the bottom of the screen because the keyboard would cover them. Now that I fixed that problem I have a new one. When I try to enter text in a textfield that is at the top of the screen, the screen rises and does not let me see what I'm typing.
I think what I would ideally like to do is change how much the keyboard pushes the screen up. Below is the code I used for the initial change.
I just started learning to develop two weeks ago so I'm still getting familiar with all the syntax and functions.
var kbHeight: CGFloat!
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated:Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
kbHeight = keyboardSize.height
self.animateTextField(true)
}
}
}
func keyboardWillHide(notification: NSNotification) {
self.animateTextField(false)
}
func animateTextField(up: Bool) {
var movement = (up ? -kbHeight : kbHeight)
UIView.animateWithDuration(0.3, animations: {
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
})
}
It's a suggestion
Instead of setting View's frame. put the view inside a UIScrollView.
then inside your keyboardWillShow and keyboardWillHide methods adjust the scrollview frame accordingly.
and set the scrollview's content offset using the "scrollRectToVisible" inside the the textfield delegate method.
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
CGRect rectText = textField.frame;
self.scrollVIew scrollRectToVisible:rectText animated:TRUE];
}