Here is the testing code:
import SwiftUI
struct ContentView: View {
@State private var pad: Bool = false
@State private var showDot: Bool = true
var body: some View {
VStack {
Button {showDot.toggle()} label: {Text("Toggle Show Dot")}
Spacer().frame(height: pad ? 100 : 10)
Circ(showDot: showDot)
Spacer()
}.onAppear {
withAnimation(.linear(duration: 3).repeatForever()) {pad = true}
}
}
}
struct Circ: View {
let showDot: Bool
var body: some View {
Circle().stroke().frame(height: 50).overlay {if showDot {Circle().frame(height: 20)}}
}
}
It happens that after I toggle showDot
, the dot circle is not on the center of the stroke circle again! How can I fix this?
The Circ
View is given, I can't change that view!
CodePudding user response:
Edit
If you can just hide the view, see solution 1, which is preferred. If you need to re-build the view, see solution 2.
Solution 1
Replace the if
condition with a .opacity()
modifier that reads 1 when showDot
is true
.
This way, the dot does not disappear completely, it is there but you just can't see it. You will be toggling the visibility, not the view itself.
Like this:
@State private var pad: Bool = false
@State private var showDot: Bool = true
var body: some View {
VStack {
Button {
showDot.toggle()
} label: {
Text("Toggle Show Dot")
}
Spacer()
.frame(height: pad ? 100 : 10)
Circle().stroke()
.frame(height: 50)
.overlay {
Circle()
.frame(height: 20)
.opacity(showDot ? 1 : 0) // <- Here
}
Spacer()
}
.onAppear {
withAnimation(.linear(duration: 3).repeatForever()) {pad = true}
}
}
Solution 2
You can replace the animation with a timer. Every time it triggers, it will move the whole view by changing the height of the Spacer()
.
// These variables will track the position and moving direction of the dot
@State private var pos: CGFloat = 0
@State private var movingUp = false
@State private var showDot: Bool = true
// This variable will change the position
// This is a dummy iniatialization, the .onAppear modifier sets the real timer
@State private var timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in }
var body: some View {
VStack {
Button {
showDot.toggle()
} label: {
Text("Toggle Show Dot")
}
Spacer()
.frame(height: pos)
Circle().stroke()
.frame(height: 50)
.overlay {
if showDot {
Circle().frame(height: 20)
}
}
Spacer()
}
.onAppear {
// The timer interval will define the speed
timer = Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { _ in
moveCircle()
}
}
}
private func moveCircle() {
if movingUp {
if pos <= 0 {
pos = 0
movingUp = false
} else {
pos -= 1
}
} else {
if pos >= 100 {
pos = 100
movingUp = true
} else {
pos = 1
}
}
}