diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 352b01a78c..73aec7fa5a 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -59,12 +59,12 @@ struct ChatListView: View { destination: chatView ) { chatListView } } - SheetRepresentable(isPresented: $userPickerShown) { + } + .modifier( + Sheet(isPresented: $userPickerShown) { UserPicker(userPickerShown: $userPickerShown, activeSheet: $activeUserPickerSheet) } - .allowsHitTesting(userPickerShown) - .ignoresSafeArea() - } + ) .sheet(item: $activeUserPickerSheet) { sheet in if let currentUser = chatModel.currentUser { switch sheet { diff --git a/apps/ios/Shared/Views/ChatList/UserPicker.swift b/apps/ios/Shared/Views/ChatList/UserPicker.swift index 8ecca48331..371662cb6c 100644 --- a/apps/ios/Shared/Views/ChatList/UserPicker.swift +++ b/apps/ios/Shared/Views/ChatList/UserPicker.swift @@ -17,6 +17,7 @@ struct UserPicker: View { @State private var currentUser: Int64? @State private var switchingProfile = false @State private var frameWidth: CGFloat = 0 + @State private var resetScroll = ResetScrollAction() // Inset grouped list dimensions private let imageSize: CGFloat = 44 @@ -34,7 +35,7 @@ struct UserPicker: View { let currentUserWidth = max(frameWidth - sectionHorizontalPadding - rowPadding * 2 - 14 - imageSize, 0) VStack(spacing: sectionSpacing) { if let user = m.currentUser { - StickyScrollView { + StickyScrollView(resetScroll: $resetScroll) { HStack(spacing: rowPadding) { HStack { ProfileImage(imageStr: user.image, size: imageSize, color: Color(uiColor: .tertiarySystemGroupedBackground)) @@ -43,7 +44,7 @@ struct UserPicker: View { } .padding(rowPadding) .frame(width: otherUsers.isEmpty ? sectionWidth : currentUserWidth, alignment: .leading) - .background(elevatedSecondarySystemGroupedBackground(colorScheme)) + .background(elevatedSecondarySystemGroupedBackground) .clipShape(sectionShape) .onTapGesture { activeSheet = .currentProfile } ForEach(otherUsers) { u in @@ -88,7 +89,7 @@ struct UserPicker: View { } } } - .background(elevatedSecondarySystemGroupedBackground(colorScheme)) + .background(elevatedSecondarySystemGroupedBackground) .clipShape(sectionShape) .padding(.horizontal, sectionHorizontalPadding) .padding(.bottom, sectionSpacing) @@ -110,10 +111,20 @@ struct UserPicker: View { } } } -// .modifier(ThemedBackground(grouped: true)) + .onChange(of: userPickerShown) { + if !$0 { resetScroll() } + } + .modifier(ThemedBackground(grouped: true)) .disabled(switchingProfile) } + var elevatedSecondarySystemGroupedBackground: Color { + switch colorScheme { + case .dark: Color(0xFF2C2C2E) + default: Color(0xFFFFFFFF) + } + } + private var listDivider: some View { Divider().padding(.leading, 52) } @@ -130,11 +141,10 @@ struct UserPicker: View { Text(u.user.displayName).font(.title2).lineLimit(1) } .padding(rowPadding) - .background(elevatedSecondarySystemGroupedBackground(colorScheme)) + .background(elevatedSecondarySystemGroupedBackground) .clipShape(sectionShape) .onTapGesture { switchingProfile = true - Task { do { try await changeActiveUserAsync_(u.user.userId, viewPwd: nil) diff --git a/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift b/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift index 3c3f6334ad..fb5bb980e1 100644 --- a/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift +++ b/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift @@ -10,17 +10,31 @@ import SwiftUI private let sheetAnimationDuration: Double = 0.3 +struct Sheet: ViewModifier { + @Binding var isPresented: Bool + @ViewBuilder let sheetContent: () -> SheetContent + + func body(content: Content) -> some View { + ZStack { + content + SheetRepresentable(isPresented: $isPresented, content: sheetContent()) + .allowsHitTesting(isPresented) + .ignoresSafeArea() + } + } +} + struct SheetRepresentable: UIViewControllerRepresentable { @Binding var isPresented: Bool - @ViewBuilder let content: () -> Content + let content: Content func makeUIViewController(context: Context) -> Controller { - Controller(content: content(), representer: self) + Controller(content: content, representer: self) } func updateUIViewController(_ sheetController: Controller, context: Context) { sheetController.animate(isPresented: isPresented) - sheetController.hostingController.rootView = content() + sheetController.hostingController.rootView = content } class Controller: UIViewController { @@ -55,14 +69,7 @@ struct SheetRepresentable: UIViewControllerRepresentable { addChild(hostingController) hostingController.didMove(toParent: self) if let sheet = hostingController.view { - sheet.backgroundColor = UIColor { traits in - let elevated = switch traits.userInterfaceStyle { - case .dark: elevatedSystemGroupedBackground(.dark) - default: elevatedSystemGroupedBackground(.light) - } - return elevated.cgColor.map { UIColor(cgColor: $0) } - ?? .secondarySystemBackground - } + sheet.clipsToBounds = true sheet.layer.cornerRadius = 10 sheet.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] sheet.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(pan(gesture:)))) @@ -117,63 +124,3 @@ struct SheetRepresentable: UIViewControllerRepresentable { } } } - -// MARK: Sheet Colors - -func elevatedSystemGroupedBackground(_ colorScheme: ColorScheme) -> Color { - switch colorScheme { - case .dark: Color(0xFF1C1C1E) - default: Color(0xFFF2F2F7) - } -} - -func elevatedSecondarySystemGroupedBackground(_ colorScheme: ColorScheme) -> Color { - switch colorScheme { - case .dark: Color(0xFF2C2C2E) - default: Color(0xFFFFFFFF) - } -} - -/// # Extracting Sheet Colors Programatically -/// -/// System colors are returned dynamically, depending on the context: -/// -/// struct ColorResolverView: View { -/// @Environment(\.self) var environment -/// let colors: [Color] -/// -/// var body: some View { -/// HStack { -/// column.environment(\.colorScheme, .dark) -/// column.environment(\.colorScheme, .light) -/// } -/// } -/// -/// var column: some View { -/// VStack { -/// ForEach(colors, id: \.self) { -/// Text("\($0.resolve(in: environment))") -/// .frame(maxWidth: .infinity, maxHeight: .infinity) -/// .background($0) -/// } -/// } -/// } -/// } -/// -/// Place `ColorResolverView` inside a sheet to acquire elevated color versions: -/// -/// struct ContentView: View { -/// var body: some View { -/// EmptyView() -/// .sheet(isPresented: .constant(true)) { -/// ColorResolverView( -/// colors: [ -/// Color(.systemGroupedBackground), -/// Color(.secondarySystemGroupedBackground) -/// ] -/// ) -/// } -/// } -/// } - - diff --git a/apps/ios/Shared/Views/Helpers/StickyScrollView.swift b/apps/ios/Shared/Views/Helpers/StickyScrollView.swift index 71bee4f548..5799962778 100644 --- a/apps/ios/Shared/Views/Helpers/StickyScrollView.swift +++ b/apps/ios/Shared/Views/Helpers/StickyScrollView.swift @@ -9,6 +9,7 @@ import SwiftUI struct StickyScrollView: UIViewRepresentable { + @Binding var resetScroll: ResetScrollAction @ViewBuilder let content: () -> Content func makeUIView(context: Context) -> UIScrollView { @@ -18,6 +19,9 @@ struct StickyScrollView: UIViewRepresentable { sv.showsHorizontalScrollIndicator = false sv.addSubview(hc.view) sv.delegate = context.coordinator + DispatchQueue.main.async { + resetScroll = ResetScrollAction { sv.setContentOffset(.zero, animated: false) } + } return sv } @@ -50,3 +54,8 @@ struct StickyScrollView: UIViewRepresentable { } } } + +struct ResetScrollAction { + var action = { } + func callAsFunction() { action() } +}