ios 仅当键盘覆盖输入字段时向上移动视图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28813339/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Move a view up only when the keyboard covers an input field
提问by John Allijn
I am trying to build an input screen for the iPhone. The screen has a number of input fields. Most of them on the top of the screen, but two fields are at the bottom. When the user tries to edit the text on the bottom of the screen, the keyboard will pop up and it will cover the screen. I found a simple solution to move the screen up when this happens, but the result is that the screen alwaysmoves up and the fields on top of the screen move out of reach when the user tries to edit those.
我正在尝试为 iPhone 构建一个输入屏幕。屏幕有许多输入字段。其中大部分位于屏幕顶部,但有两个字段位于底部。当用户尝试编辑屏幕底部的文本时,键盘会弹出并覆盖屏幕。我找到了一个简单的解决方案来在发生这种情况时向上移动屏幕,但结果是屏幕总是向上移动,并且当用户尝试编辑这些字段时,屏幕顶部的字段会移动到无法触及的范围内。
Is there a way to have the screen onlymove when the bottom fields are edited?
有没有办法让屏幕只在编辑底部字段时移动?
I have used this code I found here:
我使用了我在这里找到的这段代码:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(sender: NSNotification) {
self.view.frame.origin.y -= 150
}
func keyboardWillHide(sender: NSNotification) {
self.view.frame.origin.y += 150
}
回答by Nerkyator
Your problem is well explained in this document by Apple. Example code on this page (at Listing 4-1
) does exactly what you need, it will scroll your view only when the current editing should be under the keyboard. You only need to put your needed controls in a scrollViiew.
The only problem is that this is Objective-C and I think you need it in Swift..so..here it is:
您的问题在Apple 的这份文档中得到了很好的解释。此页面上的示例代码 (at Listing 4-1
) 正是您所需要的,只有当当前编辑应该在键盘下时,它才会滚动您的视图。您只需要将您需要的控件放在 scrollView 中。唯一的问题是这是 Objective-C,我认为你在 Swift 中需要它......所以......这里是:
Declare a variable
声明一个变量
var activeField: UITextField?
then add these methods
然后添加这些方法
func registerForKeyboardNotifications()
{
//Adding notifies on keyboard appearing
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
}
func deregisterFromKeyboardNotifications()
{
//Removing notifies on keyboard appearing
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWasShown(notification: NSNotification)
{
//Need to calculate keyboard exact size due to Apple suggestions
self.scrollView.scrollEnabled = true
var info : NSDictionary = notification.userInfo!
var keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
var contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeFieldPresent = activeField
{
if (!CGRectContainsPoint(aRect, activeField!.frame.origin))
{
self.scrollView.scrollRectToVisible(activeField!.frame, animated: true)
}
}
}
func keyboardWillBeHidden(notification: NSNotification)
{
//Once keyboard disappears, restore original positions
var info : NSDictionary = notification.userInfo!
var keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
var contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
self.scrollView.scrollEnabled = false
}
func textFieldDidBeginEditing(textField: UITextField!)
{
activeField = textField
}
func textFieldDidEndEditing(textField: UITextField!)
{
activeField = nil
}
Be sure to declare your ViewController as UITextFieldDelegate
and set correct delegates in your initialization methods:
ex:
请务必将您的 ViewController 声明为UITextFieldDelegate
并在您的初始化方法中设置正确的委托:例如:
self.you_text_field.delegate = self
And remember to call registerForKeyboardNotifications
on viewInit and deregisterFromKeyboardNotifications
on exit.
记得registerForKeyboardNotifications
在 viewInit 和deregisterFromKeyboardNotifications
退出时调用。
Edit/Update: Swift 4.2 Syntax
编辑/更新:Swift 4.2 语法
func registerForKeyboardNotifications(){
//Adding notifies on keyboard appearing
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIResponder.keyboardWillHideNotification, object: nil)
}
func deregisterFromKeyboardNotifications(){
//Removing notifies on keyboard appearing
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIResponder.keyboardWillHideNotification, object: nil)
}
@objc func keyboardWasShown(notification: NSNotification){
//Need to calculate keyboard exact size due to Apple suggestions
self.scrollView.isScrollEnabled = true
var info = notification.userInfo!
let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize!.height, right: 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
var aRect : CGRect = self.view.frame
aRect.size.height -= keyboardSize!.height
if let activeField = self.activeField {
if (!aRect.contains(activeField.frame.origin)){
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
@objc func keyboardWillBeHidden(notification: NSNotification){
//Once keyboard disappears, restore original positions
var info = notification.userInfo!
let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: -keyboardSize!.height, right: 0.0)
self.scrollView.contentInset = contentInsets
self.scrollView.scrollIndicatorInsets = contentInsets
self.view.endEditing(true)
self.scrollView.isScrollEnabled = false
}
func textFieldDidBeginEditing(_ textField: UITextField){
activeField = textField
}
func textFieldDidEndEditing(_ textField: UITextField){
activeField = nil
}
回答by Mr H
Here is My 2 cents:
这是我的 2 美分:
Have you tried: https://github.com/hackiftekhar/IQKeyboardManager
您是否尝试过:https: //github.com/hackiftekhar/IQKeyboardManager
Extremely easy to install Swift or Objective-C.
非常容易安装 Swift 或 Objective-C。
Here how it works:
这是它的工作原理:
IQKeyboardManager (Swift):- IQKeyboardManagerSwift is available through CocoaPods, to install it simply add the following line to your Podfile: (#236)
IQKeyboardManager (Swift):- IQKeyboardManagerSwift 可通过 CocoaPods 获得,只需将以下行添加到您的 Podfile 中即可安装它:(#236)
pod 'IQKeyboardManagerSwift'
In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager.
在 AppDelegate.swift 中,只需导入 IQKeyboardManagerSwift 框架并启用 IQKeyboardManager。
import IQKeyboardManagerSwift
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
IQKeyboardManager.sharedManager().enable = true
// For Swift 4, use this instead
// IQKeyboardManager.shared.enable = true
return true
}
}
And that is all. Easy!
这就是全部。简单!
回答by Edward
The one I found to work perfectly for me was this:
我发现最适合我的是:
func textFieldDidBeginEditing(textField: UITextField) {
if textField == email || textField == password {
animateViewMoving(true, moveValue: 100)
}
}
func textFieldDidEndEditing(textField: UITextField) {
if textField == email || textField == password {
animateViewMoving(false, moveValue: 100)
}
}
func animateViewMoving (up:Bool, moveValue :CGFloat){
let movementDuration:NSTimeInterval = 0.3
let movement:CGFloat = ( up ? -moveValue : moveValue)
UIView.beginAnimations("animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration)
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
UIView.commitAnimations()
}
You can also change the height values. Remove the "if statement" if you want to use it for all text fields.
您还可以更改高度值。如果要将“if 语句”用于所有文本字段,请删除它。
You can even use this for all controls that require user input like TextView.
您甚至可以将它用于所有需要用户输入的控件,例如 TextView。
回答by villejacob
Is there a way to have the screen onlymove when the bottom fieldsare edited?
有没有办法让屏幕只在编辑底部字段时移动?
I had a similar problem and found a pretty straightforward solution withoutusing a scrollView, and instead using if statements within the keyboardWillShow/Hide methods.
我遇到了类似的问题,找到了一个非常简单的解决方案,不使用 scrollView,而是在 keyboardWillShow/Hide 方法中使用 if 语句。
func keyboardWillShow(notification: NSNotification) {
if bottomText.editing{
self.view.window?.frame.origin.y = -1 * getKeyboardHeight(notification)
}
}
func keyboardWillHide(notification: NSNotification) {
if self.view.window?.frame.origin.y != 0 {
self.view.window?.frame.origin.y += getKeyboardHeight(notification)
}
}
This was a good solution for me because I only had two text fields.
这对我来说是一个很好的解决方案,因为我只有两个文本字段。
Shifts whole view up:only when certain text fields (bottomText) are edited
向上移动整个视图:仅在编辑某些文本字段 (bottomText) 时
Shifts whole view down:only when the view is not at the original location
向下移动整个视图:仅当视图不在原始位置时
回答by Bilal Mustafa
Just use this extension to move any UIView when keyboard is presented.
只需使用此扩展程序即可在出现键盘时移动任何 UIView。
extension UIView {
func bindToKeyboard(){
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillChange(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
@objc func keyboardWillChange(_ notification: NSNotification){
let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
let beginningFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let endFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let deltaY = endFrame.origin.y - beginningFrame.origin.y
UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
self.frame.origin.y += deltaY
}, completion: nil)
}
}
Then in your viewdidload bind your view to the keyboard
然后在您的 viewdidload 中将您的视图绑定到键盘
UiView.bindToKeyboard()
回答by RJiryes
Why not implement this in a UITableViewController instead? The keyboard won't hide any text fields when its shown.
为什么不在 UITableViewController 中实现它呢?键盘在显示时不会隐藏任何文本字段。
回答by iluvatar_GR
Swift 4 (**updated) with extension**
Swift 4 (**更新) 扩展**
- add the buttons in one container
- connect bottom constraint of the container with IBOutlet containerBtmConstrain
inViewDidLoad
self.containerDependOnKeyboardBottomConstrain = containerBtmConstrain self.watchForKeyboard()
add the following extension
import UIKit private var xoAssociationKeyForBottomConstrainInVC: UInt8 = 0 extension UIViewController { var containerDependOnKeyboardBottomConstrain :NSLayoutConstraint! { get { return objc_getAssociatedObject(self, &xoAssociationKeyForBottomConstrainInVC) as? NSLayoutConstraint } set(newValue) { objc_setAssociatedObject(self, &xoAssociationKeyForBottomConstrainInVC, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) } } func watchForKeyboard() { NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasShown(notification:)), name:UIResponder.keyboardWillShowNotification, object: nil); NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name:UIResponder.keyboardWillHideNotification, object: nil); } @objc func keyboardWasShown(notification: NSNotification) { let info = notification.userInfo! guard let keyboardFrame: CGRect = (info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } UIView.animate(withDuration: 0.3, animations: { () -> Void in self.containerDependOnKeyboardBottomConstrain.constant = -keyboardFrame.height self.view.layoutIfNeeded() }) } @objc func keyboardWillHide(notification: NSNotification) { UIView.animate(withDuration: 0.3, animations: { () -> Void in self.containerDependOnKeyboardBottomConstrain.constant = 0 self.view.layoutIfNeeded() }) } }
- 在一个容器中添加按钮
- 将容器的底部约束与 IBOutlet containerBtmConstrain 连接起来
在视图中加载
self.containerDependOnKeyboardBottomConstrain = containerBtmConstrain self.watchForKeyboard()
添加以下扩展名
import UIKit private var xoAssociationKeyForBottomConstrainInVC: UInt8 = 0 extension UIViewController { var containerDependOnKeyboardBottomConstrain :NSLayoutConstraint! { get { return objc_getAssociatedObject(self, &xoAssociationKeyForBottomConstrainInVC) as? NSLayoutConstraint } set(newValue) { objc_setAssociatedObject(self, &xoAssociationKeyForBottomConstrainInVC, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) } } func watchForKeyboard() { NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasShown(notification:)), name:UIResponder.keyboardWillShowNotification, object: nil); NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name:UIResponder.keyboardWillHideNotification, object: nil); } @objc func keyboardWasShown(notification: NSNotification) { let info = notification.userInfo! guard let keyboardFrame: CGRect = (info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } UIView.animate(withDuration: 0.3, animations: { () -> Void in self.containerDependOnKeyboardBottomConstrain.constant = -keyboardFrame.height self.view.layoutIfNeeded() }) } @objc func keyboardWillHide(notification: NSNotification) { UIView.animate(withDuration: 0.3, animations: { () -> Void in self.containerDependOnKeyboardBottomConstrain.constant = 0 self.view.layoutIfNeeded() }) } }
回答by Joe Hoffman
I use SwiftLint, which had a few issues with the formatting of the accepted answer. Specifically:
我使用 SwiftLint,它在接受答案的格式方面存在一些问题。具体来说:
no space before the colon, no force casting, prefer UIEdgeInset(top: etc... instead of UIEdgeInsetMake.
冒号前没有空格,没有强制转换,更喜欢 UIEdgeInset(top: etc... 而不是 UIEdgeInsetMake。
so here are those updates for Swift 3
所以这里是 Swift 3 的更新
func registerForKeyboardNotifications() {
//Adding notifies on keyboard appearing
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func deregisterFromKeyboardNotifications() {
//Removing notifies on keyboard appearing
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func keyboardWasShown(notification: NSNotification) {
//Need to calculate keyboard exact size due to Apple suggestions
scrollView?.isScrollEnabled = true
var info = notification.userInfo!
if let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size {
let contentInsets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)
scrollView?.contentInset = contentInsets
scrollView?.scrollIndicatorInsets = contentInsets
var aRect: CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
if let activeField = self.activeField {
if !aRect.contains(activeField.frame.origin) {
self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
}
}
}
}
func keyboardWillBeHidden(notification: NSNotification) {
//Once keyboard disappears, restore original positions
var info = notification.userInfo!
if let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size {
let contentInsets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: -keyboardSize.height, right: 0.0)
scrollView?.contentInset = contentInsets
scrollView?.scrollIndicatorInsets = contentInsets
}
view.endEditing(true)
scrollView?.isScrollEnabled = false
}
func textFieldDidBeginEditing(_ textField: UITextField) {
activeField = textField
}
func textFieldDidEndEditing(_ textField: UITextField) {
activeField = nil
}
回答by zevij
I think this clause is wrong:
我认为这个条款是错误的:
if (!CGRectContainsPoint(aRect, activeField!.frame.origin))
While the activeField's origin may well be above the keyboard, the maxY might not...
虽然 activeField 的原点可能在键盘上方,但 maxY 可能不会......
I would create a 'max' point for the activeField and check if that is in the keyboard Rect.
我会为 activeField 创建一个“最大”点并检查它是否在键盘 Rect 中。
回答by Anurag Sharma
Awesome answers are already given but this is a different way to deal with this situation (using Swift 3x):
已经给出了很棒的答案,但这是处理这种情况的不同方式(使用Swift 3x):
First of all call the following method in viewWillAppear()
首先调用下面的方法 viewWillAppear()
func registerForKeyboardNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasShown), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillBeHidden), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
Now take one IBOutlet
of UIView
's top constraints of your UIViewcontroller
like this: (here the UIView
is the subview of UIScrollView
that means you should have a UIScrollView
for all your subViews
)
现在采取一个IBOutlet
的UIView
是你的主要约束UIViewcontroller
是这样的:(这里UIView
是子视图UIScrollView
,这意味着你应该有一个UIScrollView
为所有subViews
)
@IBOutlet weak var loginViewTopConstraint: NSLayoutConstraint!
And an another variable like following and add a delegate i.e. UITextFieldDelegate
:
另一个变量如下所示,并添加一个委托,即UITextFieldDelegate
:
var activeTextField = UITextField() //This is to keep the reference of UITextField currently active
After that here is the magical part just paste this below snippet:
之后,这是神奇的部分,只需将其粘贴到以下代码段即可:
func keyboardWasShown(_ notification: Notification) {
let keyboardInfo = notification.userInfo as NSDictionary?
//print(keyboardInfo!)
let keyboardFrameEnd: NSValue? = (keyboardInfo?.value(forKey: UIKeyboardFrameEndUserInfoKey) as? NSValue)
let keyboardFrameEndRect: CGRect? = keyboardFrameEnd?.cgRectValue
if activeTextField.frame.origin.y + activeTextField.frame.size.height + 10 > (keyboardFrameEndRect?.origin.y)! {
UIView.animate(withDuration: 0.3, delay: 0, options: .transitionFlipFromTop, animations: {() -> Void in
//code with animation
//Print some stuff to know what is actually happening
//print(self.activeTextField.frame.origin.y)
//print(self.activeTextField.frame.size.height)
//print(self.activeTextField.frame.size.height)
self.loginViewTopConstraint.constant = -(self.activeTextField.frame.origin.y + self.activeTextField.frame.size.height - (keyboardFrameEndRect?.origin.y)!) - 30.0
self.view.layoutIfNeeded()
}, completion: {(_ finished: Bool) -> Void in
//code for completion
})
}
}
func keyboardWillBeHidden(_ notification: Notification) {
UIView.animate(withDuration: 0.3, animations: {() -> Void in
self.loginViewTopConstraint.constant = self.view.frame.origin.y
self.view.layoutIfNeeded()
})
}
//MARK: textfield delegates
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
activeTextField = textField
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
switch textField {
case YOUR_TEXTFIELD_ONE:
YOUR_TEXTFIELD_TWO.becomeFirstResponder()
break
case YOUR_TEXTFIELD_TWO:
YOUR_TEXTFIELD_THREE.becomeFirstResponder()
break
default:
textField.resignFirstResponder()
break
}
return true
}
Now the last snippet:
现在是最后一个片段:
//Remove Keyboard Observers
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
Don't forget to assign delegates to all your UITextField
s in UIStoryboard
不要忘记将委托分配给您UITextField
的所有sUIStoryboard
Good luck!
祝你好运!