* Fix the search text field's tint colour. * Don't allow optional content IDs in the placeholder avatar. * Use SwiftUI to resolve the hex values in the Inspector app. This fixes incorrect values being shown in dark/high-contrast modes. * Fix a layout bug with the colour swatch in the Inspector app on iPhone. * Switch to the chats tab when selecting a room with the global search screen. * Run the latest SwiftFormat.
99 lines
4.1 KiB
Swift
99 lines
4.1 KiB
Swift
//
|
|
// Copyright 2023, 2024 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
import SwiftUI
|
|
import SwiftUIIntrospect
|
|
|
|
public extension View {
|
|
/// Styles a search bar text field using the Compound design tokens.
|
|
/// This modifier is to be used in combination with `.searchable`.
|
|
@MainActor
|
|
@ViewBuilder
|
|
func compoundSearchField() -> some View {
|
|
introspect(.navigationStack, on: .supportedVersions, scope: .ancestor) { navigationController in
|
|
// Uses the navigation stack as .searchField is unreliable when pushing the second search bar, during the create rooms flow.
|
|
guard let searchController = navigationController.navigationBar.topItem?.searchController else { return }
|
|
|
|
// Ported from Riot iOS as this is the only reliable way to get the exact look we want.
|
|
// However this is fragile and tied to gutwrenching the current UISearchBar internals.
|
|
|
|
let searchTextField = searchController.searchBar.searchTextField
|
|
searchTextField.tintColor = .compound.iconAccentTertiary
|
|
|
|
if #unavailable(iOS 26.0) {
|
|
let placeholderColor = UIColor.compound.textSecondary
|
|
|
|
// Magnifying glass icon.
|
|
let leftImageView = searchTextField.leftView as? UIImageView
|
|
leftImageView?.tintColor = placeholderColor
|
|
// Placeholder text.
|
|
let placeholderLabel = searchTextField.value(forKey: "placeholderLabel") as? UILabel
|
|
placeholderLabel?.textColor = placeholderColor
|
|
// Clear button.
|
|
let clearButton = searchTextField.value(forKey: "clearButton") as? UIButton
|
|
let buttonImage = clearButton?.image(for: .normal)?.withRenderingMode(.alwaysTemplate)
|
|
clearButton?.setImage(buttonImage, for: .normal)
|
|
clearButton?.tintColor = placeholderColor
|
|
|
|
// Text field.
|
|
searchTextField.textColor = .compound.textPrimary
|
|
searchTextField.backgroundColor = .compound._bgSubtleSecondaryAlpha
|
|
|
|
// Hide the effect views so we can use the rounded rect style without any materials.
|
|
let effectBackgroundTop = searchTextField.value(forKey: "_effectBackgroundTop") as? UIView
|
|
effectBackgroundTop?.isHidden = true
|
|
let effectBackgroundBottom = searchTextField.value(forKey: "_effectBackgroundBottom") as? UIView
|
|
effectBackgroundBottom?.isHidden = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Previews
|
|
|
|
struct SearchStyle_Previews: PreviewProvider, TestablePreview {
|
|
static var previews: some View {
|
|
NavigationStack {
|
|
List {
|
|
ForEach(0..<10, id: \.self) { index in
|
|
Text("Item \(index)")
|
|
}
|
|
}
|
|
.listStyle(.plain)
|
|
.searchable(text: .constant(""), placement: .navigationBarDrawer(displayMode: .always))
|
|
.compoundSearchField()
|
|
}
|
|
.tint(.compound.textActionPrimary)
|
|
.previewDisplayName("List")
|
|
|
|
NavigationStack {
|
|
Form {
|
|
Section {
|
|
ListRow(label: .plain(title: "Some row"),
|
|
kind: .button { })
|
|
} header: {
|
|
Text("Settings")
|
|
.compoundListSectionHeader()
|
|
}
|
|
|
|
Section {
|
|
ListRow(label: .plain(title: "Some setting"),
|
|
kind: .toggle(.constant(true)))
|
|
} header: {
|
|
Text("More Settings")
|
|
.compoundListSectionHeader()
|
|
}
|
|
}
|
|
.compoundList()
|
|
.searchable(text: .constant(""), placement: .navigationBarDrawer(displayMode: .always))
|
|
.compoundSearchField()
|
|
}
|
|
.tint(.compound.textActionPrimary)
|
|
.previewDisplayName("Form")
|
|
}
|
|
}
|