ios 如何浏览文本字段(下一步/完成按钮)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1347779/
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
How to navigate through textfields (Next / Done Buttons)
提问by phx
How can I navigate through all my text fields with the "Next" Button on the iPhone Keyboard?
如何使用 iPhone 键盘上的“下一步”按钮浏览所有文本字段?
The last text field should close the Keyboard.
最后一个文本字段应关闭键盘。
I've setup the IB the Buttons (Next / Done) but now I'm stuck.
我已经设置了 IB 按钮(下一步/完成),但现在我卡住了。
I implemented the textFieldShouldReturn action but now the Next and Done Buttons close the Keyboard.
我实现了 textFieldShouldReturn 操作,但现在 Next 和 Done 按钮关闭了键盘。
回答by PeyloW
In Cocoa for Mac OS X, you have the next responder chain, where you can ask the text field what control should have focus next. This is what makes tabbing between text fields work. But since iOS devices do not have a keyboard, only touch, this concept has not survived the transition to Cocoa Touch.
在 Mac OS X 的 Cocoa 中,您有下一个响应者链,您可以在其中询问文本字段接下来应该关注哪个控件。这就是使文本字段之间的 Tab 能够工作的原因。但是由于 iOS 设备没有键盘,只有触摸,所以这个概念在向 Cocoa Touch 的过渡中没有幸存下来。
This can be easily done anyway, with two assumptions:
无论如何,这很容易做到,有两个假设:
- All "tabbable"
UITextField
s are on the same parent view. - Their "tab-order" is defined by the tag property.
- 所有“tabable”
UITextField
都在同一个父视图上。 - 它们的“tab-order”由标签属性定义。
Assuming this you can override textFieldShouldReturn: as this:
假设您可以覆盖 textFieldShouldReturn: 如下:
-(BOOL)textFieldShouldReturn:(UITextField*)textField
{
NSInteger nextTag = textField.tag + 1;
// Try to find next responder
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
// Found next responder, so set it.
[nextResponder becomeFirstResponder];
} else {
// Not found, so remove keyboard.
[textField resignFirstResponder];
}
return NO; // We do not want UITextField to insert line-breaks.
}
Add some more code, and the assumptions can be ignored as well.
添加更多代码,假设也可以忽略。
Swift 4.0
斯威夫特 4.0
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
// Try to find next responder
let nextResponder = textField.superview?.viewWithTag(nextTag) as UIResponder!
if nextResponder != nil {
// Found next responder, so set it
nextResponder?.becomeFirstResponder()
} else {
// Not found, so remove keyboard
textField.resignFirstResponder()
}
return false
}
If the superview of the text field will be a UITableViewCell then next responder will be
如果文本字段的超级视图将是 UITableViewCell 那么下一个响应者将是
let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!
回答by memmons
There is a muchmore elegant solution which blew me away the first time I saw it. Benefits:
还有一个很多更优雅的解决方案,它把我吓倒了,我第一次看到它。好处:
- Closer to OSX textfield implementation where a textfield knows where the focus should go next
- Does not rely on setting or using tags -- which are, IMO fragile for this use case
- Can be extended to work with both
UITextField
andUITextView
controls -- or any keyboard entry UI control - Doesn't clutter your view controller with boilerplate UITextField delegate code
- Integrates nicely with IB and can be configured through the familiar option-drag-drop to connect outlets.
- 更接近 OSX 文本字段实现,其中文本字段知道接下来焦点应该去哪里
- 不依赖于设置或使用标签——对于这个用例,IMO 很脆弱
- 可以扩展以使用
UITextField
和UITextView
控件 -- 或任何键盘输入 UI 控件 - 不会用样板 UITextField 委托代码弄乱您的视图控制器
- 与 IB 很好地集成,可以通过熟悉的选项拖放进行配置以连接插座。
Create a UITextField subclass which has an IBOutlet
property called nextField. Here's the header:
创建一个 UITextField 子类,它有一个IBOutlet
名为 nextField的属性。这是标题:
@interface SOTextField : UITextField
@property (weak, nonatomic) IBOutlet UITextField *nextField;
@end
And here's the implementation:
这是实现:
@implementation SOTextField
@end
In your view controller, you'll create the -textFieldShouldReturn:
delegate method:
在您的视图控制器中,您将创建-textFieldShouldReturn:
委托方法:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if ([textField isKindOfClass:[SOTextField class]]) {
UITextField *nextField = [(SOTextField *)textField nextField];
if (nextField) {
dispatch_async(dispatch_get_current_queue(), ^{
[nextField becomeFirstResponder];
});
}
else {
[textField resignFirstResponder];
}
}
return YES;
}
In IB, change your UITextFields to use the SOTextField
class. Next, also in IB, set the delegate for each of the 'SOTextFields'to 'File's Owner' (which is right where you put the code for the delegate method - textFieldShouldReturn). The beauty of this design is that now you can simply right-click on any textField and assign the nextField outlet to the next SOTextField
object you want to be the next responder.
在 IB 中,更改您的 UITextFields 以使用SOTextField
该类。接下来,同样在 IB 中,将每个“SOTextFields”的委托设置为“文件的所有者”(这正是您放置委托方法代码的地方 - textFieldShouldReturn)。这种设计的美妙之处在于,现在您只需右键单击任何 textField 并将 nextField 出口分配给SOTextField
您希望成为下一个响应者的下一个对象。
Moreover, you can do cool things like loop the textFields so that after the last one loses focus, the first one will receive focus again.
此外,您可以做一些很酷的事情,例如循环 textFields,以便在最后一个失去焦点后,第一个将再次获得焦点。
This can easily be extended to automatically assign the returnKeyType
of the SOTextField
to a UIReturnKeyNext
if there is a nextField assigned -- one less thing manually configure.
这可以很容易地扩展到自动分配returnKeyType
的SOTextField
一个UIReturnKeyNext
,如果有一个nextField分配-少了一个东西手动配置。
回答by mxcl
Here's one without delegation:
这是一个没有委托的:
tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
ObjC:
对象:
[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
[tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
Works using the (mostly unknown) UIControlEventEditingDidEndOnExit
UITextField
action.
使用(大部分未知)UIControlEventEditingDidEndOnExit
UITextField
动作工作。
You can also easily hook this up in the storyboard, so no delegation orcode is required.
您还可以轻松地将其连接到故事板中,因此不需要委托或代码。
Edit: actually I cannot figure out how to hook this up in storyboard. becomeFirstResponder
does not seem to be a offered action for this control-event, which is a pity. Still, you can hook all your textfields up to a single action in your ViewController which then determines which textField to becomeFirstResponder
based on the sender (though then it is not as elegant as the above programmatic solution so IMO do it with the above code in viewDidLoad
).
编辑:实际上我无法弄清楚如何将其连接到故事板中。becomeFirstResponder
似乎不是针对此控制事件提供的操作,这很遗憾。尽管如此,您仍然可以将所有文本字段连接到 ViewController 中的单个操作,然后becomeFirstResponder
根据发件人确定哪个文本字段(尽管它不像上面的编程解决方案那么优雅,因此 IMO 使用上面的代码来完成viewDidLoad
)。
回答by Anth0
Here is my solution for this problem.
这是我对这个问题的解决方案。
To solve this (and because I hate relying on tags to do stuff) I decided to add a custom property to the UITextField object. In other words I created a category on UITextField like this :
为了解决这个问题(并且因为我讨厌依赖标签来做事),我决定向 UITextField 对象添加一个自定义属性。换句话说,我在 UITextField 上创建了一个类别,如下所示:
UITextField+Extended.h
UITextField+Extended.h
@interface UITextField (Extended)
@property(retain, nonatomic)UITextField* nextTextField;
@end
UITextField+Extended.m
UITextField+Extended.m
#import "UITextField+Extended.h"
#import <objc/runtime.h>
static char defaultHashKey;
@implementation UITextField (Extended)
- (UITextField*) nextTextField {
return objc_getAssociatedObject(self, &defaultHashKey);
}
- (void) setNextTextField:(UITextField *)nextTextField{
objc_setAssociatedObject(self, &defaultHashKey, nextTextField, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
Now, here is how I use it :
现在,这是我如何使用它:
UITextField *textField1 = ...init your textfield
UITextField *textField2 = ...init your textfield
UITextField *textField3 = ...init your textfield
textField1.nextTextField = textField2;
textField2.nextTextField = textField3;
textField3.nextTextField = nil;
And implement the textFieldShouldReturn method :
并实现 textFieldShouldReturn 方法:
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
UITextField *next = theTextField.nextTextField;
if (next) {
[next becomeFirstResponder];
} else {
[theTextField resignFirstResponder];
}
return NO;
}
I now have kind of a linked list of UITextField, each one knowing who's next in the line.
我现在有一个 UITextField 的链接列表,每个人都知道谁是下一个。
Hope it'll help.
希望它会有所帮助。
回答by Amos Joshua
A swift extension that applies mxcl's answer to make this particularly easy (adapted to swift 2.3 by Traveler):
应用 mxcl 的答案的 swift 扩展使这特别容易(由 Traveler 改编为 swift 2.3):
extension UITextField {
class func connectFields(fields:[UITextField]) -> Void {
guard let last = fields.last else {
return
}
for i in 0 ..< fields.count - 1 {
fields[i].returnKeyType = .Next
fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
}
last.returnKeyType = .Done
last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), forControlEvents: .EditingDidEndOnExit)
}
}
It's easy to use:
它易于使用:
UITextField.connectFields([field1, field2, field3])
The extension will set the return button to "Next" for all but the last field and to "Done" for the last field, and shift focus / dismiss the keyboard when these are tapped.
该扩展程序会将除最后一个字段之外的所有返回按钮设置为“下一步”,并将最后一个字段设置为“完成”,并在点击这些按钮时转移焦点/关闭键盘。
Swift < 2.3
斯威夫特 < 2.3
extension UITextField {
class func connectFields(fields:[UITextField]) -> Void {
guard let last = fields.last else {
return
}
for var i = 0; i < fields.count - 1; i += 1 {
fields[i].returnKeyType = .Next
fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
}
last.returnKeyType = .Done
last.addTarget(last, action: "resignFirstResponder", forControlEvents: .EditingDidEndOnExit)
}
}
SWIFT 3:use like this -
SWIFT 3:像这样使用 -
UITextField.connectFields(fields: [field1, field2])
Extension:
extension UITextField {
class func connectFields(fields:[UITextField]) -> Void {
guard let last = fields.last else {
return
}
for i in 0 ..< fields.count - 1 {
fields[i].returnKeyType = .next
fields[i].addTarget(fields[i+1], action: #selector(UIResponder.becomeFirstResponder), for: .editingDidEndOnExit)
}
last.returnKeyType = .go
last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit)
}
}
回答by mohamede1945
A more consistent and robust way is to use NextResponderTextFieldYou can configure it totally from interface builder with no need for setting the delegate or using view.tag
.
更一致和健壮的方法是使用NextResponderTextField您可以完全从界面构建器配置它,而无需设置委托或使用view.tag
.
All you need to do is
你需要做的就是
- Set the class type of your
UITextField
to beNextResponderTextField
- Then set the outlet of the
nextResponderField
to point to the next responder it can be anythingUITextField
or anyUIResponder
subclass. It can be also a UIButton and the library is smart enough to trigger theTouchUpInside
event of the button only if it's enabled.
- 将您的类类型设置
UITextField
为NextResponderTextField
- 然后将 的出口设置
nextResponderField
为指向下一个响应者,它可以是任何东西UITextField
或任何子UIResponder
类。它也可以是一个 UIButton 并且该库足够智能,TouchUpInside
只有在启用时才触发按钮的事件。
Here is the library in action:
这是正在运行的库:
回答by Marquee
I like the OO solutions that have already been suggested by Anth0 and Answerbot. However, I was working on a quick and small POC, so I didn't want to clutter things with subclasses and categories.
我喜欢 Anth0 和 Answerbot 已经建议的 OO 解决方案。但是,我正在开发一个快速且小型的 POC,因此我不想将子类和类别弄得乱七八糟。
Another simple solution is to create an NSArray of fields and lookup the next field when you press next. Not an OO solution, but quick, simple, and easy to implement. Also, you can see and modify the ordering at a glance.
另一个简单的解决方案是创建一个 NSArray 字段并在您按下下一步时查找下一个字段。不是面向对象的解决方案,但快速、简单且易于实现。此外,您可以一目了然地查看和修改排序。
Here's my code (built upon other answers in this thread):
这是我的代码(基于此线程中的其他答案):
@property (nonatomic) NSArray *fieldArray;
- (void)viewDidLoad {
[super viewDidLoad];
fieldArray = [NSArray arrayWithObjects: firstField, secondField, thirdField, nil];
}
- (BOOL) textFieldShouldReturn:(UITextField *) textField {
BOOL didResign = [textField resignFirstResponder];
if (!didResign) return NO;
NSUInteger index = [self.fieldArray indexOfObject:textField];
if (index == NSNotFound || index + 1 == fieldArray.count) return NO;
id nextField = [fieldArray objectAtIndex:index + 1];
activeField = nextField;
[nextField becomeFirstResponder];
return NO;
}
- I always return NO because I don't want a line break inserted. Just thought I'd point that out since when I returned YES it would automatically exit the subsequent fields or insert a line break in my TextView. It took me a bit of time to figure that out.
- activeField keeps track of the active field in case scrolling is necessary to unobscure the field from the keyboard. If you have similar code, make sure you assign the activeField before changing the first responder. Changing first responder is immediate and will fire the KeyboardWasShown event immediately.
- 我总是返回 NO 因为我不想插入换行符。只是想我会指出这一点,因为当我返回 YES 时,它会自动退出后续字段或在我的 TextView 中插入换行符。我花了一些时间才弄明白。
- activeField 跟踪活动字段,以防需要滚动以从键盘上清除该字段。如果您有类似的代码,请确保在更改第一响应者之前分配了 activeField。立即更改第一响应者并将立即触发 KeyboardWasShown 事件。
回答by picciano
Here is an implementation of tabbing using a category on UIControl. This solution has all of the advantages of the methods from Michael and Anth0, but works for all UIControls, not just UITextField
s. It also works seamlessly with Interface Builder and storyboards.
这是使用 UIControl 上的类别的 Tab 键实现。此解决方案具有 Michael 和 Anth0 方法的所有优点,但适用于所有 UIControl,而不仅仅是UITextField
s。它还可以与 Interface Builder 和故事板无缝协作。
Source and sample app: GitHub repository for UIControlsWithTabbing
源代码和示例应用程序:UIControlsWithTabbing 的 GitHub 存储库
Usage:
用法:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField transferFirstResponderToNextControl];
return NO;
}
Header:
标题:
//
// UIControl+NextControl.h
// UIControlsWithTabbing
//
#import <UIKit/UIKit.h>
@interface UIControl (NextControl)
@property (nonatomic, weak) IBOutlet UIControl *nextControl;
- (BOOL)transferFirstResponderToNextControl;
@end
Implementation:
执行:
#import "UIControl+NextControl.h"
#import <objc/runtime.h>
static char defaultHashKey;
@implementation UIControl (NextControl)
- (UIControl *)nextControl
{
return objc_getAssociatedObject(self, &defaultHashKey);
}
- (void)setNextControl:(UIControl *)nextControl
{
objc_setAssociatedObject(self, &defaultHashKey, nextControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)transferFirstResponderToNextControl
{
if (self.nextControl)
{
[self.nextControl becomeFirstResponder];
return YES;
}
[self resignFirstResponder];
return NO;
}
@end
回答by BHUVANESH MOHANKUMAR
I have tried many codes and finally, this worked for me in Swift 3.0 Latest [March 2017]
我尝试了很多代码,最后,这在Swift 3.0 最新版 [2017 年 3 月] 中对我有用
The ViewController
class should be inherited the UITextFieldDelegate
for making this code working.
该ViewController
班应该继承了UITextFieldDelegate
制作该代码工作。
class ViewController: UIViewController,UITextFieldDelegate
Add the Text field with the Proper Tag number and this tag number is used to take the control to appropriate text field based on incremental tag number assigned to it.
添加具有正确标签编号的文本字段,该标签编号用于根据分配给它的增量标签编号将控件带到适当的文本字段。
override func viewDidLoad() {
userNameTextField.delegate = self
userNameTextField.tag = 0
userNameTextField.returnKeyType = UIReturnKeyType.next
passwordTextField.delegate = self
passwordTextField.tag = 1
passwordTextField.returnKeyType = UIReturnKeyType.go
}
In the above code, the returnKeyType = UIReturnKeyType.next
where will make the Key pad return key to display as Next
you also have other options as Join/Go
etc, based on your application change the values.
在上面的代码中,returnKeyType = UIReturnKeyType.next
where 将使键盘返回键显示,因为Next
您还有其他选项Join/Go
等,根据您的应用程序更改值。
This textFieldShouldReturn
is a method of UITextFieldDelegate controlled and here we have next field selection based on the Tag value incrementation
这 textFieldShouldReturn
是 UITextFieldDelegate 控制的一个方法,这里我们有基于 Tag 值增量的下一个字段选择
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
nextField.becomeFirstResponder()
} else {
textField.resignFirstResponder()
return true;
}
return false
}
回答by iKushal
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
return YES;
}