refine iOS swipe surface reveal
This commit is contained in:
@@ -47,39 +47,10 @@ struct RoundView: View {
|
||||
)
|
||||
|
||||
ZStack {
|
||||
HermesTheme.surface
|
||||
.ignoresSafeArea()
|
||||
|
||||
if round != nil {
|
||||
HermesVideoPlayerView(coordinator: playerCoordinator, cornerRadius: 0, fixedHeight: nil)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
|
||||
LinearGradient(
|
||||
colors: [.black.opacity(0.72), .black.opacity(0.12), .black.opacity(0.78)],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
.ignoresSafeArea()
|
||||
|
||||
sideSelectionOverlay(round: round)
|
||||
|
||||
if let feedback = swipeFeedback {
|
||||
swipeFeedbackOverlay(feedback)
|
||||
swipeSurface(round: round, remaining: remaining, timerLocked: timerLocked, bannerMessage: bannerMessage)
|
||||
}
|
||||
|
||||
VStack(spacing: 0) {
|
||||
header(round: round, remaining: remaining)
|
||||
Spacer()
|
||||
if let bannerMessage {
|
||||
banner(message: bannerMessage)
|
||||
.padding(.horizontal, HermesTheme.screenPadding)
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
bottomOverlay(round: round, timerLocked: timerLocked)
|
||||
}
|
||||
}
|
||||
.offset(x: dragOffset.width * 0.12)
|
||||
.contentShape(Rectangle())
|
||||
.gesture(swipeGesture(round: round, timerLocked: timerLocked))
|
||||
.onAppear {
|
||||
@@ -113,6 +84,43 @@ struct RoundView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func swipeSurface(round: HermesRound?, remaining: TimeInterval, timerLocked: Bool, bannerMessage: String?) -> some View {
|
||||
ZStack {
|
||||
HermesTheme.surface
|
||||
.ignoresSafeArea()
|
||||
|
||||
if round != nil {
|
||||
HermesVideoPlayerView(coordinator: playerCoordinator, cornerRadius: 0, fixedHeight: nil)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
|
||||
LinearGradient(
|
||||
colors: [.black.opacity(0.72), .black.opacity(0.12), .black.opacity(0.78)],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
.ignoresSafeArea()
|
||||
|
||||
if let feedback = swipeFeedback {
|
||||
swipeFeedbackOverlay(feedback)
|
||||
}
|
||||
|
||||
VStack(spacing: 0) {
|
||||
header(round: round, remaining: remaining)
|
||||
Spacer()
|
||||
if let bannerMessage {
|
||||
banner(message: bannerMessage)
|
||||
.padding(.horizontal, HermesTheme.screenPadding)
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
bottomOverlay(round: round, timerLocked: timerLocked)
|
||||
}
|
||||
}
|
||||
.offset(x: dragOffset.width * 0.72)
|
||||
.rotationEffect(.degrees(max(min(Double(dragOffset.width / 40.0), 6), -6)))
|
||||
.shadow(color: .black.opacity(0.28), radius: 22, x: 0, y: 10)
|
||||
}
|
||||
|
||||
private func header(round: HermesRound?, remaining: TimeInterval) -> some View {
|
||||
HStack(alignment: .top, spacing: 16) {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
@@ -243,7 +251,7 @@ struct RoundView: View {
|
||||
@ViewBuilder
|
||||
private func sideSelectionOverlay(round: HermesRound?) -> some View {
|
||||
if let round, phase == .preview {
|
||||
let dragThreshold: CGFloat = 18
|
||||
let dragThreshold: CGFloat = 10
|
||||
let isDraggingRight = dragOffset.width > dragThreshold
|
||||
let isDraggingLeft = dragOffset.width < -dragThreshold
|
||||
|
||||
@@ -252,13 +260,13 @@ struct RoundView: View {
|
||||
title: sortedOutcomes(for: round).dropFirst().first.map(outcomeTitle) ?? localization.string(for: "round.no"),
|
||||
subtitle: localization.string(for: "round.swipe_left"),
|
||||
color: .red,
|
||||
opacity: isDraggingLeft ? min(abs(dragOffset.width) / 180.0, 0.78) : 0
|
||||
opacity: isDraggingLeft ? min(abs(dragOffset.width) / 240.0, 0.95) : 0
|
||||
)
|
||||
sideTint(
|
||||
title: sortedOutcomes(for: round).first.map(outcomeTitle) ?? localization.string(for: "round.yes"),
|
||||
subtitle: localization.string(for: "round.swipe_right"),
|
||||
color: HermesTheme.positive,
|
||||
opacity: isDraggingRight ? min(abs(dragOffset.width) / 180.0, 0.78) : 0
|
||||
opacity: isDraggingRight ? min(abs(dragOffset.width) / 240.0, 0.95) : 0
|
||||
)
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
@@ -287,7 +295,7 @@ struct RoundView: View {
|
||||
}
|
||||
|
||||
private func swipeGesture(round: HermesRound?, timerLocked: Bool) -> some Gesture {
|
||||
DragGesture(minimumDistance: 24)
|
||||
DragGesture(minimumDistance: 12)
|
||||
.onChanged { value in
|
||||
dragOffset = value.translation
|
||||
}
|
||||
@@ -298,7 +306,7 @@ struct RoundView: View {
|
||||
return
|
||||
}
|
||||
|
||||
guard abs(value.translation.width) > 72, abs(value.translation.width) > abs(value.translation.height) else {
|
||||
guard abs(value.translation.width) > 145, abs(value.translation.width) > abs(value.translation.height) else {
|
||||
swipeFeedback = nil
|
||||
return
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HermesPrediction.app/HermesApp";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HermesPrediction.app/HermesPrediction";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -344,7 +344,7 @@
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HermesPrediction.app/HermesApp";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HermesPrediction.app/HermesPrediction";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import XCTest
|
||||
@testable import HermesApp
|
||||
@testable import HermesPrediction
|
||||
|
||||
private struct TestFailure: Error, CustomStringConvertible {
|
||||
let description: String
|
||||
|
||||
Reference in New Issue
Block a user