WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Question about Canvas

Forums > SwiftUI

Hi everybody, Can someone point me out what the different between changing value of "questionValue" to zero and non-zero

At first, I set questionValue to zero, and hope my context.rotate will give me full set of clock ticks but It wont Then after I set questionValue to 100, I got what I want. But I'm still dont know why it works, can someone tell me ! thanks !

    var body: some View {
        Canvas{context, size in
            let questionValue = 0.0
            //let questionValue = size.width * 0.40

            let t = size.width * 0.012
            let h = size.height * 0.025
            let path = RoundedRectangle(cornerRadius: 0).path(in: CGRect(x: 0, y: questionValue, width: t, height: h))
            for tick in 0..<60 {
                var context = context
                context.translateBy(x:size.width/2.0, y: size.height/2.0)
                context.rotate(by: .degrees(Double(tick) * 6.0))
                context.fill(path, with: .color(.black))
            }

        }
    }

for zero, I got

and for non-zero, I got clock ticks

   

Perhaps questionValue isn't the right name for this variable?

In your grand design, you may be thinking of a visual timer that's different for each question and you want to adjust this timer view accordingly. But I think the name clouds both the intent and mechanics of drawing.

Draw One Tick

You might get a quick look into your problem by FIRST only drawing ONE tick.

  1. Set your questionValue to 100.
  2. Then adjust your loop code to draw just the first tick.
// Just draw the FIRST tick mark.
   for tick in 0..<1 {
        var context = context
        context.translateBy(x:size.width/2.0, y: size.height/2.0)
        context.rotate(by: .degrees(Double(tick) * 6.0))
        context.fill(path, with: .color(.indigo))
}

That's weird. It's near the bottom of the canvas at the timer's 6 o'clock position, right?

Translate

Next, comment out the translate code.

    // context.translateBy(x:size.width/2.0, y: size.height/2.0)

Without a translation, the tick mark should be all the way to the left of your frame. This is because the origin of the tick mark is CGPoint.zero, aka The Upper Left Corner. However, the tick mark is moved down about 100 pixels. Why's that? That happens to be the value from questionValue which is the Y coordinate of your tickMark, from the origin.

  1. Change questionValue to 0.0 and run it again.
  2. Change questionValue to 5.0 and run it again.

As questionValue increases the tickMark is pushed DOWN the screen.

In a peer code review, my team would certainly focus on this variable's name and probably suggest you change it to timerRadius or tickDistanceFromCenter

Once you select a pleasing distance from center, then you move the origin to the center of the canvas.
Then, you rotate the tick. Then you paint it.

While you're changing variable names, take a look at "w" and "t". Consider tickHeight and tickWidth. Future you will be grateful. Plus it makes it easy to pull those names into a global preferences struct, because your users, or designers will certainly ask you, in the not too distant future, to change your prototype designs. If you pulled "w" or "t" into a global struct, future you will certainly ask "Whut?"

   

hey I have used the exact code but couldn't succeed. There is no clock ticks. i dont know where i m wrong!

   

This line specifies the CGRect for the path. It starts in the lower left and extends to the upper right, but the context can flip it to start in the upper left and extend to the lower left corner.

let path = RoundedRectangle(cornerRadius: 0).path(in: CGRect(x: 0, y: questionValue, width: t, height: h))

The RoundedRectangle therefore is drawn in the CGRect area as defined by the path.

You build up the view in the for … in loop. For each tick you first translate to the centre of the view, then you rotate the tick, and finally you fill the path from the start corner (which now rotated about the centre).

Try this code to help you understand what is going on.

struct ContentView: View {

    @State private var questionValue: Double = 10.0
    @State private var maxTicks: Double = 60.0

    var body: some View {

        VStack {
            Canvas{context, size in
//            let questionValue = 0.0
            //let questionValue = size.width * 0.40

                let t = size.width * 0.012
                let h = size.height * 0.025
                let path = RoundedRectangle(cornerRadius: 0).path(in: CGRect(x: 0, y: questionValue, width: t, height: h))
                for tick in 0..<Int(maxTicks) {
                    var context = context
                    context.translateBy(x:size.width/2.0, y: size.height/2.0)
                    context.rotate(by: .degrees(Double(tick) * 6.0))
                    context.fill(path, with: .color(.black))
                }
            }
            HStack{
                Text("questionValue \(String(format: "%.1f", questionValue))")
                Slider(value: $questionValue, in: 0.0...150.0, step: 0.1)
            }
            .padding(10)

            HStack{
                Text("number of ticks \(String(format: "%.0f", maxTicks))")
                Slider(value: $maxTicks, in: 1.0...60.0, step: 1.0)
            }
            .padding(10)
        }
    }
}

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.