テーブルビューのライブフィルターとして機能するUISearchBarがあります。 endEditing:を介してキーボードが閉じられると、クエリテキストと灰色の円形の「クリア」ボタンが残ります。ここから、灰色の「クリア」ボタンをタップすると、テキストがクリアされるとキーボードが再表示されます。
これを防ぐにはどうすればよいですか?キーボードが現在開いていない場合は、キーボードを再度開かずにそのボタンでテキストをクリアする必要があります。
クリアボタンをタップすると呼び出されるプロトコルメソッドがあります。ただし、UISearchBarにresignFirstResponderメッセージを送信しても、キーボードには影響しません。
これは古い質問であり、私はちょうど同じ問題に出会い、次の方法でそれを解決することができました:
searchBar:textDidChange:
ユーザーが「クリア」ボタンをタップしたためにUISearchBarDelegateのメソッドが呼び出されます。searchBarはまだ最初のレスポンダーになっていないため、ユーザーが実際にクリアしようとしたときに検出するためにそれを利用できますsearchBarにフォーカスを移さずに、および/または他のことを行います。
これを追跡するには、viewControllerでsearchBarデリゲートでもあるBOOL
ivarを宣言し(shouldBeginEditing
と呼びましょう)、YES
(viewControllerクラスがSearchViewControllerと呼ばれる場合):
@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
後で、UISearchBarDelegateで、searchBar:textDidChange:
およびsearchBarShouldBeginEditing:
メソッド:
- (void)searchBar:(UISearchBar *)bar textDidChange:(NSString *)searchText {
NSLog(@"searchBar:textDidChange: isFirstResponder: %i", [self.searchBar isFirstResponder]);
if(![searchBar isFirstResponder]) {
// user tapped the 'clear' button
shouldBeginEditing = NO;
// do whatever I want to happen when the user clears the search...
}
}
- (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;
}
基本的にはそれだけです。
ベスト
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];
}
クリアボタンが押されたかどうかを知るための非常に安全な方法を見つけ、ユーザーが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を編集するときにクリアボタンをクリックすると、2つのpingがありますが、ユーザーが編集していないときにクリックすると1つだけになることです。
編集:私はそれをテストすることはできませんが、ここにSwift 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!")
}
}
@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
私の経験からの最善の解決策は、システムのクリアボタンの上に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];
}
textDidChange
UISearchBarDelegateメソッド内でsearchBar.resignFirstResponder()
を呼び出しても、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.
}
}
}
iOS 8以上でUISearchController
を使用している場合は、単にUISearchController
をサブクラス化する必要があります。完全を期すために、cancelボタンを非表示にすることもできます。これは、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:
に移動することができます。検索ボタン。[クリア]ボタンをタッチすると、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;
}
検索バーのデリゲート呼び出しのうち、古い値から新しい値への変更を受け入れるように求められます-新しい値がnilであり、古い値が非nilであり、ユーザーがそうではなかったことを示すインジケーターを検出できますキーボードが最後に起動してから何でも入力しました-その場合、検索バーの最初のレスポンダーを辞任します。ただし、キーボードが一時的に表示されるかどうかはわかりません。
私は非常によく似た状況にあり、自分で試してみるかもしれません。
EndEditingメソッドで、UISearchBarをクリアしないのはなぜですか?最初のレスポンダーを辞任する場所でもあるはずなので、それは理にかなっています。
A Swift @bolivaの回答のバージョン。
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
}
}