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:
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
, adduikeyboarddidshownotification
observerscrollrecttovisible
inside - works, there 2 scroll animations, doesn't good.
questions
- why changing
contentinset
(withoutscrollrecttovisible
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
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
Post a Comment