ios - Move a view up when the keyboard covers an input field but with leaving some space between them -


description

i have few uitextfield want scroll up, when 1 of them covered keyboard during edition. there tons of answers here on many flavors: moving view (by changing frame), modifying constraint, using uiscrollview , uitableview, or using uiscrollview , modifying contentinset.

i decided use last one. 1 described apple, , has swift version on so being described on blog including sample project on github.

partial code

override func viewdidload() {     super.viewdidload()     nsnotificationcenter.defaultcenter().addobserver(self, selector: "keyboardwillshow:", name: uikeyboardwillshownotification, object: nil)     nsnotificationcenter.defaultcenter().addobserver(self, selector: "keyboardwillbehidden:", name: uikeyboardwillhidenotification, object: nil) }  func textfieldshouldreturn(textfield: uitextfield) -> bool {     textfield.resignfirstresponder()     return true }  func textfielddidendediting(textfield: uitextfield) {     self.activefield = nil }  func textfielddidbeginediting(textfield: uitextfield) {     self.activefield = textfield }  func keyboardwillshow(notification: nsnotification) {     if let activefield = self.activefield, keyboardsize = (notification.userinfo?[uikeyboardframebeginuserinfokey] as? nsvalue)?.cgrectvalue() {         let contentinsets = uiedgeinsets(top: 0.0, left: 0.0, bottom: keyboardsize.height, right: 0.0)         self.scrollview.contentinset = contentinsets         self.scrollview.scrollindicatorinsets = contentinsets         var arect = self.view.frame         arect.size.height -= keyboardsize.size.height         if (!cgrectcontainspoint(arect, activefield.frame.origin)) {             self.scrollview.scrollrecttovisible(activefield.frame, animated: true)         }     } }  func keyboardwillbehidden(notification: nsnotification) {     let contentinsets = uiedgeinsetszero     self.scrollview.contentinset = contentinsets     self.scrollview.scrollindicatorinsets = contentinsets } 

issue

i want 1 simple modification - little more space between keyboard , edited field. because out of box looks this:

screenshot

i modified cgrect in scrollrecttovisible call, changed nothing. what's more, commenting out line scrollrecttovisible had no effect @ - worked before (including scrolling content). checked on ios 9.2

replicating, if needed, trivial - download working code using github link above , comment out scrollrecttovisible line.

tested workarounds

workarounds tried, didn't final effect:

  • increasing contentinset - user scroll more contentsize
  • replacing uikeyboarddidshownotification uikeyboardwillshownotification, add uikeyboarddidshownotification observer scrollrecttovisible inside - works, there 2 scroll animations, doesn't good.

questions

  • why changing contentinset (without scrollrecttovisible call) scrolls content in scrollview? i've not see in docs information such behavior
  • and more important - do, scroll little more, have space between edited text field , keyboard?

what did miss? there easy way fix it? or maybe it's better play scrollview.contentsize, instead of contentinset?

i haven't found why scrollrecttovisible doesn't work in scenario described above, found other solution works. in mentioned apple article managing keyboard @ bottom there hint

there other ways can scroll edited area in scroll view above obscuring keyboard. instead of altering bottom content inset of scroll view, can extend height of content view height of keyboard , scroll edited text object view.

solution below based on extending height of content view (scrollview.contentsize). takes account orientation change , scrolling when keyboard being hidden. works required - has space between active field , keyboard, see image below

screenshot

working code

i've placed full working code on github: scrollviewonkeyboardshow

var animatecontenetview = true var originalcontentoffset: cgpoint? var iskeyboardvisible = false  let offset : cgfloat = 18  override func viewdidload() {     super.viewdidload()      case let textfield uitextfield in contentview.subviews {         textfield.delegate = self     }      let notificationcenter = nsnotificationcenter.defaultcenter()     notificationcenter.addobserver(self, selector: #selector(viewcontroller.keyboardwillbeshown(_:)), name: uikeyboardwillshownotification, object: nil)     notificationcenter.addobserver(self, selector: #selector(viewcontroller.keyboardwillbehidden(_:)), name: uikeyboardwillhidenotification, object: nil) }   func textfieldshouldreturn(textfield: uitextfield) -> bool {     textfield.resignfirstresponder()     return true }   func textfielddidendediting(textfield: uitextfield) {     self.activefield = nil }   func textfielddidbeginediting(textfield: uitextfield) {     self.activefield = textfield }   func keyboardwillbeshown(notification: nsnotification) {     originalcontentoffset = scrollview.contentoffset      if let activefield = self.activefield, keyboardsize = (notification.userinfo?[uikeyboardframebeginuserinfokey] as? nsvalue)?.cgrectvalue() {         var visiblerect = self.scrollview.bounds         visiblerect.size.height -= keyboardsize.size.height          //that's avoid enlarging contentsize multiple times in case of many uitextfields,         //when user changes edited text field         if iskeyboardvisible == false {             scrollview.contentsize.height += keyboardsize.height         }          //scroll if keyboard cover bottom edge of         //active field (including given offset)         let activefieldbottomy = activefield.frame.origin.y + activefield.frame.size.height + offset         let activefieldbottompoint = cgpoint(x: activefield.frame.origin.x, y: activefieldbottomy)         if (!cgrectcontainspoint(visiblerect, activefieldbottompoint)) {             var scrolltopointy = activefieldbottomy - (self.scrollview.bounds.height - keyboardsize.size.height)             scrolltopointy = min(scrolltopointy, scrollview.contentsize.height - scrollview.frame.size.height)              scrollview.setcontentoffset(cgpoint(x: 0, y: scrolltopointy), animated: animatecontenetview)         }     }      iskeyboardvisible = true }   func keyboardwillbehidden(notification: nsnotification) {     scrollview.contentsize.height = contentview.frame.size.height     if var contentoffset = originalcontentoffset {         contentoffset.y = min(contentoffset.y, scrollview.contentsize.height - scrollview.frame.size.height)         scrollview.setcontentoffset(contentoffset, animated: animatecontenetview)     }      iskeyboardvisible = false }   override func viewwilltransitiontosize(size: cgsize, withtransitioncoordinator coordinator: uiviewcontrollertransitioncoordinator) {     coordinator.animatealongsidetransition(nil) { (_) -> void in         self.scrollview.contentsize.height = self.contentview.frame.height     } }   deinit {     let notificationcenter = nsnotificationcenter.defaultcenter()     notificationcenter.removeobserver(self, name: uikeyboardwillshownotification, object: nil)     notificationcenter.removeobserver(self, name: uikeyboardwillhidenotification, object: nil) } 

Comments