简体   繁体   English

如何在 SwiftUI 中制作下拉搜索栏

[英]How to make a pull down search bar in SwiftUI

I want to make a pull down search bar in my app using SwiftUI and have to be compatible with iOS13, so I couldn't use ScrollViewReader.我想使用 SwiftUI 在我的应用程序中创建一个下拉搜索栏,并且必须与 iOS13 兼容,所以我不能使用 ScrollViewReader。 I tried using Introspect , but I didn't understand what it does and how it works, because the documentation doesn't explain how to do stuff for ScrollView other than pull down to refresh.我尝试使用Introspect ,但我不明白它的作用和工作原理,因为除了下拉刷新之外,文档没有解释如何为 ScrollView 做一些事情。 I used introspect for TextField to become and resign FirstResponder, and it works, but when I use introspect for scrollview, the function only runs when the scroll view first appeared.我使用 Introspect for TextField 成为并退出 FirstResponder,它可以工作,但是当我使用 introspect for scrollview 时,function 仅在滚动视图首次出现时运行。

@State private var willSearch = false

...

.introspectTextField{ textfield in
    textfield.returnKeyType = .done
    if willSearch {
        textfield.becomeFirstResponder()
    }else{
        textfield.resignFirstResponder()
    }
}
// this works when `willSearch` changed value

This is how my ScrollView introspection looks like这就是我的 ScrollView 内省的样子

extension UIScrollView {
    var isBouncing: Bool {
        return (contentOffset.y + contentInset.top < 0)
    }
}

...

.introspectScrollView { scroll in
    if scroll.isDragging{
        self.willSearch = scroll.isBouncing;
    }
}
// this doesn't work

I also tried detecting scroll by following this suggestion , but it's not very effective, and I can hear my MacBook fan spinning louder than normal, so it probably will impact the performance.我也尝试按照这个建议检测滚动,但它不是很有效,而且我可以听到我的 MacBook 风扇旋转的声音比平时大,所以它可能会影响性能。

This is roughly how I want it to behave Pull down search bar as seen in Telegram这大致就是我希望它的行为方式下拉搜索栏,如 Telegram 中所示

I prefer to use UIKit to implement inapp navigation and only use SwiftUI to build screen UI.我更喜欢使用UIKit来实现应用内导航,只使用SwiftUI来构建屏幕 UI。 So I work with UIHostingController + UINavigationController and can easily integrate UISearchController .所以我使用UIHostingController + UINavigationController并且可以轻松集成UISearchController For example like this:例如像这样:

import UIKit
import SwiftUI

class ViewController: UIHostingController<Content>, UISearchResultsUpdating {

    private let viewModel = ViewModel()

    init() {
        super.init(rootView: Content(viewModel: viewModel))
        title = "Test"
    }

    @objc required dynamic init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let searchController = UISearchController(searchResultsController: nil)
        searchController.searchResultsUpdater = self
        self.navigationItem.searchController = searchController
        navigationController?.view.setNeedsLayout() // this is needed for iOS 13 only
    }

    func updateSearchResults(for searchController: UISearchController) {
        viewModel.searchDidChange(searchController.searchBar.text ?? "")
    }

}

struct Content: View {
    @ObservedObject var viewModel: ViewModel
    var body: some View {
        ScrollView {
            VStack {
                ForEach(viewModel.items, id: \.self) { item in
                    Text(item)
                        .frame(maxWidth: .infinity)
                }
            }
        }
    }
}

class ViewModel: ObservableObject {
    @Published var items: [String]
    private let allItems: [String]
    init() {
        self.allItems = (0..<100).map { "\($0)" }
        self.items = allItems
    }

    func searchDidChange(_ text: String) {
        items = allItems.filter {
            $0.starts(with: text)
        }
    }
}

WARNING: you have to disable Main.storyboard ( https://sarunw.com/posts/how-to-create-new-xcode-project-without-storyboard/ ) and load root controller manually.警告:您必须手动禁用Main.storyboard ( https://sarunw.com/posts/how-to-create-new-xcode-project-without-storyboard/ ) 并手动加载根 Z594C103F52C6E04C31AZ8 For example like this:例如像这样:

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: scene)
        window.rootViewController = UINavigationController(rootViewController: ViewController())
        window.makeKeyAndVisible()
        self.window = window
    }

}

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM