Tableviewcontrollerに設定しているsearchBarがあります。私はこの同様の質問を参照しました ITableViewが再表示された後、UISearchBarがファーストレスポンダーになることはできません しかし、それをファーストレスポンダーとして設定することはまだできません。 .hファイル内:
@property (strong, nonatomic) UISearchController *searchController;
@property (strong, nonatomic) IBOutlet UISearchBar *searchBar;
ビューのロード:
self.searchController = [[UISearchController alloc]initWithSearchResultsController:nil];
self.searchController.searchResultsUpdater = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.hidesNavigationBarDuringPresentation = NO;
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.Origin.x, self.searchController.searchBar.frame.Origin.y, self.searchController.searchBar.frame.size.width, 44.0);
self.tableView.tableHeaderView = self.searchController.searchBar;
そしてviewDidAppearで:
-(void)viewDidAppear:(BOOL)animated {
[self.searchController setActive:YES];
[self.searchController.searchBar becomeFirstResponder];
[super viewDidAppear:animated];
}
ビューに移動すると、searchBarはアニメーション化しますが、キーボードは表示されません。
私もこの問題に気づきました。起こりそうなのは、searchControllerがまだ「ロード中」のときにbecomeFirstResponder
の呼び出しが行われることです。 becomeFirstResponder
をコメントアウトすると、違いはありません。したがって、searchControllerが「完了」した後に、becomeFirstResponderを呼び出す方法が必要です。
さまざまなデリゲートメソッドを見ると、デリゲートメソッドがあることに気付きました。
- (void)didPresentSearchController:(UISearchController *)searchController
このメソッドは、searchControllerが提示された直後に呼び出されます。次に、becomeFirstResponder
を呼び出します。
- (void)didPresentSearchController:(UISearchController *)searchController
{
[searchController.searchBar becomeFirstResponder];
}
これにより問題が修正されます。 searchControllerがロードされると、検索バーにフォーカスが移ります。
-(void)didPresentSearchController:(UISearchController *)searchController
を使用したソリューションは機能しませんでした。このデリゲートメソッドは、ユーザーが検索バーをタップしたときにのみ呼び出されるためです...
ただし、このソリューションは機能しました。
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:@selector(showKeyboard) withObject:nil afterDelay:0.1];
}
- (void) showKeyboard
{
[self.searchController.searchBar becomeFirstResponder];
}
Swift
delay(0.1) { self.searchController.searchBar.becomeFirstResponder() }
func delay(_ delay: Double, closure: @escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
まあ、私は実際に私のために完全に機能しているソリューションを見つけました。
[self.searchController setActive:YES];
を呼び出す前に[self.searchController.searchBar becomeFirstResponder];
を呼び出さないでください
さらに、[self.searchController setActive:YES];
を呼び出さないでください。
[self.searchController.searchBar becomeFirstResponder];
のみを呼び出すと、キーボードが遅延なくすぐに飛び出します。
バグのようなもので、多くの人が確認しています。たとえば、ここでチェックしてください: becomeFirstResponderを介してUISearchControllerのUISearchBarにフォーカスを割り当てると、キーボードは表示されません
関数searchController.searchBar.becomeFirstResponder()
は、メインスレッドで、viewDidLoadメソッドのsearchController.active = true
の後に呼び出す必要があります。完全なソリューションは次のとおりです。 iOS 9.3で動作します
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
searchController.active = true
Async.main {
self.searchController.searchBar.becomeFirstResponder()
}
}
Swift 4、iOS 11
わたしにはできる
// 1.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
resultSearchController.isActive = true
}
// 2. ->> UISearchControllerDelegate
func didPresentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {
searchController.searchBar.becomeFirstResponder()
}
}
他の回答と非常に似ていますが、ViewDidAppear
でメインキューにアクセスする必要がありました。 SearchBarController
は、View
が現れるまで処理できず、UI
のメインキューでのみ実行できます。
searchController.active = true // doubtful whether this is needed
dispatch_async(dispatch_get_main_queue(), {
self.searchController.searchBar.becomeFirstResponder()
});
簡単Swiftバリアント:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.titleView = mySearchController.searchBar
mySearchController.searchResultsUpdater = self
mySearchController.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
DispatchQueue.main.async {
self.mySearchController.isActive = true
}
}
func presentSearchController(_ searchController: UISearchController) {
mySearchController.searchBar.becomeFirstResponder()
}
できます ;-)
答えは、viewDidAppearでbecomeFirstResponderを呼び出すことです。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
searchBar?.becomeFirstResponder()
}
この回答では、この発生に関連するいくつかの問題を調べ、私の場合の問題の特定の原因を特定して特定するために使用されるデバッグロジックについて説明します。途中で、私は他の状況で機能したかもしれない他の可能性を共有し、これが私の状況で機能する理由を説明します。
tldr; self.definesPresentationContext = true、isBeingDismissed、isBeingPresented、canBecomeFirstResponder、およびSearchController、Searchbar、SearchResultsUpdaterおよびそれらのデリゲートメソッドのデリゲート割り当てを見てください。
(Sourceof _self.definesPresentationContext
_ solution- SO answer を参照)
心に留めておくべきことの1つは、SearchBarの表示方法のコンテキストです。入力または入力アクセサリビューとして、ツールバー、ナビゲーションバー、別のUIViewに埋め込まれます。そのすべてが、検索バーが表示または非表示にされるときに、検索バーのタイミングと内部アニメーションに何らかの影響を与えることがわかりました。
提示されたすべてのソリューションを試しましたが、searchBarの使用方法を再考するまで、これらのいずれも機能しませんでした。私の場合、私はコントローラー(B)からコントローラー(A)既に初期のsearchcontrollerがその上にありました。プルリフレッシュを実行するときに、ナビゲーションアイテムのtitleView内に各検索コントローラーをプログラムで埋め込みました。
ライフサイクルにsearchbar.becomeFirstResponder()
を追加することを示唆する回答は、ナビゲーションバーに検索バーを挿入して表示したい時点でビューが完全に読み込まれたため、ユースケースには意味がありませんでした。 View Controllerのライフサイクルメソッドはすでにメインスレッドで動作しているはずなので、ロジックも混乱しているように見えました。プレゼンテーションに遅延を追加することは、システムで使用されるディスプレイとアニメーションの内部操作の干渉とも思われます。
ControllerAからView Controllerを押したときに挿入を切り替える関数を呼び出すことはできましたが、controllerBから押したときにキーボードが正しく表示されないことがわかりました。これら2つの状況の違いは、controllerAが静的Tableview Controllerであり、controllerBには独自の検索コントローラーを追加して検索可能にしたTableview Controllerがあったことです。
例えばcontrollerAサーチバーを使用するとcontrollerBをサーチバーで使用します
いくつかのブレークポイントを使用して、さまざまなポイントでsearchControllerと検索バーのステータスを調べると、_searchController.canBecomeFirstResponder
_がfalse
を返していることがわかりました。また、SearchResultsUpdater
をself
に設定し、searchControllerとsearchBarの両方でデリゲートを設定する必要があることもわかりました。
最後に、controllerAに_self.definesPresentationContext = true
_を設定しても、controllerBをナビゲーションスタックにプッシュしたときにキーボードを表示できないことに注意しました。私の解決策は、controllerAの_self.definesPresentationContext = true
_をviewDidAppear
に移動し、controllerAのprepare(for:sender:)
メソッドで、宛先がcontrollerBのときに_self.definesPresentationContext = false
_に変更することでした。これにより、私の場合のキーボード表示の問題が解決しました。
アニメーションに関する単語〜navigationItemまたはnavigationBarに物事を割り当てると、システムにはタイミングとデフォルトのアニメーションが組み込まれていることがわかりました。多くの場合、予期しない動作が発生するため、カスタムアニメーション、moveToParentメソッドのコード、遅延プレゼンテーションの追加は避けます。
Apple documentation on definesPresentationContext
はデフォルトの動作を示し、このコンテキストがキーボードの外観を管理するためにコントローラーが割り当てた動作を調整する状況を示します。私の場合、controllerAはcontrollerBではなくプレゼンテーションを管理するように指定されていたので、この値を調整してその動作を変更しました。
CurrentContextスタイルまたはoverCurrentContextスタイルを使用してView Controllerを表示する場合、このプロパティは、View Controller階層内のどの既存のView Controllerが実際に新しいコンテンツでカバーされるかを制御します。コンテキストベースのプレゼンテーションが発生すると、UIKitは表示するView Controllerから開始し、View Controller階層をたどります。このプロパティの値がtrueであるView Controllerを見つけると、そのView Controllerに新しいView Controllerを表示するように要求します。プレゼンテーションコンテキストを定義しているView Controllerがない場合、UIKitはウィンドウのルートView Controllerにプレゼンテーションの処理を要求します。このプロパティのデフォルト値はfalseです。 UINavigationControllerなどの一部のシステム提供のView Controllerは、デフォルト値をtrueに変更します。
これは、Swift 4
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
searchController.isActive = true
DispatchQueue.main.async{
self.searchController.searchBar.becomeFirstResponder()
}
}
@mislovrのソリューションに基づいて、0.1
遅延が長すぎました。これがその答えに対する私の更新されたコードです。
func presentSearchController() {
searchController.isActive = true
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak searchController] timer in
guard let searchController = searchController else {
timer.invalidate()
return
}
if searchController.searchBar.canBecomeFirstResponder {
searchController.searchBar.becomeFirstResponder()
timer.invalidate()
}
}
}
もっときれいな解決策があると思います。キーボードが表示されたときに「ブリッピング」してからダウンするようなものであり、didPresentSearchController:
でbecomeFirstResponder
を呼び出すことは機能していましたが、キーボードが遅くなり、アニメーションが少し風変わりでした。
データのリロードメソッドをプレゼンテーションのチェックでラップすると、誰もが幸せになりました。
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
if (!searchController.isBeingPresented && !searchController.isBeingDismissed) {
[self.collectionView reloadData];
}
}
resignFirstResponder
にブレークポイントを設定することでこれを見つけました。 resignFirstResponder
はreloadData
から呼び出されていましたが、これはupdateSearchResultsForSearchController:
によって呼び出されました。検索コントローラーの表示中にupdateSearchResultsForSearchController:
が呼び出されることがわかりました。この間にUISearchBarが含まれているビュー階層をいじると、検索コントローラーが中断されます。私の推測では、reloadData呼び出しによりUICollectionReusableView
ヘッダービューが表示され、ビュー階層に戻り、UISearchBarサブビューに最初のレスポンダーを強制的に辞任させたと考えられます。
私が見た別の症状は、キャンセル時に検索用語が検索バーの中央にリセットされなかったため、将来のクリックで適切に表示されないことでした。
私はこれとしばらくの間戦いましたが、それを機能させました:
viewDidLoad()
のsearchController
の初期化viewDidAppear()
で_active = true
_を設定するUISearchControllerDelegate
拡張でdidPresentSearchController()
がトリガーされます。searchBar.becomeFirstResponder()
にdidPresentSearchController()
を設定完全な例は次のとおりです。GoogleMaps Autocompleteを使用しています。
_class myViewController: UIViewController {
// MARK: Variables
var resultsViewController: GMSAutocompleteResultsViewController?
var searchController: UISearchController?
var resultView: UITextView?
// MARK: Outlets
@IBOutlet var myView: UIView!
// MARK: View Methods
override func viewDidLoad() {
super.viewDidLoad()
resultsViewController = GMSAutocompleteResultsViewController()
resultsViewController?.delegate = self
searchController = UISearchController(searchResultsController: resultsViewController)
searchController?.delegate = self
searchController?.searchResultsUpdater = resultsViewController
searchController?.searchBar.Prompt = "Search for a Place"
searchController?.searchBar.placeholder = "place name"
searchController?.searchBar.text = ""
searchController?.searchBar.sizeToFit()
searchController?.searchBar.returnKeyType = .Next
searchController?.searchBar.setShowsCancelButton(true, animated: false)
myView.addSubview((searchController?.searchBar)!)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
searchController?.active = true
}
// MARK: GMSAutocompleteResultsViewControllerDelegate Extension
extension myViewController: GMSAutocompleteResultsViewControllerDelegate {
func resultsController(resultsController: GMSAutocompleteResultsViewController,
didAutocompleteWithPlace place: GMSPlace) {
searchController?.active = false
// Do something with the selected place.
print("Place name: ", place.name)
print("Place address: ", place.formattedAddress)
print("Place attributions: ", place.attributions)
}
func resultsController(resultsController: GMSAutocompleteResultsViewController,
didFailAutocompleteWithError error: NSError){
// TODO: handle the error.
print("Error: ", error.description)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictionsForResultsController(resultsController: GMSAutocompleteResultsViewController) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictionsForResultsController(resultsController: GMSAutocompleteResultsViewController) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
}
extension myViewController: UISearchControllerDelegate {
func didPresentSearchController(searchController: UISearchController) {
self.searchController?.searchBar.becomeFirstResponder()
}
}
}
_
@edwardmpと@mislovrを混合するソリューションは、私のために働いていました(キーボードが少し遅れて飛び出します):
- (void)didPresentSearchController:(UISearchController *)searchController {
[self performSelector:@selector(showKeyboard) withObject:nil afterDelay:0.001];
}
- (void) showKeyboard {
[self.searchController.searchBar becomeFirstResponder];
}
Swift 5
解決策:
override func viewDidLoad() {
super.viewDidLoad()
definesPresentationContext = true
searchController.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
searchController.isActive = true
}
extension ViewController: UISearchControllerDelegate {
func didPresentSearchController(_ searchController: UISearchController) {
DispatchQueue.main.async {[weak self] in
self?.searchController.searchBar.becomeFirstResponder()
}
}
}
Objective Cの場合:
- (void)viewDidLoad {
[super viewDidLoad];
// Setup your SearchViewController here...
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Will show Cancel button in White colour
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTintColor:[UIColor whiteColor]];
// searchController.active = YES; // This is not necessary
// set SearchBar first responder inside Main Queue block
dispatch_async(dispatch_get_main_queue(), ^{
[self->searchController.searchBar becomeFirstResponder];
});
}
これは、Swift 3。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.perform(#selector(self.showKeyboard), with: nil, afterDelay: 0.1)
}
func showKeyboard() {
self.searchController.searchBar.becomeFirstResponder()
}
私にとって、viewDidAppear
を使用するとかなり大きな遅れがあります。 becomeFirstResponder
で非同期にviewDidLoad
を使用する方が良い場合があります(iOS 10でテスト済みSwift 3):
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.async {
searchController.searchBar.becomeFirstResponder()
}
}