I am working with a slider and dealing with translation. Once the slider performs an action, I mock an api call and then on completion the image is unlocked but now I would like to return the slider to the original position once the api call is completed. Here is what the slider looks like in code.
struct DraggingComponent: View {
@Binding var isLocked: Bool
@Binding var isLoading: Bool
let maxWidth: CGFloat
@State private var width = CGFloat(50)
private let minWidth = CGFloat(50)
init(isLocked: Binding<Bool>, isLoading: Binding<Bool>, maxWidth: CGFloat) {
_isLocked = isLocked
self._isLoading = isLoading
self.maxWidth = maxWidth
}
var body: some View {
RoundedRectangle(cornerRadius: 16)
.fill(Color.black)
.opacity(width / maxWidth)
.frame(width: width)
.overlay(
Button(action: { }) {
ZStack {
image(name: "lock", isShown: isLocked)
progressView(isShown: isLoading)
image(name: "lock.open", isShown: !isLocked && !isLoading)
}
.animation(.easeIn(duration: 0.35).delay(0.55), value: !isLocked && !isLoading)
}
.buttonStyle(BaseButtonStyle())
.disabled(!isLocked || isLoading),
alignment: .trailing
)
.simultaneousGesture(
DragGesture()
.onChanged { value in
guard isLocked else { return }
if value.translation.width > 0 {
width = min(max(value.translation.width minWidth, minWidth), maxWidth)
}
}
.onEnded { value in
guard isLocked else { return }
if width < maxWidth {
width = minWidth
UINotificationFeedbackGenerator().notificationOccurred(.warning)
} else {
UINotificationFeedbackGenerator().notificationOccurred(.success)
withAnimation(.spring().delay(0.5)) {
isLocked = false
}
}
}
)
.animation(.spring(response: 0.5, dampingFraction: 1, blendDuration: 0), value: width)
}
private func image(name: String, isShown: Bool) -> some View {
Image(systemName: name)
.font(.system(size: 20, weight: .regular, design: .rounded))
.foregroundColor(Color.black)
.frame(width: 42, height: 42)
.background(RoundedRectangle(cornerRadius: 14).fill(.white))
.padding(4)
.opacity(isShown ? 1 : 0)
.scaleEffect(isShown ? 1 : 0.01)
}
private func progressView(isShown: Bool) -> some View {
ProgressView()
.progressViewStyle(.circular)
.tint(.white)
.opacity(isShown ? 1 : 0)
.scaleEffect(isShown ? 1 : 0.01)
}
}
Where it is used:
@State private var isLocked = true
@State private var isLoading = false
GeometryReader { geometry in
ZStack(alignment: .leading) {
BackgroundComponent()
DraggingComponent(isLocked: $isLocked, isLoading: $isLoading, maxWidth: geometry.size.width)
}
}
.frame(height: 50)
.padding()
.padding(.bottom, 20)
.onChange(of: isLocked) { isLocked in
guard !isLocked else { return }
simulateRequest()
}
private func simulateRequest() {
isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() 2) {
isLoading = false
}
}
How can I get the translation back to the initial position.
CodePudding user response:
Pull your width
@State
up into the containing view and pass it on as @Binding
. After simulateRequest
set it to its initial state.
In your DraggingComponent
use
struct DraggingComponent: View {
@Binding var width: CGFloat
.....
in the View that contains DraggingComponent
:
@State private var width = CGFloat(50)
and:
.onChange(of: isLocked) { isLocked in
guard !isLocked else { return }
simulateRequest()
self.width = CGFloat(50) //reset it here probably with animation
}