闽公网安备 35020302035485号
struct LiquidGlassSearchBar: View {
@Stateprivatevar searchText = ""
@Stateprivatevar isSearching = false
@FocusStateprivatevar isFocused: Bool
var body: some View {
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.secondary)
TextField("搜索...", text: $searchText)
.focused($isFocused)
.onTapGesture {
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
isSearching = true
}
}
}
.padding(.horizontal, 15)
.padding(.vertical, 12)
.background(
RoundedRectangle(cornerRadius: isSearching ? 25 : 15)
.fill(.ultraThinMaterial)
.glassEffect(.regular.tint(.blue.opacity(0.1)))
)
.scaleEffect(isSearching ? 1.05 : 1.0)
.animation(.spring(response: 0.4, dampingFraction: 0.7), value: isSearching)
.onChange(of: isFocused) { focused in
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
isSearching = focused
}
}
}
}

struct SearchResult: Identifiable, Equatable {
let id: UUID
let title: String
let subtitle: String
let imageURL: URL
}
struct LiquidGlassSearchResultsView: View {
@Stateprivatevar searchResults: [SearchResult] = []
@Namespaceprivatevar namespace
init(searchResults: [SearchResult] = []) {
self._searchResults = State(initialValue: searchResults)
}
var body: some View {
GlassEffectContainer(spacing: 8) {
ForEach(searchResults) { result in
SearchResultCard(result: result)
.glassEffect(.regular.tint(.white.opacity(0.1)).interactive())
.glassEffectID(result.id, in: namespace)
.transition(.asymmetric(
insertion: .move(edge: .trailing).combined(with: .opacity),
removal: .move(edge: .leading).combined(with: .opacity)
))
}
}
.animation(.spring(response: 0.6, dampingFraction: 0.8), value: searchResults)
}
}
struct SearchResultCard: View {
let result: SearchResult
var body: some View {
HStack {
AsyncImage(url: result.imageURL) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
RoundedRectangle(cornerRadius: 8)
.fill(.secondary.opacity(0.3))
}
.frame(width: 50, height: 50)
.clipped()
.cornerRadius(8)
VStack(alignment: .leading, spacing: 4) {
Text(result.title)
.font(.headline)
Text(result.subtitle)
.font(.subheadline)
.foregroundColor(.secondary)
.lineLimit(2)
}
Spacer()
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
}
}

struct LiquidGlassSearchSuggestions: View {
@Stateprivatevar suggestions: [String]
@Stateprivatevar selectedSuggestion: String?
init(suggestions: [String], selectedSuggestion: String? = nil) {
self.suggestions = suggestions
self.selectedSuggestion = selectedSuggestion
}
var body: some View {
LazyVStack(spacing: 0) {
ForEach(suggestions, id: \.self) { suggestion in
Button(action: {
withAnimation(.spring(response: 0.5, dampingFraction: 0.8)) {
selectedSuggestion = suggestion
}
}) {
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.secondary)
Text(suggestion)
.font(.body)
Spacer()
Image(systemName: "arrow.up.left")
.foregroundColor(.secondary)
.font(.caption)
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
}
}
}
.background(
RoundedRectangle(cornerRadius: 16)
.fill(.regularMaterial)
.glassEffect(.regular.tint(.white.opacity(0.1)))
)
}
}

struct HistoryItemView: View {
let text: String
init(text: String) {
self.text = text
}
var body: some View {
HStack {
Image(systemName: "clock")
.foregroundColor(.secondary)
Text(text)
.font(.body)
.lineLimit(1)
Spacer()
Image(systemName: "xmark.circle")
.foregroundColor(.red)
.onTapGesture {
// Handle delete action
print("Delete history item: \(text)")
}
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
}
}
struct LiquidGlassSearchHistory: View {
@Stateprivatevar searchHistory: [String] = [
"SwiftUI 26",
"Liquid Glass 26",
"iOS 18",
]
@Namespaceprivatevar historyNamespace
var body: some View {
VStack(alignment: .leading, spacing: 12) {
HStack {
Text("搜索历史")
.font(.headline)
Spacer()
Button("清空") {
withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
searchHistory.removeAll()
}
}
.foregroundColor(.blue)
}
GlassEffectContainer(spacing: 6) {
ForEach(searchHistory, id: \.self) { historyItem in
HistoryItemView(text: historyItem)
.glassEffect(.regular.tint(.gray.opacity(0.1)).interactive())
.glassEffectID(historyItem, in: historyNamespace)
}
}
.animation(.spring(response: 0.5, dampingFraction: 0.8), value: searchHistory)
}
.padding()
.background(
RoundedRectangle(cornerRadius: 20)
.fill(.ultraThinMaterial)
.glassEffect(.regular.tint(.white.opacity(0.05)))
)
}
}

struct LiquidGlassSearchView: View {
@Stateprivatevar searchText = ""
@Stateprivatevar searchResults: [SearchResult] = []
@Stateprivatevar searchSuggestions: [String] = ["SwiftUI", "Liquid Glass", "Swift", "iOS", "macOS", "Xcode", "WWDC", "SwiftUI 26", "Liquid Glass 26"]
@FocusStateprivatevar isSearchFieldFocused: Bool
var body: some View {
NavigationView {
VStack {
// 搜索栏
searchBar
.padding()
// 根据状态显示不同内容
if isSearchFieldFocused && !searchSuggestions.isEmpty {
LiquidGlassSearchSuggestions(suggestions: searchSuggestions)
.padding(.horizontal, 16)
} elseif !searchResults.isEmpty {
LiquidGlassSearchResultsView(searchResults: searchResults)
.padding(.horizontal, 16)
} else {
LiquidGlassSearchHistory()
.padding(.horizontal, 16)
}
Spacer()
}
.background(
LinearGradient(
colors: [.blue.opacity(0.1), .purple.opacity(0.1)],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.navigationTitle("搜索")
.background(.yellow)
}
}
privatevar searchBar: some View {
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.secondary)
TextField("搜索内容...", text: $searchText)
.focused($isSearchFieldFocused)
.onSubmit {
performSearch()
}
if isSearchFieldFocused && !searchText.isEmpty {
Button("取消") {
searchText = ""
isSearchFieldFocused = false
}
.foregroundColor(.blue)
}
}
.padding()
.background(
RoundedRectangle(cornerRadius: isSearchFieldFocused ? 25 : 15)
.fill(.ultraThinMaterial)
.glassEffect(.regular.tint(.blue.opacity(0.1)))
)
.scaleEffect(isSearchFieldFocused ? 1.02 : 1.0)
.animation(.spring(response: 0.4, dampingFraction: 0.7), value: isSearchFieldFocused)
}
privatefunc performSearch() {
// 执行搜索逻辑
isSearchFieldFocused = false
// 模拟搜索结果
searchResults = [
SearchResult(id: UUID(), title: "SwiftUI 26", subtitle: "新特性和改进", imageURL: URL(string: "https://example.com/swiftui26.png")!),
SearchResult(id: UUID(), title: "Liquid Glass 26", subtitle: "探索液态玻璃效果", imageURL: URL(string: "https://example.com/liquidglass26.png")!)
]
}
}
主要就是把各个组件按需显示,重点是保持动画的流畅性。// 这样做比较好
GlassEffectContainer(spacing: 8) {
ForEach(items) { item in
ItemView(item: item)
.glassEffect(.regular.tint(.white.opacity(0.1)))
}
}
2. 动画要用 spring.animation(.spring(response: 0.5, dampingFraction: 0.8), value: isSearching)3. 兼容性问题
@available(iOS 26.0, *)
private var liquidGlassSearchBar: some View {
// Liquid Glass 实现
}
private var fallbackSearchBar: some View {
// 传统搜索框
}
适用场景.笔记、文档类 App
.spring 动画 - 让过渡更自然,像液体一样流畅