Can you set contentInset more than once? - ios

I have a tableview with table cells. Each cell has a textfield. I have the following code to prevent the bottom few cells from being blocked by the keyboard
#objc func keyboardWillShow(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
}
}
#objc func keyboardWillHide(notification: NSNotification) {
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(TransactionViewController.keyboardWillHide), name: UIApplication.keyboardWillChangeFrameNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(TransactionViewController.keyboardWillShow), name: UIApplication.keyboardWillChangeFrameNotification, object: nil)
}
The keyboardWillHide function works as expected. However, when the keyboard is hidden, the table does bounce back down the the bottom, resulting in no extra whitespace being shown (which is what I want). What I don't like is how you can still scroll down on the table to the contentInset from where the keyboard was first shown. Is there a way to make it so that after the keyboard disappears, that you can't scroll down passed the bottom of the table?

Replace
keyboardFrameBeginUserInfoKey
with
keyboardFrameEndUserInfoKey
#objc func keyboardWillShow(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)
}
}
#objc func keyboardWillHide(notification: NSNotification) {
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(TransactionViewController.keyboardWillHide), name: UIApplication.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(TransactionViewController.keyboardWillShow), name: UIApplication.keyboardWillShowNotification, object: nil)
}

Related

Label is attached to the keyboard

How to add a block with the send button so that it moves with the keyboard. And also that would be textview behaved the same.
preview
Add this code in your controller
func createInputAccessoryView () -> UIToolbar {
let toolbarAccessoryView = UIToolbar(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 44))
toolbarAccessoryView.barStyle = .default
toolbarAccessoryView.tintColor = UIColor.blue
let flexSpace = UIBarButtonItem(barButtonSystemItem:.flexibleSpace, target:nil, action:nil)
let doneButton = UIBarButtonItem(barButtonSystemItem:.done, target:self, action:Selector(("doneTouched")))
toolbarAccessoryView.setItems([flexSpace, doneButton], animated: false)
return toolbarAccessoryView
}
#objc func doneTouched() {
/* Your action goes here */
}
Now add this in your viewDidLoad or anywhere
yourTextView.inputAccessoryView = createInputAccessoryView ()
Design the block view and then attach it's bottom constraint as IBOutlet and do this
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.viewBotCon.constant = -1 * keyboardSize.height
self.view.layoutIfNeeded()
}
}
#objc func keyboardWillHide(notification: NSNotification) {
self.viewBotCon.constant = 0
self.view.layoutIfNeeded()
}

Container View Wont Raise

I have some code that is supposed to raise a container view when the keyboard comes in to enter some text. I feel like I have definitely
implemented this right but the text view is not raising.
var bottomConstraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Comments"
collectionView?.backgroundColor = UIColor.white
self.navigationItem.hidesBackButton = true
let backButton = UIBarButtonItem(image: UIImage(named: "icons8-Back-64"), style: .plain, target: self, action: #selector(GoBack))
self.navigationItem.leftBarButtonItem = backButton
self.collectionView?.register(CommentCell.self, forCellWithReuseIdentifier: cellID)
collectionView?.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom:-50, right: 0)
collectionView?.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: -50, right: 0)
collectionView?.alwaysBounceVertical = true
collectionView?.keyboardDismissMode = .interactive
setupKeyboardObserver()
view.addSubview(containerView)
view.addConstraintsWithFormat("H:|[v0]|", views: containerView)
view.addConstraintsWithFormat("V:[v0(48)]", views: containerView)
Here im setting the constant to 0
let bottomConstraint = NSLayoutConstraint(item: containerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
view.addConstraint(bottomConstraint)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
// Register cell classes
// self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellID)
fetchComments()
}
Here im taking control of constant and making it react to the keyboard height
func handleKeyboardNotification(notification: NSNotification){
if let userinfo = notification.userInfo{
let keyboardFrame = (userinfo[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue
bottomConstraint?.constant = -(keyboardFrame?.height)!
let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
bottomConstraint?.constant = isKeyboardShowing ? CGFloat(-(keyboardFrame?.height)!) : CGFloat(0)
}
}
Despite all this, the keyboard still covers the container view. WHen I change the constant manually it moves but these functions seem to have no effect at dynamically moving the view. I'm confused and any help will be rewarded with a shot at the WWE championship belt. No but seriously I would appreciate the help
What I am able to observe from your code is you are managing the scroll top to down and vice versa with single method you just need to do that with individual method one is while keyboard is appear and another one is while the keyboard is dismiss
Here is the sample snippet which may help you
add the different selector for each notification
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Handle the keyboard appear and disappear event with following method
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y -= keyboardSize.height
})
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y += keyboardSize.height
})
}
}
}
Note:
Do not forgot to remove Observer while your view is going to disappear like
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Your handleKeyboardNotification function is setting the constant back to 0 in the last line of the function.
Also, your constant shouldn't be negative keyboard value, it should be the height of the keyboard plus a margin if you need it.
Fixed it by changing bottomConstraint from let to var. Forgot let makes it immutable. Haha guess I'm the WWE champion

iOS - Set CollectionView Header with dynamic height

I have a collection view with a Header and no cells.
I've set the header size to be equal to the superview bounds as following:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
This header has a UITextView in it, which is anchored, as well, to the headers bounds (topAnchor, leftAnchor, bottomAnchor and rightAnchor).
I need the header to be anchored to the collection view height when the keyboard is closed, but anchored to the top of the keyboard when this one is opened, changing its size respectively.
The problem here lies when the text of the headers UITextView subview keeps growing and the text goes behind the keyboard.
Here's a GIF showing what's happening.
The yellow view is the Header
The white view is the TextView with some padding.
When I pass the keyboard the text goes behind it - if I could anchor the header to the bottom of the screen when there's no keyboard and to the bottom of the keyboard when its shown would be great.
Any hint?
Thanks
EDIT ADICIONAL INFO
As #Aravind said I've implemented the Notification listener to fire whenever the keyboard shows or hides.
Since I'm using the UITextView inside the Header class as its subview I've implemented the following:
This is an helper function to anchor my views easily:
extension UIView {
func anchor(top: NSLayoutYAxisAnchor?, left: NSLayoutXAxisAnchor?, bottom: NSLayoutYAxisAnchor?, right: NSLayoutXAxisAnchor?, paddingTop: CGFloat, paddinfLeft: CGFloat, paddingBottom: CGFloat, paddingRight: CGFloat, width: CGFloat, height: CGFloat) {
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
}
if let left = left {
leftAnchor.constraint(equalTo: left, constant: paddinfLeft).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
}
if let right = right {
rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
}
if width != 0 {
widthAnchor.constraint(equalToConstant: width).isActive = true
}
if height != 0 {
heightAnchor.constraint(equalToConstant: height).isActive = true
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(inputTextView)
inputTextView.delegate = self
inputTextView.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddinfLeft: 12, paddingBottom: 0, paddingRight: 12, width: 0, height: 0)
setupInputTextViewToolbar()
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillShow,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)
}
Then the Show/Hide functions
func keyboardWillShow(_ sender:NSNotification){
guard let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue else { return }
inputTextView.anchor(top: topAnchor, left: leftAnchor, bottom: bottomAnchor, right: rightAnchor, paddingTop: 0, paddinfLeft: 12, paddingBottom: keyboardSize.height, paddingRight: 12, width: 0, height: 0)
}
func keyboardWillHide(_ sender:NSNotification){
self.inputTextView.anchor(top: self.topAnchor, left: self.leftAnchor, bottom: self.bottomAnchor, right: self.rightAnchor, paddingTop: 0, paddinfLeft: 12, paddingBottom: 0, paddingRight: 12, width: 0, height: 0)
}
The show function works properly and makes the text view to the remaining screen size so it doesn't stay behind the keyboard, so far so good.
But when I fire the hide function it doesn't resize again to the bottom of the screen.
What maybe causing this?
Here make an outlet of the textView's bottom constraint and change its value when keyboard shows up and when it hides like,
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillShow,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil)
}
func keyboardWillShow(_ sender:NSNotification){
let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
bottomSpace.constant = (keyboardSize?.height)!
}
func keyboardWillHide(_ sender:NSNotification){
bottomSpace.constant = 0
}

How to I add preview text on top of keyboard layout in iOS

I want to show preview text on top of keyboard like this:
How do I solve with this problem?
I think this is what you want :
Code :
override func viewDidAppear(_ animated: Bool) {
let viewAcc = UIView()
viewAcc.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 50)
viewAcc.backgroundColor = UIColor.gray
let newTF = UITextField(frame: CGRect(x: 2, y: 10, width: self.view.frame.size.width - 75 , height: 30))
newTF.backgroundColor = UIColor.white
newTF.borderStyle = UITextBorderStyle.none
let btnDone = UIButton(frame: CGRect(x: newTF.frame.size.width + 10, y: 5, width: 45, height: 30 ))
btnDone.backgroundColor = UIColor.blue
btnDone.setTitle("Done", for: .normal)
viewAcc.addSubview(newTF)
viewAcc.addSubview(btnDone)
self.mytextField.inputAccessoryView = viewAcc
}
Reference:
Apple Doc Reference
#dahiya_boy's method is much better i guess. but if you need alternative way...
you can observe on this notifications and show or hide a label that show a preview of text.
// this one trigger keyboardWillShow when keyboard is shown to user
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
// this one trigger keyboardWillHide when keyboard is dismissing
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)
and in function keyboardWillShow you can display a label for preview on top of the keyboard.
func keyboardWillShow(sender: NSNotification) {
if let userInfo = sender.userInfo {
if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue().size.height {
// keyboard height is available you can use it to put image in view
}
}
}
func keyboardWillHide(sender: NSNotification) {
// you can hide the label here
}

Moving UIButton when keyboard pops up

I have below class. I am basically adding NSNotification to check whether keyboard is up. If keyboard is up, I change the frame of button to locate it on top of the keyboard. I think I did this in a correct manner, but the button apparently does not move. What could be the problem?
class vc: UIViewController {
var previousButton: UIButton!
var nextButton: UIButton!
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
previousButton = UIButton(frame: CGRectMake(margin + 20, containerView.frame.size.height + 10, 80, 30))
previousButton.setImage(UIImage(named: "previous"), forState: .Normal)
previousButton.addTarget(self, action: "previousButtonPressed2:", forControlEvents: .TouchUpInside)
nextButton = UIButton(frame: CGRectMake(self.view.frame.width - margin - 80 - 20, containerView.frame.size.height + 10 , 80, 30))
nextButton.setImage(UIImage(named: "next"), forState: .Normal)
nextButton.addTarget(self, action: "nextButtonPressed2:", forControlEvents: .TouchUpInside)
self.view.addSubview(previousButton)
self.view.addSubview(nextButton)
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
previousButton = UIButton(frame: CGRectMake(10 + 20, self.view.frame.size.height - keyboardSize.height - 40, 80, 30))
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
previousButton = UIButton(frame: CGRectMake(10 + 20, self.view.frame.size.height - 40, 80, 30))
}
}
}
You should check for UIKeyboardFrameEndUserInfoKey key in the keyboardWillShow: method instead of UIKeyboardFrameBeginUserInfoKey. The same goes for keyboardWillHide: method:
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
previousButton = UIButton(frame: CGRectMake(10 + 20, self.view.frame.size.height - keyboardSize.height - 40, 80, 30))
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
previousButton = UIButton(frame: CGRectMake(10 + 20, self.view.frame.size.height - 40, 80, 30))
}
}
UIKeyboardFrameBeginUserInfoKey allows you to get the frame of the keyboard from before the keyboard animation starts when you really need a frame of the keyboard when keyboard is fully visible in the screen. This value can be retrieved using UIKeyboardFrameBeginUserInfoKey key.

Resources