reset user scroll position on dismiss; cleanup

This commit is contained in:
Levitating Pineapple
2024-09-25 23:42:19 +03:00
parent de594aac88
commit cabf8aba16
4 changed files with 47 additions and 81 deletions
@@ -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 {
@@ -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)
@@ -10,17 +10,31 @@ import SwiftUI
private let sheetAnimationDuration: Double = 0.3
struct Sheet<SheetContent: View>: 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<Content: View>: UIViewControllerRepresentable {
@Binding var isPresented: Bool
@ViewBuilder let content: () -> Content
let content: Content
func makeUIViewController(context: Context) -> Controller<Content> {
Controller(content: content(), representer: self)
Controller(content: content, representer: self)
}
func updateUIViewController(_ sheetController: Controller<Content>, context: Context) {
sheetController.animate(isPresented: isPresented)
sheetController.hostingController.rootView = content()
sheetController.hostingController.rootView = content
}
class Controller<C: View>: UIViewController {
@@ -55,14 +69,7 @@ struct SheetRepresentable<Content: View>: 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<Content: View>: 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)
/// ]
/// )
/// }
/// }
/// }
@@ -9,6 +9,7 @@
import SwiftUI
struct StickyScrollView<Content: View>: UIViewRepresentable {
@Binding var resetScroll: ResetScrollAction
@ViewBuilder let content: () -> Content
func makeUIView(context: Context) -> UIScrollView {
@@ -18,6 +19,9 @@ struct StickyScrollView<Content: View>: 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<Content: View>: UIViewRepresentable {
}
}
}
struct ResetScrollAction {
var action = { }
func callAsFunction() { action() }
}