如何在iOS UISearchBar中search(基于打字速度)?
我有一个UISearchDisplayController的UISearchBar部分,用来显示来自本地CoreData和远程API的search结果。 我想实现的是在远程API上search的“延迟”。 目前,对于由用户键入的每个字符,发送请求。 但是,如果用户input的速度特别快,发送很多请求是没有意义的:这将有助于等待他停止input。 有没有办法做到这一点?
阅读文档build议等到用户明确点击search,但我不觉得它是理想的在我的情况。
性能问题。 如果search操作可以非常迅速地进行,可以通过在委托对象上实现searchBar:textDidChange:方法来更新search结果。 但是,如果search操作需要更多时间,则应该等到用户点击“search”button后才开始searchsearchBarSearchButtonClicked:方法。 总是执行后台线程search操作,以避免阻塞主线程。 这使您的应用程序在search运行时保持对用户的响应,并提供更好的用户体验。
向API发送很多请求不是本地性能问题,而只是避免远程服务器上的请求率过高。
谢谢
试试这个魔法:
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{ // to limit network activity, reload half a second after last key press. [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(reload) object:nil]; [self performSelector:@selector(reload) withObject:nil afterDelay:0.5]; }
Swift版本:
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { // to limit network activity, reload half a second after last key press. NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: "reload", object: nil) self.performSelector("reload", withObject: nil, afterDelay: 0.5) }
注意这个例子调用一个叫做reload的方法,但是你可以调用任何你喜欢的方法!
对于Swift需要的人来说
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { // to limit network activity, reload half a second after last key press. NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: "reload", object: nil) self.performSelector("reload", withObject: nil, afterDelay: 0.5) }
编辑: SWIFT 3版本
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { // to limit network activity, reload half a second after last key press. NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(self.reload), object: nil) self.perform(#selector(self.reload), with: nil, afterDelay: 0.5) } func reload() { print("Doing things") }
感谢这个链接 ,我发现了一个非常快速和干净的方法。 与Nirmit的答案相比,它缺less“加载指示器”,但它赢得了代码行数,并且不需要额外的控制。 我首先添加dispatch_cancelable_block.h
文件到我的项目(从这个回购 ),然后定义以下类variables: __block dispatch_cancelable_block_t searchBlock;
。
我的search代码现在看起来像这样:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { if (searchBlock != nil) { //We cancel the currently scheduled block cancel_block(searchBlock); } searchBlock = dispatch_after_delay(searchBlockDelay, ^{ //We "enqueue" this block with a certain delay. It will be canceled if the user types faster than the delay, otherwise it will be executed after the specified delay [self loadPlacesAutocompleteForInput:searchText]; }); }
笔记:
-
loadPlacesAutocompleteForInput
是LPGoogleFunctions库的一部分 -
searchBlockDelay
在@implementation
之外定义如下:static CGFloat searchBlockDelay = 0.2;
一个快速的黑客会是这样的:
- (void)textViewDidChange:(UITextView *)textView { static NSTimer *timer; [timer invalidate]; timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(requestNewDataFromServer) userInfo:nil repeats:NO]; }
每当文本视图改变,定时器失效,导致它不会触发。 一个新的计时器被创build并被设置为在1秒之后被触发。 search仅在用户停止键入1秒后才会更新。
请参阅我在cocoa控件上find的以下代码。 他们asynchronous发送请求来获取数据。 可能是他们从本地获取数据,但您可以使用远程API来尝试。 在后台线程中发送远程API的asynchronous请求。 按照下面的链接:
https://www.cocoacontrols.com/controls/jcautocompletingsearch
我们可以使用dispatch_source
+ (void)runBlock:(void (^)())block withIdentifier:(NSString *)identifier throttle:(CFTimeInterval)bufferTime { if (block == NULL || identifier == nil) { NSAssert(NO, @"Block or identifier must not be nil"); } dispatch_source_t source = self.mappingsDictionary[identifier]; if (source != nil) { dispatch_source_cancel(source); } source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); dispatch_source_set_timer(source, dispatch_time(DISPATCH_TIME_NOW, bufferTime * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0); dispatch_source_set_event_handler(source, ^{ block(); dispatch_source_cancel(source); [self.mappingsDictionary removeObjectForKey:identifier]; }); dispatch_resume(source); self.mappingsDictionary[identifier] = source; }
更多有关使用GCD限制块执行的信息
如果您使用的是ReactiveCocoa ,请考虑RACSignal
throttle
方法
这里是你感兴趣的Swift中的ThrottleHandler
Swift 2.0版本的NSTimer解决scheme:
private var searchTimer: NSTimer? func doMyFilter() { //perform filter here } func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { if let searchTimer = searchTimer { searchTimer.invalidate() } searchTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(MySearchViewController.doMyFilter), userInfo: nil, repeats: false) }