A few weeks ago, I wrote an article in which I explored the advanced 3D view modifier for rotating objects in SwiftUI. It was well received and I thought I would do a short follow-up to explore the subject a little more.
Let’s do a nano review before I do. I put together the 3D object below near the end of that article:
It is basically four squares that we slowly warp across 90 degrees in parallel. We added a couple more visual clues to fool our brain into thinking it is a 3D object — namely numbers that turn inside out as the cube twists and changes to the opacity.
Let’s do some more exploring. There are two parameters I touched on but didn’t really look into in the previous article: perspective and anchorZ
.
Now, one of the things 3D objects have that 2D ones normally don’t is shadows, and there is a view modifier for that very property in SwiftUI. It’s called shadow
— no surprise there. Sadly, it doesn’t really live up to its name and I fear it should actually be called smudge
. You can get a much better shadow using the perspective in the rotate
command. Beyond that, you can animate your shadow.
A shadow created with rotate3D
Here is the code behind this subtle effect:
struct ContentView: View {
@State var warp1:CGFloat = 0.5
@State var shade1: Double = 1
@State var mover: CGFloat = 40
@State var turn: Double = 45
var body: some View {
return VStack {
Spacer()
Image(systemName: "circle")
.foregroundColor(Color.blue)
.onTapGesture {
withAnimation(.linear(duration: 20)) {
self.warp1 = 5
self.shade1 = 0.2
self.mover = 35
self.turn = 25
}
}
ZStack {
Text("Better Programming")
.font(Fonts.avenirNextCondensedBold(size: 48))
.padding()
Text("Better Programming")
.rotation3DEffect(.degrees(turn), axis: (x: 1, y: 0, z: 0), anchor: UnitPoint.init(x: 0.5, y: 0.5), anchorZ: 0, perspective: warp1)
.font(Fonts.avenirNextCondensedBold(size: 48))
.padding()
.offset(x: 0, y: mover)
.foregroundColor(Color.gray)
.opacity(shade1)
}
Spacer()
}
}
}
The other attribute I hardly mentioned was anchorZ
. Let’s stay on the subject of text and use anchorZ
to turn said text on a globe as we rotate it. We’ll add some colored shading to help out on the image trickery:
SwiftUI globe using text and anchorZ values
I am sure you agree it looks great. We have a spinning globe that looks 3D-ish. But wait, let’s continue with the globe theme and look at how we can create a wireframe globe.
struct ContentView: View {
@State var rotate1:Double = 0
@State var rotate2:Double = 90
@State var rotate3:Double = 90
@State var rotate4:Double = 90
@State var direction:CGFloat = 64
var body: some View {
VStack {
Spacer()
Image(systemName: "circle")
.foregroundColor(Color.blue)
.onTapGesture {
withAnimation(.linear(duration: 6)) {
self.rotate1 = 90
self.rotate2 = 0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5.5, execute: {
withAnimation(.linear(duration: 6)) {
self.rotate2 = -90
self.rotate3 = 0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5.5, execute: {
withAnimation(.linear(duration: 6)) {
self.rotate3 = -90
self.rotate4 = 0
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5.5, execute: {
self.direction = -64
withAnimation(.linear(duration: 6)) {
self.rotate4 = -90
self.rotate1 = 0
}
})
})
})
}
Spacer()
ZStack {
Circle()
.fill(LinearGradient(gradient: Gradient(colors: [.white, .yellow, .orange, .red]), startPoint: .leading, endPoint: .trailing))
.frame(width: 128, height: 128, alignment: .center)
Circle()
.stroke(Color.black)
.frame(width: 128, height: 128, alignment: .center)
Text("A")
// .stroke(Color.black)
.frame(width: 64, height: 64, alignment: .center)
.rotation3DEffect(.degrees(rotate1), axis: (x: 0, y: 1, z: 0), anchor: UnitPoint.center, anchorZ: direction, perspective: 0)
.font(.largeTitle)
Text("B")
// .stroke(Color.red)
.frame(width: 64, height: 64, alignment: .center)
.rotation3DEffect(.degrees(rotate2), axis: (x: 0, y: 1, z: 0), anchor: UnitPoint.center, anchorZ: -64, perspective: 0)
.font(.largeTitle)
Text("C")
// .stroke(Color.black)
.frame(width: 64, height: 64, alignment: .center)
.rotation3DEffect(.degrees(rotate3), axis: (x: 0, y: 1, z: 0), anchor: UnitPoint.center, anchorZ: -64, perspective: 0)
.font(.largeTitle)
Text("D")
// .stroke(Color.red)
.frame(width: 64, height: 64, alignment: .center)
.rotation3DEffect(.degrees(rotate4), axis: (x: 0, y: 1, z: 0), anchor: UnitPoint.center, anchorZ: -64, perspective: 0)
.font(.largeTitle)
}
Spacer()
}
}
}
#swiftui #swift #ios #3d-drawing #programming