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

Drawing in 3D Using SwiftUI
15.80 GEEK