1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SwiftUIで長いリストの検索を軽くする:debounceとフィルタ結果のキャッシュ

1
Posted at

個人開発のiOSアプリで、1,000件ほどのレイアウトデータ(ゲームの配置図)を検索・絞り込みする画面を作りました。最初は TextField の入力ごとに全件フィルタしていたので、文字を打つたびにスクロールがカクつきました。やったことは2つだけです。

1. 入力をdebounceする

@MainActor
final class SearchModel: ObservableObject {
    @Published var query = ""
    @Published private(set) var results: [Layout] = []
    private var task: Task<Void, Never>?
    private let all: [Layout]

    init(all: [Layout]) { self.all = all; self.results = all }

    func queryChanged() {
        task?.cancel()
        let q = query
        task = Task {
            try? await Task.sleep(for: .milliseconds(250))
            guard !Task.isCancelled else { return }
            results = filter(q)
        }
    }
}

View側は .onChange(of: model.query) { model.queryChanged() } で呼ぶだけです。250msにすると、普通に打っている間はフィルタが走りません。

2. 絞り込み結果をキーごとにキャッシュする

カテゴリ(レベル×用途)の組み合わせは数十通りしかないので、一度計算した結果を辞書に持っておきます。

private var cache: [String: [Layout]] = [:]

func filter(_ q: String) -> [Layout] {
    let key = q.lowercased()
    if let hit = cache[key] { return hit }
    let r = key.isEmpty ? all : all.filter { $0.searchText.contains(key) }
    cache[key] = r
    return r
}

searchText は起動時にタイトルとタグを小文字で連結しておいた文字列です。毎回 lowercased() しないだけでもかなり違いました。

結果

iPhone 12で、入力中のカクつきはほぼ体感できなくなりました。データが更新されたら cache.removeAll() を忘れずに。もっと良いやり方があればコメントで教えてください。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?