我有一个UISearchBar,它充当表视图的实时过滤器。当键盘通过endEditing:解除时,查询文本和灰色圆形“清除”按钮保持不变。从这里,如果我点击灰色的“清除”按钮,键盘会在文本被清除时重新出现。
如何防止这种情况?如果键盘当前未打开,我希望该按钮在不重新打开键盘的情况下清除文本。
点击清除按钮时会调用一个协议方法。但是向UISearchBar发送resignFirstResponder消息对键盘没有任何影响。
答案 0 :(得分:121)
这是一个老问题,我遇到了同样的问题,并设法通过以下方式解决:
当由于用户点击“清除”按钮而调用UISearchBarDelegate的searchBar:textDidChange:
方法时,searchBar尚未成为第一个响应者,因此我们可以利用它来检测用户实际上打算清除搜索,而不是将焦点带到searchBar和/或做其他事情。
为了跟踪这一点,我们需要在viewController中声明BOOL
ivar,它也是searchBar委托(让我们称之为shouldBeginEditing
)并将其设置为初始值{{1 (假设我们的viewController类叫做SearchViewController):
YES
稍后,在UISearchBarDelegate中,我们实施了@interface SearchViewController : UIViewController <UISearchBarDelegate> {
// all of our ivar declarations go here...
BOOL shouldBeginEditing;
....
}
...
@end
@implementation SearchViewController
...
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
...
shouldBeginEditing = YES;
}
}
...
@end
和searchBar:textDidChange:
方法:
searchBarShouldBeginEditing:
基本上就是这样。
最佳
答案 1 :(得分:33)
我发现当从触摸到“清除按钮”调用textDidChange时,resignFirstResponder不起作用。但是,使用performSelection: withObject: afterDelay:
似乎是一种有效的解决方法:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if ([searchText length] == 0) {
[self performSelector:@selector(hideKeyboardWithSearchBar:) withObject:searchBar afterDelay:0];
}
}
- (void)hideKeyboardWithSearchBar:(UISearchBar *)searchBar
{
[searchBar resignFirstResponder];
}
答案 2 :(得分:10)
我找到了一种非常安全的方法来知道是否按下了清除按钮,并忽略了用户刚刚删除UISearchBar的最后一个字符的时间。这是:
- (BOOL)searchBar:(UISearchBar *)searchBar shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
_isRemovingTextWithBackspace = ([searchBar.text stringByReplacingCharactersInRange:range withString:text].length == 0);
return YES;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length == 0 && !_isRemovingTextWithBackspace)
{
NSLog(@"Has clicked on clear !");
}
}
非常简单明了,不是吗?唯一需要注意的是,如果用户在编辑UISearchBar的UITextField时单击清除按钮,则会有两个ping,而如果用户在未编辑时单击它,则只能获得一个ping
编辑: 我无法测试它,但根据Rotem,这里是快速版本:
var isRemovingTextWithBackspace = false
func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool
{
self.isRemovingTextWithBackspace = (NSString(string: searchBar.text!).stringByReplacingCharactersInRange(range, withString: text).characters.count == 0)
return true
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String)
{
if searchText.characters.count == 0 && !isRemovingTextWithBackspace
{
NSLog("Has clicked on clear !")
}
}
@ Rotem的更新(Swift2):
var isRemovingTextWithBackspace = false
func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
self.isRemovingTextWithBackspace = (NSString(string: searchBar.text!).stringByReplacingCharactersInRange(range, withString: text).characters.count == 0)
return true
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.characters.count == 0 && !isRemovingTextWithBackspace {
NSLog("Has clicked on clear!")
}
}
答案 3 :(得分:8)
我将@ boliva的答案和@ radiospiel的answer组合使用了一个不同的SO问题:
@interface SearchViewController : UIViewController <UISearchBarDelegate> {
// all of our ivar declarations go here...
BOOL shouldBeginEditing;
....
}
...
@end
@implementation SearchViewController
...
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
...
shouldBeginEditing = YES;
}
}
...
- (void) searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {
// TODO - dynamically update the search results here, if we choose to do that.
if (![searchBar isFirstResponder]) {
// The user clicked the [X] button while the keyboard was hidden
shouldBeginEditing = NO;
}
else if ([searchText length] == 0) {
// The user clicked the [X] button or otherwise cleared the text.
[theSearchBar performSelector: @selector(resignFirstResponder)
withObject: nil
afterDelay: 0.1];
}
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)bar {
// reset the shouldBeginEditing BOOL ivar to YES, but first take its value and use it to return it from the method call
BOOL boolToReturn = shouldBeginEditing;
shouldBeginEditing = YES;
return boolToReturn;
}
@end
答案 4 :(得分:3)
根据我的经验,最佳解决方案是在系统清除按钮上方放置UIButton
(背景清晰,没有文字),然后连接IBAction
使用自动布局非常简单
- (IBAction)searchCancelButtonPressed:(id)sender {
[self.searchBar resignFirstResponder];
self.searchBar.text = @"";
// some of my stuff
self.model.fastSearchText = nil;
[self.model fetchData];
[self reloadTableViewAnimated:NO];
}
答案 5 :(得分:1)
即使您在searchBar.resignFirstResponder()
UISearchBarDelegate方法中调用textDidChange
,在UISearchBar中按下“清除”按钮也会自动打开键盘。
要在按下“ x”清除UISearchBar时实际上隐藏键盘,请使用以下代码。这样,即使在UISearchBar中键入内容时调用textDidChange
,也只有在删除键盘中的所有文本,使用删除按钮或单击“ x”的情况下才会隐藏键盘:
extension ViewController: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text!.count == 0 {
DispatchQueue.main.async {
searchBar.resignFirstResponder()
}
} else {
// Code to make a request here.
}
}
}
答案 6 :(得分:0)
在你的endEditing方法中,为什么不清除UISearchBar呢?因为那也是你辞职第一响应者的地方,这是有道理的。
答案 7 :(得分:0)
搜索栏中的代理调用要求您接受从旧值到新值的更改 - 您可以检测到新值为nil,以及旧值为非零,并指示用户没有键入任何东西,因为键盘是最后一次 - 然后在那种情况下辞职搜索栏的第一响应者。不确定键盘是否会暂时显示。
我的情况非常相似,可能会自己尝试。
答案 8 :(得分:0)
触摸清除按钮会导致searchText
为空。另一种实现此目的的方法是检查- (void)searchBar:(UISearchBar *)bar textDidChange:(NSString *)searchText
中的空文本:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if([searchText length] == 0)
{
[self dismissSearch];
}
else
{
self.searchResultsTable.hidden = YES;
[self handleSearchForString:searchText];
}
}
- (void)dismissSearch
{
[self.searchBar performSelector: @selector(resignFirstResponder)
withObject: nil
afterDelay: 0.1];
self.searchResultsTable.hidden = YES;
}
答案 9 :(得分:0)
对于那些使用 iOS 8 及以上的UISearchController
的人,您只想简单地继承UISearchController
。为了完整起见,您可能还想像我一样隐藏取消按钮,因为清除UISearchBar
中的文本实际上是对搜索的取消。如果您想使用它,我已在下面添加了该代码。
这样做的好处是,您可以将它用于任何类和任何视图,而不需要UIViewController
的子类。我甚至会在此解决方案的底部包含我初始化UISearchController
的方式。
如果你想像我一样隐藏取消按钮,只需要覆盖此类。标记searchController.searchBar.showsCancelButton = NO
似乎不适用于 iOS 8 。我还没有测试 iOS 9 。
清空,但放在这里是为了完整。
@import UIKit;
@interface FJSearchBar : UISearchBar
@end
#import "FJSearchBar.h"
@implementation FJSearchBar
- (void)setShowsCancelButton:(BOOL)showsCancelButton {
// do nothing
}
- (void)setShowsCancelButton:(BOOL)showsCancelButton animated:(BOOL)animated {
// do nothing
}
@end
您希望在此处进行真正的更改。我将UISearchBarDelegate
拆分为自己的类别,因为恕我直言,这些类别使类更清晰,更易于维护。如果您希望将代理保留在主类接口/实现中,那么您非常欢迎这样做。
@import UIKit;
@interface FJSearchController : UISearchController
@end
@interface FJSearchController (UISearchBarDelegate) <UISearchBarDelegate>
@end
#import "FJSearchController.h"
#import "FJSearchBar.h"
@implementation FJSearchController {
@private
FJSearchBar *_searchBar;
BOOL _clearedOutside;
}
- (UISearchBar *)searchBar {
if (_searchBar == nil) {
// if you're not hiding the cancel button, simply uncomment the line below and delete the FJSearchBar alloc/init
// _searchBar = [[UISearchBar alloc] init];
_searchBar = [[FJSearchBar alloc] init];
_searchBar.delegate = self;
}
return _searchBar;
}
@end
@implementation FJSearchController (UISearchBarDelegate)
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar {
// if we cleared from outside then we should not allow any new editing
BOOL shouldAllowEditing = !_clearedOutside;
_clearedOutside = NO;
return shouldAllowEditing;
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// hide the keyboard since the user will no longer add any more input
[searchBar resignFirstResponder];
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
if (![searchBar isFirstResponder]) {
// the user cleared the search while not in typing mode, so we should deactivate searching
self.active = NO;
_clearedOutside = YES;
return;
}
// update the search results
[self.searchResultsUpdater updateSearchResultsForSearchController:self];
}
@end
需要注意的一些部分:
BOOL
作为私有变量而不是属性,因为
searchBar
是否是第一响应者。如果不是,那么我们实际上会停用搜索控制器,因为文本为空并且我们不再搜索。如果您确实想要确定,您还可以确保searchText.length == 0
。searchBar:textDidChange:
在searchBarShouldBeginEditing:
之前调用,这就是我们按此顺序处理的原因。[self.searchResultsUpdater updateSearchResultsForSearchController:self];
移至searchBarSearchButtonClicked:
按钮。答案 10 :(得分:0)
@boliva答案的Swift版本。
class MySearchContentController: UISearchBarDelegate {
private var searchBarShouldBeginEditing = true
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchBarShouldBeginEditing = searchBar.isFirstResponder
}
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
defer {
searchBarShouldBeginEditing = true
}
return searchBarShouldBeginEditing
}
}
答案 11 :(得分:-1)
我现在已经碰到过好几次了。我非常感谢人们给出的答案。
最终,我真的希望Apple允许我们(开发人员)检测何时按下清除按钮。
很明显,按下它会被检测到,因为搜索框中的任何文字都会被清除。
我猜他现在的优先顺序并不是很高......但我真的希望Apple的某个人能给UISearchBar一点爱!