Xcode 当键盘向上移动时,如何使文本字段保持原位?敏捷的
我创建了一个包含4个字段和一个按钮的表单。视图层次结构如下所示:主UIVIew、视图(重命名为contentView),在contentView之上,我有4个字段和1个编程创建的按钮Xcode 当键盘向上移动时,如何使文本字段保持原位?敏捷的,xcode,uistoryboard,Xcode,Uistoryboard,我创建了一个包含4个字段和一个按钮的表单。视图层次结构如下所示:主UIVIew、视图(重命名为contentView),在contentView之上,我有4个字段和1个编程创建的按钮 触发viewDidLoad时,按钮不会向上滚动,以便在contentView中可见 开始键入文本字段时,文本字段会在可视区域之外向上滚动 当firstResponder辞职(键盘隐藏)时,我无法滚动contentView。 我将按照上面指定的顺序列出图片截图 在此尝试之前,我将按钮固定在ViewController
鉴于Iphone有不同的屏幕尺寸,请建议最佳方法
class EleventhViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var fullName: UITextField!
@IBOutlet weak var flatNumber: UITextField!
@IBOutlet weak var streetAddress: UITextField!
@IBOutlet weak var phoneNumber: UITextField!
@IBOutlet weak var contentView: UIView!
@IBOutlet weak var scrollView: UIScrollView!
var nextButtonOutlet:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
//called whenever keyboard is shown/hidden
registerForKeyboardNotifications()
//when identifies single or multiple taps, call DismissKeyboard
var tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "DismissKeyboard")
contentView.addGestureRecognizer(tap)
//create button programmatically
var button = UIButton(type: UIButtonType.custom) as UIButton
button = UIButton(frame: CGRect(x: 0, y: 637, width: 375, height: 50))
button.titleLabel?.textColor = UIColor.white
button.backgroundColor = UIColor(colorLiteralRed: 117/255, green: 232/255, blue: 0, alpha: 1)
button.setTitle("Next", for: .normal)
button.addTarget(self, action: #selector(EleventhViewController.nextButton), for: .touchUpInside)
self.contentView.addSubview(button)
self.nextButtonOutlet = button
//disable scroll bouncing
scrollView.bounces = false
self.fullName.delegate = self
self.flatNumber.delegate = self
self.streetAddress.delegate = self
self.phoneNumber.delegate = self
}
//Call this function when the tap is recognized.
func DismissKeyboard(){
contentView.endEditing(true)
}
// Stop Editing on Return Key Tap.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
weak var activeField: UITextField?
func keyboardDidShow(_ notification: Notification) {
//when a textfield is edited lift the button above the keyboard
if let activeField = self.activeField,let 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
var aRect = self.view.frame
aRect.size.height -= keyboardSize.size.height
if !aRect.contains(nextButtonOutlet.frame.origin) {
self.scrollView.scrollRectToVisible(nextButtonOutlet.frame, animated: true)
}
}
func keyboardWillHide(_ notification: Notification) {
let contentInsets = UIEdgeInsets.zero
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
//Keep track of which textfield is being edited to make sure the field is visible when keyboard pops up
func textFieldDidBeginEditing(_ textField: UITextField) {
self.activeField = textField
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
self.activeField = nil
}
//register for keyboard notifications
func registerForKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector:
#selector(keyboardDidShow),
name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector:
#selector(keyboardWillHide), name:
NSNotification.Name.UIKeyboardWillHide, object: nil)
}
//remove keyBoard observers
func deregisterFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name:
NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name:
NSNotification.Name.UIKeyboardWillHide, object: nil)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(true)
//deregister keyboard notifications
deregisterFromKeyboardNotifications()
}
} //end of class
view Hierachy
触发viewDidLoad时,按钮不显示
开始键入文本字段时
我认为只有当字段被键盘覆盖时,才应该向上移动字段,就像我几天前做的那样:
let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue ?? NSValue()).cgRectValue
let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
UIView.animate(withDuration: 0.2) {
if notification.name == Notification.Name.UIKeyboardWillHide {
self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
} else {
let offset = (self.view.frame.size.height - self.activeField.frame.maxY) - keyboardViewEndFrame.height
if offset < 0 {
self.view.frame = CGRect(x: 0, y: offset, width: self.view.width, height: self.view.height)
} else {
self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
}
}
}
将keyboardScreenEndFrame=(userInfo[UIKeyboardFrameEndUserInfoKey]设为?NSValue??NSValue()).cgRectValue
让keyboardViewEndFrame=view.convert(keyboardScreenEndFrame,from:view.window)
UIView.animate(持续时间:0.2){
如果notification.name==notification.name.ui键盘将隐藏{
self.view.frame=CGRect(x:0,y:0,width:self.view.width,height:self.view.height)
}否则{
让偏移量=(self.view.frame.size.height-self.activeField.frame.maxY)-keyboardViewEndFrame.height
如果偏移量<0{
self.view.frame=CGRect(x:0,y:offset,width:self.view.width,height:self.view.height)
}否则{
self.view.frame=CGRect(x:0,y:0,width:self.view.width,height:self.view.height)
}
}
}
基本上,您只需要为键盘处理计时添加逻辑,若键盘帧超过文本字段帧,您应该处理它。
希望能有所帮助。我认为只有当字段被键盘覆盖时,才应该将其向上移动,就像我几天前做的那样:
let keyboardScreenEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue ?? NSValue()).cgRectValue
let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window)
UIView.animate(withDuration: 0.2) {
if notification.name == Notification.Name.UIKeyboardWillHide {
self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
} else {
let offset = (self.view.frame.size.height - self.activeField.frame.maxY) - keyboardViewEndFrame.height
if offset < 0 {
self.view.frame = CGRect(x: 0, y: offset, width: self.view.width, height: self.view.height)
} else {
self.view.frame = CGRect(x: 0, y: 0, width: self.view.width, height: self.view.height)
}
}
}
将keyboardScreenEndFrame=(userInfo[UIKeyboardFrameEndUserInfoKey]设为?NSValue??NSValue()).cgRectValue
让keyboardViewEndFrame=view.convert(keyboardScreenEndFrame,from:view.window)
UIView.animate(持续时间:0.2){
如果notification.name==notification.name.ui键盘将隐藏{
self.view.frame=CGRect(x:0,y:0,width:self.view.width,height:self.view.height)
}否则{
让偏移量=(self.view.frame.size.height-self.activeField.frame.maxY)-keyboardViewEndFrame.height
如果偏移量<0{
self.view.frame=CGRect(x:0,y:offset,width:self.view.width,height:self.view.height)
}否则{
self.view.frame=CGRect(x:0,y:0,width:self.view.width,height:self.view.height)
}
}
}
基本上,您只需要为键盘处理计时添加逻辑,若键盘帧超过文本字段帧,您应该处理它。
希望能有所帮助。所以我想你已经接近了,我不确定这是代码问题还是自动布局问题。我猜你正在收到关于你的scrollview的投诉,因为你不知道内容大小,所以我将介绍这两个方面 编辑: 另外,您的按钮必须位于我首先添加的视图容器下方,并且需要与scrollview分开处理。不要将其放在滚动视图中 下面的方法适用于保存scrollview的视图,除了在布局底部添加50或其他方法。下面的方法也有助于进行编辑 自动布局: 首先,对于只在页面上显示的表单,我喜欢先在故事板上添加一个视图,然后在顶部布局指南中固定(按钮所需的任何空间),左右。然后,我将我的ScrollView(将ScrollView固定到该视图)添加到刚才添加的视图中。然后,我将我的内容视图添加到ScrollView中。现在我将其固定到scrollview。您将看到autolayout仍然不开心。那么为什么是第一个视图,以及如何解决这个问题。我从contentView拖动到包含scrollview的视图,并选择等高等宽。现在,你将不会有自动布局尖叫你。注意:这适用于要填充第一个视图大小的内容,但允许其滚动以避免键盘。查看图像 在添加这个相等的高度后,我可以继续使用故事板。我设置了文本字段。底部文本字段您可能希望也可能不希望将其固定在底部,但如果您确实使其>=您的号码 编辑: 现在将“下一步”按钮添加到包含所有内容的视图下方的故事板。该按钮必须固定在主视图的底部,并带有0以粘贴到键盘上。现在看起来是这样的 显然,这与初始图像有点冲突,但您所要做的只是增加底部布局指南的空间,只需确保您的按钮添加到主视图,而不是包含滚动视图的视图。现在将按钮连接到iboutlet中的控制器。我们需要它 接下来,确保模拟器中有正确的键盘**不使用硬件键盘 最后是代码。当我在子视图中循环设置委托时,您需要替换textfield变量。我还为向上滚动添加了填充。您应该将注销者移动到deint()。查看我的代码,最后您可能希望滚动键盘上的滚动视图将显示,而不是确实显示,但我没有改变这一点
import UIKit
class ViewController: UIViewController,UITextFieldDelegate {
//added in storyboard. removed the code
@IBOutlet weak var nextButton: UIButton!
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
//called whenever keyboard is shown/hidden
registerForKeyboardNotifications()
//when identifies single or multiple taps, call DismissKeyboard
var tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "DismissKeyboard")
contentView.addGestureRecognizer(tap)
//disable scroll bouncing
scrollView.bounces = false
//replace with your textfields
for subs in self.contentView.subviews{
if subs is UITextField{
print("setting")
(subs as! UITextField).delegate = self
}
}
}
//Call this function when the tap is recognized.
func DismissKeyboard(){
contentView.endEditing(true)
}
// Stop Editing on Return Key Tap.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
//edited for next button
weak var activeField: UITextField?
func keyboardDidShow(_ notification: Notification) {
//when a textfield is edited lift the button above the keyboard
if let activeField = self.activeField,let keyboardSize =
(notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as?
NSValue)?.cgRectValue {
//20 in insets and offset is just padding
let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom:
keyboardSize.height + 20 + nextButton.bounds.height, right: 0.0)
self.scrollView.contentInset = contentInsets
var aRect = self.view.frame
aRect.size.height -= keyboardSize.height
let bottomPoint = CGPoint(x: activeField.frame.origin.x, y:activeField.frame.origin.y)
if aRect.contains(bottomPoint){
let scrollPoint = CGPoint(x: 0.0, y: bottomPoint.y - keyboardSize.height - 20 - nextButton.bounds.height)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
}
func keyboardWillHide(_ notification: Notification) {
let contentInsets = UIEdgeInsets.zero
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
//Keep track of which textfield is being edited to make sure the field is visible when keyboard pops up
func textFieldDidBeginEditing(_ textField: UITextField) {
self.activeField = textField
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
self.activeField = nil
}
//register for keyboard notifications
func registerForKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector:
#selector(keyboardDidShow),
name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector:
#selector(keyboardWillHide), name:
NSNotification.Name.UIKeyboardWillHide, object: nil)
}
//remove keyBoard observers
func deregisterFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name:
NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name:
NSNotification.Name.UIKeyboardWillHide, object: nil)
}
deinit {
//deregister keyboard notifications
deregisterFromKeyboardNotifications()
}
} //end of class
现在一个
import UIKit
class ViewController: UIViewController,UITextFieldDelegate {
//added in storyboard. removed the code
@IBOutlet weak var nextButton: UIButton!
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var contentView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
//called whenever keyboard is shown/hidden
registerForKeyboardNotifications()
//when identifies single or multiple taps, call DismissKeyboard
var tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "DismissKeyboard")
contentView.addGestureRecognizer(tap)
//disable scroll bouncing
scrollView.bounces = false
//replace with your textfields
for subs in self.contentView.subviews{
if subs is UITextField{
print("setting")
(subs as! UITextField).delegate = self
}
}
}
//Call this function when the tap is recognized.
func DismissKeyboard(){
contentView.endEditing(true)
}
// Stop Editing on Return Key Tap.
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
//edited for next button
weak var activeField: UITextField?
func keyboardDidShow(_ notification: Notification) {
//when a textfield is edited lift the button above the keyboard
if let activeField = self.activeField,let keyboardSize =
(notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as?
NSValue)?.cgRectValue {
//20 in insets and offset is just padding
let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom:
keyboardSize.height + 20 + nextButton.bounds.height, right: 0.0)
self.scrollView.contentInset = contentInsets
var aRect = self.view.frame
aRect.size.height -= keyboardSize.height
let bottomPoint = CGPoint(x: activeField.frame.origin.x, y:activeField.frame.origin.y)
if aRect.contains(bottomPoint){
let scrollPoint = CGPoint(x: 0.0, y: bottomPoint.y - keyboardSize.height - 20 - nextButton.bounds.height)
scrollView.setContentOffset(scrollPoint, animated: true)
}
}
}
func keyboardWillHide(_ notification: Notification) {
let contentInsets = UIEdgeInsets.zero
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
}
//Keep track of which textfield is being edited to make sure the field is visible when keyboard pops up
func textFieldDidBeginEditing(_ textField: UITextField) {
self.activeField = textField
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
self.activeField = nil
}
//register for keyboard notifications
func registerForKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector:
#selector(keyboardDidShow),
name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector:
#selector(keyboardWillHide), name:
NSNotification.Name.UIKeyboardWillHide, object: nil)
}
//remove keyBoard observers
func deregisterFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name:
NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name:
NSNotification.Name.UIKeyboardWillHide, object: nil)
}
deinit {
//deregister keyboard notifications
deregisterFromKeyboardNotifications()
}
} //end of class
import UIKit
class AvoidingConstraint: NSLayoutConstraint {
private var offset : CGFloat = 0
private var keyboardVisibleHeight : CGFloat = 0
override public func awakeFromNib() {
super.awakeFromNib()
offset = constant
NotificationCenter.default.addObserver(self, selector: #selector(AvoidingConstraint.keyboardWillShowNotification(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(AvoidingConstraint.keyboardWillHideNotification(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: Notification
func keyboardWillShowNotification(_ notification: Notification) {
if let userInfo = notification.userInfo {
if let frameValue = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let frame = frameValue.cgRectValue
keyboardVisibleHeight = frame.size.height
}
self.updateConstant()
switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) {
case let (.some(duration), .some(curve)):
let options = UIViewAnimationOptions(rawValue: curve.uintValue)
UIView.animate(
withDuration: TimeInterval(duration.doubleValue),
delay: 0,
options: options,
animations: {
UIApplication.shared.keyWindow?.layoutIfNeeded()
return
}, completion: { finished in
})
default:
break
}
}
}
func keyboardWillHideNotification(_ notification: NSNotification) {
keyboardVisibleHeight = 0
self.updateConstant()
if let userInfo = notification.userInfo {
switch (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber, userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber) {
case let (.some(duration), .some(curve)):
let options = UIViewAnimationOptions(rawValue: curve.uintValue)
UIView.animate(
withDuration: TimeInterval(duration.doubleValue),
delay: 0,
options: options,
animations: {
UIApplication.shared.keyWindow?.layoutIfNeeded()
return
}, completion: { finished in
})
default:
break
}
}
}
func updateConstant() {
self.constant = offset + keyboardVisibleHeight
}
}