SwiftUI搜索栏动画

huangapple go评论63阅读模式
英文:

SwiftUI Search Bar Animation

问题

这是我的WelcomeView.swift代码:

import SwiftUI

struct WelcomeView: View {
    @EnvironmentObject var locationManager: LocationManager
    @ObservedObject var locationSearch: LocationSearch
    @State private var isSearching = false
    
    var body: some View {
        
        VStack {
            
            if isSearching {
                LocationSearchView(locationSearch: locationSearch)
                    .zIndex(1)
//                    .background(Color.blue)
            } else {
                VStack(spacing: 10) {
                    Text("欢迎使用应用!")
                        .bold().font(.largeTitle)
                    
                    Text("Blah Blah Blah Blah Blah Blah")
                        .font(.title)
                    
                }
                .multilineTextAlignment(.center)
                .padding()
                
                VStack() {
                    Text("Blah Blah Blah Blah Blah")
                    Text("Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah")
                }
                .padding()
                .font(.headline)
                
                ZStack {
                    
                    VStack {
                        TextField("搜索", text: $locationSearch.queryFragment, onEditingChanged: { isEditing in isSearching = isEditing
                        })
                        .padding(.horizontal)
                        .padding(.vertical, 8)
                        .frame(width: 535, height: 70)
                        .background(.thinMaterial)
                        .cornerRadius(10)
        
                        Button(action: {
                            locationManager.requestLocation()
                        }, label: {
                            HStack {
                                Image(systemName: "location.fill")
                                Text("允许位置服务")
                                    .font(.title)
                            }
                            .frame(width: 500, height: 70)
                        })
                    }
                    .zIndex(0)
                }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

这是LocationSearch.swift代码:

import Foundation
import Combine
import MapKit

class LocationSearch: NSObject, ObservableObject {
    
    enum LocationStatus: Equatable {
        case idle
        case noResults
        case isSearching
        case error(String)
        case result
    }
    
    @Published var queryFragment: String = ""
    @Published private(set) var status: LocationStatus = .idle
    @Published private(set) var searchResults: [MKLocalSearchCompletion] = []
    
    private var queryCancellable: AnyCancellable?
    private let searchCompleter: MKLocalSearchCompleter!
    
    init(searchCompleter: MKLocalSearchCompleter = MKLocalSearchCompleter()) {
        self.searchCompleter = searchCompleter
        super.init()
        self.searchCompleter.delegate = self
        
        queryCancellable = $queryFragment
            .receive(on: DispatchQueue.main)
            .debounce(for: .milliseconds(250), scheduler: RunLoop.main, options: nil)
            .sink(receiveValue: { fragment in
                self.status = .isSearching
                if !fragment.isEmpty {
                    self.searchCompleter.queryFragment = fragment
                } else {
                    self.status = .idle
                    self.searchResults = []
                }
        })
    }
}

extension LocationSearch: MKLocalSearchCompleterDelegate {
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        self.searchResults = completer.results.filter({ $0.subtitle == "" })
        self.status = completer.results.isEmpty ? .noResults : .result
    }
    
    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
        self.status = .error(error.localizedDescription)
    }
}

这是我希望它如何运行的gif

在这个GIF中,点击搜索栏后,搜索栏平滑地移动到屏幕顶部,淡化其他内容,并弹出键盘。然后用户可以开始输入,结果将显示出来。

我已经完成了所有的搜索逻辑,但我无法让UI在点击搜索栏时以我设想的方式反应。

英文:

I have a search bar in SwiftUi and it doesn't react the way I am wanting it to. I have gone through a few attempts at using .overlay and .zIndex but that did not work as intended. It added the SearchView, but it still displayed the other text. Here's the code I have currently. What I'm intending to do is search for a location using MapKit, which I have working. I included the code for that file in case it's needed. I just cannot get the search bar to react the way I want.

This is my WelcomeView.Swift code:

import SwiftUI

struct WelcomeView: View {
    @EnvironmentObject var locationManager: LocationManager
    @ObservedObject var locationSearch: LocationSearch
    @State private var isSearching = false
    
    var body: some View {
        
        VStack {
            
            if isSearching {
                LocationSearchView(locationSearch: locationSearch)
                    .zIndex(1)
//                    .background(Color.blue)
            } else {
                VStack(spacing: 10) {
                    Text("Welcome to App!")
                        .bold().font(.largeTitle)
                    
                    Text("Blah Blah Blah Blah Blah Blah")
                        .font(.title)
                    
                }
                .multilineTextAlignment(.center)
                .padding()
                
                VStack() {
                    Text("Blah Blah Blah Blah Blah")
                    Text("Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah")
                }
                .padding()
                .font(.headline)
                
                ZStack {
                    
                    VStack {
                        TextField("Search", text: $locationSearch.queryFragment, onEditingChanged: { isEditing in isSearching = isEditing
                        })
                        .padding(.horizontal)
                        .padding(.vertical, 8)
                        .frame(width: 535, height: 70)
                        .background(.thinMaterial)
                        .cornerRadius(10)
        
                        Button(action: {
                            locationManager.requestLocation()
                        }, label: {
                            HStack {
                                Image(systemName: "location.fill")
                                Text("Allow Location Services")
                                    .font(.title)
                            }
                            .frame(width: 500, height: 70)
                        })
                    }
                    .zIndex(0)
                }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

}

This is the LocationSearch.swift code:

import Foundation
import Combine
import MapKit

class LocationSearch: NSObject, ObservableObject {
    
    enum LocationStatus: Equatable {
        case idle
        case noResults
        case isSearching
        case error(String)
        case result
    }
    
    @Published var queryFragment: String = ""
    @Published private(set) var status: LocationStatus = .idle
    @Published private(set) var searchResults: [MKLocalSearchCompletion] = []
    
    private var queryCancellable: AnyCancellable?
    private let searchCompleter: MKLocalSearchCompleter!
    
    init(searchCompleter: MKLocalSearchCompleter = MKLocalSearchCompleter()) {
        self.searchCompleter = searchCompleter
        super.init()
        self.searchCompleter.delegate = self
        
        queryCancellable = $queryFragment
            .receive(on: DispatchQueue.main)
            .debounce(for: .milliseconds(250), scheduler: RunLoop.main, options: nil)
            .sink(receiveValue: { fragment in
                self.status = .isSearching
                if !fragment.isEmpty {
                    self.searchCompleter.queryFragment = fragment
                } else {
                    self.status = .idle
                    self.searchResults = []
                }
        })
    }
}

extension LocationSearch: MKLocalSearchCompleterDelegate {
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        self.searchResults = completer.results.filter({ $0.subtitle == "" })
        self.status = completer.results.isEmpty ? .noResults : .result
    }
    
    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
        self.status = .error(error.localizedDescription)
    }
}

This is a gif of how I am wanting it to function

In this GIF, upon tapping the search bar, the search bar moves smoothly to the top of the screen, fades everything else out, and brings up the keyboard. Then the user can start typing and results will display.

I have all of the search logic working but I can't get the UI to react in the way I am envisioning when tapping the search bar.

答案1

得分: 1

ObservableObjects are a thing of the past. When programming for visionOS you should be using @Observable.

ObservableObjects have always been inefficient and terrible with animation because they cause a complete redraw instead of a targeted redraw.

Apple has completely changed how models work.

英文:

ObservableObjects are a thing of the past. When programming for visionOS you should be using @Observable

https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro

ObservableObjects have always been inefficient and terrible with animation because they cause a complete redraw instead of a targeted redraw.

Apple as completely changed how models work.

https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

huangapple
  • 本文由 发表于 2023年6月26日 15:20:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76554370.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定