UISearchBarをテーブルビューに実装しましたが、1つの小さなことを除いて、ほとんどすべてが機能しています。テキストを入力してからキーボードの検索ボタンを押すと、キーボードが消え、検索結果だけがテーブルに表示されます。テキストはUISearchBarに残りますが、キャンセルボタンは無効になります。
私は自分のリストをApple連絡先アプリの機能にできるだけ近づけようとしていますが、そのアプリで検索を押してもキャンセルボタンは無効になりません。
UISearchBarヘッダーファイルを調べたところ、_searchBarFlags構造体の下にautoDisableCancelButtonのフラグがあることに気付きましたが、これはプライベートです。
UISearchBarをセットアップするときに欠けているものはありますか?
私は解決策を見つけました。このforループを使用して、検索バーのサブビューをループし、キーボードの検索ボタンが押されたときに有効にすることができます。
for (UIView *possibleButton in searchBar.subviews)
{
if ([possibleButton isKindOfClass:[UIButton class]])
{
UIButton *cancelButton = (UIButton*)possibleButton;
cancelButton.enabled = YES;
break;
}
}
IOS 7で機能させるには、これを少し調整する必要がありました。
- (void)enableCancelButton:(UISearchBar *)searchBar
{
for (UIView *view in searchBar.subviews)
{
for (id subview in view.subviews)
{
if ( [subview isKindOfClass:[UIButton class]] )
{
[subview setEnabled:YES];
NSLog(@"enableCancelButton");
return;
}
}
}
}
これを簡単に実現するには2つの方法があります
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
// The small and dirty
[(UIButton*)[searchBar valueForKey:@"_cancelButton"] setEnabled:YES];
// The long and safe
UIButton *cancelButton = [searchBar valueForKey:@"_cancelButton"];
if ([cancelButton respondsToSelector:@selector(setEnabled:)]) {
cancelButton.enabled = YES;
}
}
2番目のものを使用する必要があります。Appleがバックグラウンドで変更する場合、アプリケーションはクラッシュしません。
ところで、iOS 4.0から8.2までテストしましたが、変更はありません。また、ストアで承認されたアプリケーションでも問題なく使用しています。
これが私にとってiOS6で動作するようになった理由です:
searchBar.showsCancelButton = YES;
searchBar.showsScopeBar = YES;
[searchBar sizeToFit];
[searchBar setShowsCancelButton:YES animated:YES];
ここでの私の答え に従って、これをsearchBarデリゲートに配置します。
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
dispatch_async(dispatch_get_main_queue(), ^{
__block __weak void (^weakEnsureCancelButtonRemainsEnabled)(UIView *);
void (^ensureCancelButtonRemainsEnabled)(UIView *);
weakEnsureCancelButtonRemainsEnabled = ensureCancelButtonRemainsEnabled = ^(UIView *view) {
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIControl class]]) {
[(UIControl *)subview setEnabled:YES];
}
weakEnsureCancelButtonRemainsEnabled(subview);
}
};
ensureCancelButtonRemainsEnabled(searchBar);
});
}
これが私のソリューションです。これは、iOSのすべてのバージョンのすべての状況で機能します。
IE、他のソリューションは、ユーザーがスクロールビューをドラッグしたためにキーボードが閉じられたときに処理しません。
- (void)enableCancelButton:(UIView *)view {
if ([view isKindOfClass:[UIButton class]]) {
[(UIButton *)view setEnabled:YES];
} else {
for (UIView *subview in view.subviews) {
[self enableCancelButton:subview];
}
}
}
// This will handle whenever the text field is resigned non-programatically
// (IE, because it's set to resign when the scroll view is dragged in your storyboard.)
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
[self performSelector:@selector(enableCancelButton:) withObject:searchBar afterDelay:0.001];
}
// Also follow up every [searchBar resignFirstResponder];
// with [self enableCancelButton:searchBar];
これがSwift 3のソリューションで、拡張機能を利用してキャンセルボタンを簡単に取得します。
extension UISearchBar {
var cancelButton: UIButton? {
for subView1 in subviews {
for subView2 in subView1.subviews {
if let cancelButton = subView2 as? UIButton {
return cancelButton
}
}
}
return nil
}
}
今使用のために:
class MyTableViewController : UITableViewController, UISearchBarDelegate {
var searchBar = UISearchBar()
func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.tableHeaderView = searchBar
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
DispatchQueue.main.async {
if let cancelButton = searchBar.cancelButton {
cancelButton.isEnabled = true
cancelButton.isUserInteractionEnabled = true
}
}
}
}
答えはどれも私にはまったく役に立たなかった。 iOS 7をターゲットにしていますが、答えが見つかりました。
私が試しているのは、TwitteriOSアプリのようなものです。 [タイムライン]タブの虫眼鏡をクリックすると、[キャンセル]ボタンがアクティブになり、キーボードが表示され、最近の検索画面が表示された状態でUISearchBar
が表示されます。最近の検索画面をスクロールすると、キーボードは非表示になりますが、[キャンセル]ボタンはアクティブのままになります。
これは私の作業コードです:
UIView *searchBarSubview = self.searchBar.subviews[0];
NSArray *subviewCache = [searchBarSubview valueForKeyPath:@"subviewCache"];
if ([subviewCache[2] respondsToSelector:@selector(setEnabled:)]) {
[subviewCache[2] setValue:@YES forKeyPath:@"enabled"];
}
テーブルビューのscrollViewWillBeginDragging:
にブレークポイントを設定することで、このソリューションに到達しました。私は自分のUISearchBar
を調べて、そのサブビューを公開しました。常に1つだけあり、タイプはUIView
(私の変数searchBarSubview
)です。
次に、そのUIView
はNSArray
と呼ばれるsubviewCache
を保持し、3番目の最後の要素はタイプUINavigationButton
であり、パブリックAPI。そこで、代わりにKey-Valueコーディングを使用することにしました。 UINavigationButton
がsetEnabled:
に応答するかどうかを確認しましたが、幸いなことに応答します。そこで、プロパティを@YES
に設定しました。 UINavigationButton
is [キャンセル]ボタンであることがわかります。
AppleがUISearchBar
の内部の実装を変更することを決定した場合、これは間違いなく壊れますが、一体何なのでしょう。今のところは機能します。
MonotouchまたはXamariniOSの場合、iOS7およびiOS8で機能する次のC#ソリューションがあります。
foreach(UIView view in searchBar.Subviews)
{
foreach(var subview in view.Subviews)
{
//Console.WriteLine(subview.GetType());
if(subview.GetType() == typeof(UIButton))
{
if(subview.RespondsToSelector(new Selector("setEnabled:")))
{
UIButton cancelButton = (UIButton)subview;
cancelButton.Enabled = true;
Console.WriteLine("enabledCancelButton");
return;
}
}
}
}
この答えは David Douglas ソリューションに基づいています。
より完全な答え:
searchBarTextDidEndEditing
です。。
extension MyController: UISearchBarDelegate {
public func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
DispatchQueue.main.async {
// you need that since the disabling will
// happen after searchBarTextDidEndEditing is called
searchBar.subviews.forEach({ view in
view.subviews.forEach({ subview in
// ios 7+
if let cancelButton = subview as? UIButton {
cancelButton.isEnabled = true
cancelButton.isUserInteractionEnabled = true
return
}
})
// ios 7-
if let cancelButton = subview as? UIButton {
cancelButton.isEnabled = true
cancelButton.isUserInteractionEnabled = true
return
}
})
}
}
}