mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-26 20:34:50 +00:00
touchable list row; prevent tap gesture passtrough
This commit is contained in:
@@ -77,6 +77,7 @@ struct UserPicker: View {
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
.frame(maxWidth: 20, maxHeight: 20)
|
||||
.padding(.horizontal, rowPadding)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
if (colorScheme == .light) {
|
||||
ThemeManager.applyTheme(systemDarkThemeDefault.get())
|
||||
@@ -164,19 +165,15 @@ struct UserPicker: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func openSheetOnTap(_ icon: String, title: LocalizedStringKey, sheet: UserPickerSheet) -> some View {
|
||||
Button {
|
||||
activeSheet = sheet
|
||||
} label: {
|
||||
settingsRow(icon, color: theme.colors.secondary) {
|
||||
Text(title).foregroundColor(.primary)
|
||||
}
|
||||
settingsRow(icon, color: theme.colors.secondary) {
|
||||
Text(title).foregroundColor(.primary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, rowPadding)
|
||||
.padding(.vertical, rowVerticalPadding)
|
||||
.contentShape(Rectangle())
|
||||
.modifier(ListRow { activeSheet = sheet })
|
||||
}
|
||||
|
||||
private func unreadBadge(_ u: UserInfo) -> some View {
|
||||
@@ -191,6 +188,64 @@ struct UserPicker: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct ListRow: ViewModifier {
|
||||
@State private var touchDown = false
|
||||
let action: () -> Void
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
ZStack {
|
||||
Color(touchDown ? .systemGray4 : .clear)
|
||||
content
|
||||
TouchOverlay(touchDown: $touchDown, action: action)
|
||||
}
|
||||
}
|
||||
|
||||
struct TouchOverlay: UIViewRepresentable {
|
||||
@Binding var touchDown: Bool
|
||||
let action: () -> Void
|
||||
|
||||
func makeUIView(context: Context) -> TouchView {
|
||||
let touchView = TouchView()
|
||||
let gesture = UILongPressGestureRecognizer(
|
||||
target: touchView,
|
||||
action: #selector(touchView.longPress(gesture:))
|
||||
)
|
||||
gesture.delegate = touchView
|
||||
gesture.minimumPressDuration = 0.05
|
||||
touchView.addGestureRecognizer(gesture)
|
||||
return touchView
|
||||
}
|
||||
|
||||
func updateUIView(_ touchView: TouchView, context: Context) {
|
||||
touchView.representer = self
|
||||
}
|
||||
|
||||
class TouchView: UIView, UIGestureRecognizerDelegate {
|
||||
var representer: TouchOverlay?
|
||||
|
||||
@objc
|
||||
func longPress(gesture: UILongPressGestureRecognizer) {
|
||||
switch gesture.state {
|
||||
case .began:
|
||||
representer?.touchDown = true
|
||||
case .ended:
|
||||
representer?.touchDown = false
|
||||
if hitTest(gesture.location(in: self), with: nil) == self {
|
||||
representer?.action()
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
func gestureRecognizer(
|
||||
_: UIGestureRecognizer,
|
||||
shouldRecognizeSimultaneouslyWith: UIGestureRecognizer
|
||||
) -> Bool { true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct UserPicker_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
@State var activeSheet: UserPickerSheet?
|
||||
|
||||
@@ -42,6 +42,7 @@ struct SheetRepresentable<Content: View>: UIViewControllerRepresentable {
|
||||
private let animator = UIViewPropertyAnimator(duration: sheetAnimationDuration, curve: .easeInOut)
|
||||
private let representer: SheetRepresentable<C>
|
||||
private var retainedFraction: CGFloat = 0
|
||||
private var sheetHeight: Double { hostingController.view.frame.height }
|
||||
|
||||
init(content: C, representer: SheetRepresentable<C>) {
|
||||
self.representer = representer
|
||||
@@ -83,9 +84,11 @@ struct SheetRepresentable<Content: View>: UIViewControllerRepresentable {
|
||||
animator.pausesOnCompletion = true
|
||||
animator.scrubsLinearly = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
||||
self?.animator.addAnimations {
|
||||
sheet.transform = CGAffineTransform(translationX: 0, y: -sheet.frame.height)
|
||||
self?.view.backgroundColor = .black.withAlphaComponent(0.3)
|
||||
if let self {
|
||||
self.animator.addAnimations {
|
||||
sheet.transform = CGAffineTransform(translationX: 0, y: -self.sheetHeight)
|
||||
self.view.backgroundColor = .black.withAlphaComponent(0.3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,11 +102,11 @@ struct SheetRepresentable<Content: View>: UIViewControllerRepresentable {
|
||||
animator.pauseAnimation()
|
||||
retainedFraction = animator.fractionComplete
|
||||
case .changed:
|
||||
animator.fractionComplete = retainedFraction - gesture.translation(in: view).y / hostingController.view.frame.height
|
||||
animator.fractionComplete = retainedFraction - gesture.translation(in: view).y / sheetHeight
|
||||
case .ended, .cancelled:
|
||||
let velocity = gesture.velocity(in: view).y
|
||||
animator.isReversed = velocity.sign == .plus
|
||||
let defaultVelocity = hostingController.view.frame.height / sheetAnimationDuration
|
||||
animator.isReversed = (velocity - (animator.fractionComplete - 0.5) * 100).sign == .plus
|
||||
let defaultVelocity = sheetHeight / sheetAnimationDuration
|
||||
let fractionRemaining = 1 - animator.fractionComplete
|
||||
let durationFactor = min(fractionRemaining / (abs(velocity) / defaultVelocity), 1)
|
||||
animator.continueAnimation(withTimingParameters: nil, durationFactor: durationFactor)
|
||||
@@ -118,7 +121,9 @@ struct SheetRepresentable<Content: View>: UIViewControllerRepresentable {
|
||||
func tap(gesture: UITapGestureRecognizer) {
|
||||
switch gesture.state {
|
||||
case .ended:
|
||||
representer.isPresented = false
|
||||
if gesture.location(in: view).y < view.frame.height - sheetHeight {
|
||||
representer.isPresented = false
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user