我创建了一个包含4个字段和一个按钮的表单。视图层次结构如下所示:主要UIVIew,View(重命名的contentView),在contentView之上我有4个字段和1个以编程方式创建的按钮。
在此尝试之前,我在ViewController的视图上按下按钮,将按钮的底部约束分配给变量,当keyboardDidShow时,我将键盘大小添加到底部约束,从而拍摄键盘上方的按钮。然而,stackoverflower说这种方法很容易出错:Move button when keyboard appears swift
我已经按照本教程,但我没有得到相同的结果。 https://spin.atomicobject.com/2014/03/05/uiscrollview-autolayout-ios/
鉴于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
开始输入textFields时
When keyboard is hidden
Desired result
答案 0 :(得分:3)
我认为如果它们被键盘覆盖,你应该只将字段向上移动,就像我几天前做的那样:
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)
}
}
}
基本上你只需要为键盘处理时间添加逻辑,如果键盘框架超过文本框架,你应该处理它。 希望它有所帮助。
答案 1 :(得分:3)
所以我认为你很接近,我不确定这是代码问题还是自动布局问题。我的猜测是你得到关于你的滚动视图的投诉不知道内容大小所以我将涵盖两者。
编辑: 此外,您的按钮必须位于我首次添加的视图容器下方,并且需要与scrollview分开处理。不要把它放在滚动视图中。
这些方法在下面工作,除了为布局底部添加50或其他任何内容以保存滚动视图。此外,下面的方法将有助于编辑
自动布局: 首先,对于仅在页面上占用的表单,我首先要在故事板上添加一个视图,然后在顶部布局指南中添加(按钮所需的任何空间),左右。然后我将ScrollView(将scrollview固定到该视图)添加到刚刚添加的视图中。接下来,我将内容视图添加到scrollview。现在我把它固定到scrollview上。您会看到autolayout仍然不满意。那么为什么第一个视图以及如何解决这个问题。我从contentView拖动到持有scrollview的视图,并选择相等的高度和相等的宽度。现在你不会有自动布局尖叫着你。注意:这适用于要填充第一个视图大小但允许其滚动以避开键盘的内容。见图片
添加相同的高度后,我可以继续使用故事板。我设置了文本字段。您可能想要也可能不想将它固定在底部的底部文本字段,但如果您确实将其设为&gt; = yourNumber。
编辑: 现在将您的NEXT按钮添加到视图下方的故事板中。按钮必须固定在主视图的底部,并用0固定在键盘上。它现在看起来像这样。
显然,这与初始图像略有冲突,但您所要做的就是增加底部布局指南的空间,只需确保将按钮添加到主视图而不是保存滚动视图的视图。现在将您的按钮连接到iboutlet中的控制器。我们需要它。
最后的代码。其中一些你需要替换你的文本字段变量,因为我循环通过子视图来设置委托。我还在向上滚动中添加了填充。您应该将deRegister移动到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
现在又迈出了一步。我们必须处理按钮的上移。而不是仅仅杀死这个控制器并在其中放入更多处理,你可以继承bottomconstraint来处理它。 (只需确保不要在底部添加顶部约束。)这是一个要放下项目的约束。
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
}
}
将其添加到项目中的文件中。然后你要做的就是在故事板上按钮的底部约束将它改为这个子类。只要确保没有顶级约束。持有滚动视图的视图需要对主视图具有底部约束,而不是具有足够空间的按钮的按钮。运行项目,享受。如果此解释不充分,请参阅测试项目的链接。 https://www.dropbox.com/s/ir5x324mvhhne64/ScrollView.zip?dl=0