【问题标题】:How to make a pull down search bar in SwiftUI如何在 SwiftUI 中制作下拉搜索栏
【发布时间】:2021-05-21 01:58:01
【问题描述】:

我想在我的应用中使用 SwiftUI 制作下拉搜索栏,并且必须与 iOS13 兼容,所以我无法使用 ScrollViewReader。我尝试使用Introspect,但我不明白它的作用和工作原理,因为除了下拉刷新之外,文档没有解释如何为 ScrollView 做一些事情。我使用 Introspect for TextField 成为并退出 FirstResponder,它可以工作,但是当我使用 introspect for scrollview 时,该函数仅在滚动视图首次出现时运行。

@State private var willSearch = false

...

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

这就是我的 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

我也尝试通过关注this suggestion 来检测滚动,但效果不是很好,而且我可以听到我的 MacBook 风扇转动的声音比平时大,所以它可能会影响性能。

这大致就是我希望它的行为Pull down search bar as seen in Telegram

【问题讨论】:

    标签: ios swift swiftui uikit introspection


    【解决方案1】:

    我更喜欢使用UIKit 来实现应用内导航,并且只使用SwiftUI 来构建屏幕UI。所以我使用UIHostingController + UINavigationController 并且可以轻松集成UISearchController。比如这样:

    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)
            }
        }
    }
    

    警告:您必须禁用 Main.storyboard (https://sarunw.com/posts/how-to-create-new-xcode-project-without-storyboard/) 并手动加载根控制器。比如这样:

    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
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多