I'm new to coding having done very little of it previously, and am yet again trying to learn how to code using Swift / SwiftUI.
Although I do have a simple app idea that I don't expect will ever make any money but it will fill a particular need of mine and hopefully others in my position.
For the UI of my app I'd like to display different views depending on what the user wants to do and I have the idea to present these views in a circular design and "flip" the circle over as each view is required.
I've looked at a few different "flip animation" tutorials and they all seem to focus on just two views, and flipping between them (ie like a playing card) but what I need is to have an arbitrary number of views and flip between any given two.
I thought I had it with my last attempt but the results are unexpected (to me) and I'm struggling to follow why. Which is probably my fault, as I used a function from one of the tutorials that flips between just two views. Here is my (admittedly messy I know) code. I know there will be tons of things I've done wrong, and better ways of doing things so please don't be too hard on me :)
struct tealView : View {
@Binding var degree : Double
var body: some View {
ZStack{
Group {
Circle()
.frame(width: 350, height: 350)
.foregroundColor(Color(.systemTeal))
Text("Teal View!")
}
}.rotation3DEffect(Angle(degrees: degree), axis: (x: 0, y: 1, z: 0), anchor: .center, anchorZ: 0, perspective: 0.2)
}
}
struct redView : View {
@Binding var degree : Double
var body: some View {
ZStack {
Group {
Circle()
.frame(width: 350, height: 350)
.foregroundColor(Color(.systemRed))
Text("Red View!")
}
}.rotation3DEffect(Angle(degrees: degree), axis: (x: 0, y: 1, z: 0), anchor: .center, anchorZ: 0, perspective: 0.2)
}
}
struct blueView : View {
@Binding var degree : Double
var body: some View {
ZStack {
Group {
Circle()
.frame(width: 350, height: 350)
.foregroundColor(Color(.systemBlue))
Text("Blue View!")
}
}.rotation3DEffect(Angle(degrees: degree), axis: (x: 0, y: 1, z: 0), anchor: .center, anchorZ: 0, perspective: 0.2)
}
}
struct yellowView : View {
@Binding var degree : Double
var body: some View {
ZStack {
Group {
Circle()
.frame(width: 350, height: 350)
.foregroundColor(Color(.systemYellow))
Text("Yellow View!")
}
}.rotation3DEffect(Angle(degrees: degree), axis: (x: 0, y: 1, z: 0), anchor: .center, anchorZ: 0, perspective: 0.2)
}
}
struct ContentView: View {
@State var backDegree = 0.0
@State var frontDegree = -90.0
@State var isFlipped = false
@State private var tealFront = true
@State private var redFront = false
@State private var blueFront = false
@State private var yellowFront = false
@State private var tealWasFront = false
@State private var redWasFront = true
@State private var blueWasFront = false
@State private var yellowWasFront = false
let durationAndDelay : CGFloat = 0.45
func flipcard () {
isFlipped = !isFlipped
if isFlipped {
withAnimation(.easeInOut(duration: durationAndDelay)) {
backDegree = 90
}
withAnimation(.easeInOut(duration: durationAndDelay).delay(durationAndDelay)){
frontDegree = 0
}
} else {
withAnimation(.easeInOut(duration: durationAndDelay)) {
frontDegree = -90
}
withAnimation(.easeInOut(duration: durationAndDelay).delay(durationAndDelay)){
backDegree = 0
}
}
}
var body: some View {
VStack {
ZStack {
if tealFront {
tealView(degree: $frontDegree)
if yellowWasFront {
yellowView(degree: $backDegree)
} else if redWasFront {
redView(degree: $backDegree)
} else {
blueView(degree: $backDegree)
}
} else if redFront {
redView(degree: $frontDegree)
if yellowWasFront {
yellowView(degree: $backDegree)
} else if tealWasFront {
tealView(degree: $backDegree)
} else {
blueView(degree: $backDegree)
}
} else if blueFront {
blueView(degree: $frontDegree)
if yellowWasFront {
yellowView(degree: $backDegree)
} else if redWasFront {
redView(degree: $backDegree)
} else {
tealView(degree: $backDegree)
}
} else {
yellowView(degree: $frontDegree)
if tealWasFront {
tealView(degree: $backDegree)
} else if redWasFront {
redView(degree: $backDegree)
} else {
blueView(degree: $backDegree)
}
}
}
.padding(20)
HStack {
Group {
Button {
if !tealFront { tealFront = true }
if redFront {
redFront = false
redWasFront = true
blueWasFront = false
yellowWasFront = false
tealWasFront = false
}
if blueFront { blueFront = false
blueWasFront = true
redWasFront = false
yellowWasFront = false
tealWasFront = false
}
if yellowFront { yellowFront = false
yellowWasFront = true
blueWasFront = false
redWasFront = false
tealWasFront = false
}
flipcard()
} label: {
ZStack {
Circle()
.foregroundColor(Color(.systemTeal))
Text("Teal")
.foregroundColor(Color.black)
}
}
}
Group {
Button {
if !redFront { redFront = true }
if tealFront {
tealFront = false
tealWasFront = true
blueWasFront = false
yellowWasFront = false
redWasFront = false
}
if blueFront { blueFront = false
blueWasFront = true
yellowWasFront = false
tealWasFront = false
redWasFront = false
}
if yellowFront { yellowFront = false
yellowWasFront = true
blueWasFront = false
tealWasFront = false
redWasFront = false
}
flipcard()
} label: {
ZStack {
Circle()
.foregroundColor(Color(.systemRed))
Text("Red")
.foregroundColor(Color.black)
}
}
}
Group {
Button {
if !blueFront { blueFront = true }
if redFront {
redFront = false
redWasFront = true
tealWasFront = false
yellowWasFront = false
blueWasFront = false
}
if tealFront { tealFront = false
tealWasFront = true
redWasFront = false
yellowWasFront = false
blueWasFront = false
}
if yellowFront { yellowFront = false
yellowWasFront = true
tealWasFront = false
redWasFront = false
blueWasFront = false
}
flipcard()
} label: {
ZStack {
Circle()
.foregroundColor(Color(.systemBlue))
Text("Blue")
.foregroundColor(Color.black)
}
}
}
Group {
Button {
if !yellowFront { yellowFront = true }
if redFront {
redFront = false
redWasFront = true
blueWasFront = false
tealWasFront = false
yellowWasFront = false
}
if blueFront { blueFront = false
blueWasFront = true
redWasFront = false
tealWasFront = false
yellowWasFront = false
}
if tealFront { tealFront = false
tealWasFront = true
blueWasFront = false
redWasFront = false
yellowWasFront = false
}
flipcard()
} label: {
ZStack {
Circle()
.foregroundColor(Color(.systemYellow))
Text("Yellow")
.foregroundColor(Color.black)
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
CodePudding user response:
There is quite a bit a redundancy (still) in this code, but I simplified it a bit and got it working. I replaced your Bool
s with frontView
and backView
enums that take ViewColors
.
When a color is clicked: Only flip when the color we want is not showing. Since frontView
and backView
are reversed if isFlipped
is true
, you have to handle that extra bit of logic. In either case, replace the hidden view with the color we want, and then flip.
Note: When the app starts, we are looking at the backView
of an unflipped View
, which is confusing. I think this is because the original flipping code may have been based upon cards that are dealt face down. The code makes sense if you think "flipped == true" means frontView is showing, "flipped == false" means backView is showing.
enum ViewColors {
case teal, red, blue, yellow
}
struct ContentView: View {
@State var backDegree = 0.0
@State var frontDegree = -90.0
@State var isFlipped = false
@State private var frontView = ViewColors.teal
@State private var backView = ViewColors.red
let durationAndDelay : CGFloat = 0.45
func flipcard () {
isFlipped.toggle()
if isFlipped {
withAnimation(.easeInOut(duration: durationAndDelay)) {
backDegree = 90
}
withAnimation(.easeInOut(duration: durationAndDelay).delay(durationAndDelay)){
frontDegree = 0
}
} else {
withAnimation(.easeInOut(duration: durationAndDelay)) {
frontDegree = -90
}
withAnimation(.easeInOut(duration: durationAndDelay).delay(durationAndDelay)){
backDegree = 0
}
}
}
var body: some View {
VStack {
ZStack {
VStack {
switch backView {
case .teal:
tealView(degree: $backDegree)
case .red:
redView(degree: $backDegree)
case .blue:
blueView(degree: $backDegree)
case .yellow:
yellowView(degree: $backDegree)
}
}
VStack {
switch frontView {
case .teal:
tealView(degree: $frontDegree)
case .red:
redView(degree: $frontDegree)
case .blue:
blueView(degree: $frontDegree)
case .yellow:
yellowView(degree: $frontDegree)
}
}
}
.padding(20)
HStack {
Group {
Button {
if !isFlipped {
if backView != .teal {
frontView = .teal
flipcard()
}
} else {
if frontView != .teal {
backView = .teal
flipcard()
}
}
} label: {
ZStack {
Circle()
.foregroundColor(Color(.systemTeal))
Text("Teal")
.foregroundColor(Color.black)
}
}
}
Group {
Button {
if !isFlipped {
if backView != .red {
frontView = .red
flipcard()
}
} else {
if frontView != .red {
backView = .red
flipcard()
}
}
} label: {
ZStack {
Circle()
.foregroundColor(Color(.systemRed))
Text("Red")
.foregroundColor(Color.black)
}
}
}
Group {
Button {
if !isFlipped {
if backView != .blue {
frontView = .blue
flipcard()
}
} else {
if frontView != .blue {
backView = .blue
flipcard()
}
}
} label: {
ZStack {
Circle()
.foregroundColor(Color(.systemBlue))
Text("Blue")
.foregroundColor(Color.black)
}
}
}
Group {
Button {
if !isFlipped {
if backView != .yellow {
frontView = .yellow
flipcard()
}
} else {
if frontView != .yellow {
backView = .yellow
flipcard()
}
}
} label: {
ZStack {
Circle()
.foregroundColor(Color(.systemYellow))
Text("Yellow")
.foregroundColor(Color.black)
}
}
}
}
}
}
}