mag*_*gix 68 search objective-c ios swift
我有一个UISearchBis部分的UISearchDisplayController,用于显示本地CoreData和远程API的搜索结果.我想要实现的是远程API搜索的"延迟".目前,对于用户键入的每个字符,发送请求.但是如果用户输入特别快,那么发送许多请求就没有意义:等到他停止输入会有所帮助.有没有办法实现这一目标?
阅读文档建议等到用户明确点击搜索,但我发现在我的情况下并不理想.
性能问题.如果可以非常快速地执行搜索操作,则可以通过在委托对象上实现searchBar:textDidChange:方法来在用户键入时更新搜索结果.但是,如果搜索操作需要更多时间,您应该等到用户点击搜索按钮,然后再在searchBarSearchButtonClicked:方法中开始搜索.始终执行后台线程的搜索操作以避免阻塞主线程.这可以使您的应用在搜索运行时对用户做出响应,并提供更好的用户体验.
向API发送许多请求不是本地性能的问题,而是避免远程服务器上的请求率过高.
谢谢
mal*_*hal 119
试试这个魔法:
-(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];
}
Run Code Online (Sandbox Code Playgroud)
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)
}
Run Code Online (Sandbox Code Playgroud)
请注意,此示例调用一个名为reload的方法,但您可以调用任何您喜欢的方法!
Viv*_*enG 43
对于在Swift中需要这个的人
保持简单,DispatchWorkItem如下所示:https://stackoverflow.com/a/48666001/308315
或使用旧的Obj-C方式:
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)
}
Run Code Online (Sandbox Code Playgroud)
编辑: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")
}
Run Code Online (Sandbox Code Playgroud)
mag*_*gix 15
感谢这个链接,我发现了一种非常快速和干净的方法.与Nirmit的答案相比,它缺少"加载指标",但它在代码行数方面获胜,并且不需要额外的控制.我首先将dispatch_cancelable_block.h文件添加到我的项目(来自此repo),然后定义以下类变量:__block dispatch_cancelable_block_t searchBlock;.
我的搜索代码现在看起来像这样:
- (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];
});
}
Run Code Online (Sandbox Code Playgroud)
笔记:
loadPlacesAutocompleteForInput是LPGoogleFunctions库的一部分searchBlockDelay定义如下@implementation:
static CGFloat searchBlockDelay = 0.2;
Ahm*_*d F 11
假设你已经符合要求UISearchBarDelegate,这是VivienG答案的改进的 Swift 4版本:
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(self.reload(_:)), object: searchBar)
perform(#selector(self.reload(_:)), with: searchBar, afterDelay: 0.75)
}
@objc func reload(_ searchBar: UISearchBar) {
guard let query = searchBar.text, query.trimmingCharacters(in: .whitespaces) != "" else {
print("nothing to search")
return
}
print(query)
}
Run Code Online (Sandbox Code Playgroud)
实现cancelPreviousPerformRequests(withTarget :)的目的是阻止对reload()搜索栏的每次更改的连续调用(如果键入"abc",则不添加它,reload()将根据添加的字符数调用三次) .
的改进是:在reload()方法具有发送器参数是搜索栏; 因此,可以通过将其声明为类中的全局属性来访问其文本或其任何方法/属性.
duc*_*i9y 10
快速黑客就是这样:
- (void)textViewDidChange:(UITextView *)textView
{
static NSTimer *timer;
[timer invalidate];
timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(requestNewDataFromServer) userInfo:nil repeats:NO];
}
Run Code Online (Sandbox Code Playgroud)
每次文本视图更改时,计时器都会失效,导致计时器不会触发.创建一个新计时器并设置为在1秒后触发.只有在用户停止键入1秒后才会更新搜索.
Swift 4 解决方案,以及一些一般性评论:
这些都是合理的方法,但是如果您想要示例性的自动搜索行为,您确实需要两个单独的计时器或分派。
理想的行为是 1) 自动搜索会定期触发,但 2) 不会太频繁(因为服务器负载、蜂窝带宽以及可能导致 UI 卡顿),以及 3) 一旦出现暂停,它就会迅速触发用户的输入。
您可以使用一个长期计时器来实现此行为,该计时器在编辑开始时立即触发(我建议 2 秒),并且无论以后的活动如何都允许运行,再加上一个短期计时器(约 0.75 秒),该计时器在每次编辑时重置改变。任一计时器到期都会触发自动搜索并重置两个计时器。
最终效果是连续打字会在每长时间秒内产生自动搜索,但保证暂停会在短时间秒内触发自动搜索。
您可以使用下面的 AutosearchTimer 类非常简单地实现此行为。以下是如何使用它:
// The closure specifies how to actually do the autosearch
lazy var timer = AutosearchTimer { [weak self] in self?.performSearch() }
// Just call activate() after all user activity
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
timer.activate()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
performSearch()
}
func performSearch() {
timer.cancel()
// Actual search procedure goes here...
}
Run Code Online (Sandbox Code Playgroud)
AutosearchTimer 在被释放时会处理自己的清理工作,因此无需在您自己的代码中担心这一点。但是不要给计时器一个强引用 self 否则你会创建一个引用循环。
下面的实现使用计时器,但如果您愿意,您可以根据调度操作重新转换它。
// Manage two timers to implement a standard autosearch in the background.
// Firing happens after the short interval if there are no further activations.
// If there is an ongoing stream of activations, firing happens at least
// every long interval.
class AutosearchTimer {
let shortInterval: TimeInterval
let longInterval: TimeInterval
let callback: () -> Void
var shortTimer: Timer?
var longTimer: Timer?
enum Const {
// Auto-search at least this frequently while typing
static let longAutosearchDelay: TimeInterval = 2.0
// Trigger automatically after a pause of this length
static let shortAutosearchDelay: TimeInterval = 0.75
}
init(short: TimeInterval = Const.shortAutosearchDelay,
long: TimeInterval = Const.longAutosearchDelay,
callback: @escaping () -> Void)
{
shortInterval = short
longInterval = long
self.callback = callback
}
func activate() {
shortTimer?.invalidate()
shortTimer = Timer.scheduledTimer(withTimeInterval: shortInterval, repeats: false)
{ [weak self] _ in self?.fire() }
if longTimer == nil {
longTimer = Timer.scheduledTimer(withTimeInterval: longInterval, repeats: false)
{ [weak self] _ in self?.fire() }
}
}
func cancel() {
shortTimer?.invalidate()
longTimer?.invalidate()
shortTimer = nil; longTimer = nil
}
private func fire() {
cancel()
callback()
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
19376 次 |
| 最近记录: |