added argument to voice over on the search bar automatically

- also removed the geomtry reader in favour of a `readWidth` function
This commit is contained in:
Mauro Romito
2025-05-08 16:26:33 +02:00
committed by Mauro
parent 489f784640
commit 36242bc271
3 changed files with 52 additions and 28 deletions
@@ -31,4 +31,15 @@ extension View {
height.wrappedValue = newValue
}
}
/// Reads the width of the view and stores it in the `width` binding.
/// - Parameters:
/// - width: a `CGFloat` binding
func readWidth(_ width: Binding<CGFloat>) -> some View {
onGeometryChange(for: CGFloat.self) { geometry in
geometry.size.width
} action: { newValue in
width.wrappedValue = newValue
}
}
}
+15 -4
View File
@@ -28,12 +28,14 @@ extension View {
placeholder: String? = nil,
hidesNavigationBar: Bool = false,
showsCancelButton: Bool = true,
disablesInteractiveDismiss: Bool = false) -> some View {
disablesInteractiveDismiss: Bool = false,
accessibilityFocusOnStart: Bool = false) -> some View {
modifier(SearchControllerModifier(searchQuery: query,
placeholder: placeholder,
hidesNavigationBar: hidesNavigationBar,
showsCancelButton: showsCancelButton,
disablesInteractiveDismiss: disablesInteractiveDismiss))
disablesInteractiveDismiss: disablesInteractiveDismiss,
accessibilityFocusOnStart: accessibilityFocusOnStart))
}
}
@@ -44,6 +46,7 @@ private struct SearchControllerModifier: ViewModifier {
let hidesNavigationBar: Bool
let showsCancelButton: Bool
let disablesInteractiveDismiss: Bool
let accessibilityFocusOnStart: Bool
/// Whether or not the user is currently searching. When ``automaticallyShowsCancelButton``
/// is `false`, checking if this value is `false` is pretty much meaningless.
@@ -58,6 +61,7 @@ private struct SearchControllerModifier: ViewModifier {
hidesNavigationBar: hidesNavigationBar,
showsCancelButton: showsCancelButton,
hidesSearchBarWhenScrolling: false,
accessibilityFocusOnStart: accessibilityFocusOnStart,
isSearching: $isSearching)
}
.onDisappear {
@@ -76,12 +80,19 @@ private struct SearchController: UIViewControllerRepresentable {
let hidesNavigationBar: Bool
let showsCancelButton: Bool
let hidesSearchBarWhenScrolling: Bool
let accessibilityFocusOnStart: Bool
@Binding var isSearching: Bool
func makeUIViewController(context: Context) -> SearchInjectionViewController {
SearchInjectionViewController(searchController: context.coordinator.searchController,
hidesSearchBarWhenScrolling: hidesSearchBarWhenScrolling)
let controller = SearchInjectionViewController(searchController: context.coordinator.searchController,
hidesSearchBarWhenScrolling: hidesSearchBarWhenScrolling)
if accessibilityFocusOnStart {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
UIAccessibility.post(notification: .screenChanged, argument: controller.searchController.searchBar)
}
}
return controller
}
func updateUIViewController(_ viewController: SearchInjectionViewController, context: Context) {
@@ -11,6 +11,8 @@ import SwiftUI
struct InviteUsersScreen: View {
@ObservedObject var context: InviteUsersScreenViewModel.Context
@State private var formWidth = CGFloat.zero
var showTopSection: Bool {
!context.viewState.selectedUsers.isEmpty || context.viewState.isSearching
}
@@ -25,7 +27,8 @@ struct InviteUsersScreen: View {
.searchController(query: $context.searchQuery,
placeholder: L10n.commonSearchForSomeone,
showsCancelButton: false,
disablesInteractiveDismiss: true)
disablesInteractiveDismiss: true,
accessibilityFocusOnStart: true)
.compoundSearchField()
.alert(item: $context.alertInfo)
}
@@ -33,34 +36,33 @@ struct InviteUsersScreen: View {
// MARK: - Private
private var mainContent: some View {
GeometryReader { proxy in
Form {
if showTopSection {
// this is a fix for having the carousel not clipped, and inside the form, so when the search is dismissed, it wont break the design
Section {
EmptyView()
} header: {
VStack(spacing: 16) {
selectedUsersSection
.textCase(.none)
.frame(width: proxy.size.width)
if context.viewState.isSearching {
ProgressView()
.frame(maxWidth: .infinity, alignment: .center)
.listRowBackground(Color.clear)
}
Form {
if showTopSection {
// this is a fix for having the carousel not clipped, and inside the form, so when the search is dismissed, it wont break the design
Section {
EmptyView()
} header: {
VStack(spacing: 16) {
selectedUsersSection
.textCase(.none)
.frame(width: formWidth)
if context.viewState.isSearching {
ProgressView()
.frame(maxWidth: .infinity, alignment: .center)
.listRowBackground(Color.clear)
}
}
}
if context.viewState.hasEmptySearchResults {
noResultsContent
} else {
usersSection
}
}
if context.viewState.hasEmptySearchResults {
noResultsContent
} else {
usersSection
}
}
.readWidth($formWidth)
}
private var noResultsContent: some View {