Creating progress circle as WKInterfaceImage in Wa

2020-02-08 09:03发布

问题:

I am trying to create a progress circle for the Apple Watch version of my app. I know that we aren't able to use UIViews (which would make things so much easier!) so I am looking for alternatives.

Basically, I would like to create one of these prototypes:

The way I was hoping to do things was to add the background layers as a normal WKInterfaceImage and then the progress arrow/line on top as a WKInterfaceImage that rotates around the circle based on the percentage calculated.

I have the percentage calculated so basically, what I am looking for is some help with the math code for rotating the arrow.

Does anyone know if this is possible, and could anyone help me out if so? I'm not trying to update the circle while the app is running; it just needs to update when the Watch App launches to correspond with the iOS version.

Thanks!

回答1:

As of watchOS 3 it is possible to use SpriteKit.

SKShapeNode can draw shapes, so it is possible to create radial rings.

  1. Add a WKInterfaceSKScene to your interface controller in storyboard and hook it up through an outlet.
  2. Set it up in the awake method of the interface controller

    override func awake(withContext context: Any?) {
       super.awake(withContext: context)
       scene = SKScene(size: CGSize(width: 100, height: 100))
       scene.scaleMode = .aspectFit
       interfaceScene.presentScene(scene)
    }
    
  3. Create a shape node and add it to the scene

    let fraction: CGFloat = 0.75
    let path = UIBezierPath(arcCenter: .zero,
                             radius: 50,
                             startAngle: 0,
                             endAngle: 2 * .pi * fraction,
                             clockwise: true).cgPath
    
    let shapeNode = SKShapeNode(path: path)
    shapeNode.strokeColor = .blue
    shapeNode.fillColor = .clear
    shapeNode.lineWidth = 4
    shapeNode.lineCap = .round
    shapeNode.position = CGPoint(x: scene.size.width / 2, y: scene.size.height / 2)
    scene.addChild(shapeNode)
    



回答2:

Another solution is to create 100 picture, for each number you have a frame. So, doing that, you can show an animation using 'startAnimatingWithImagesInRange:duration:repeatCount' method.

The problem is that it's hard to customise each frame. Somebody thought about this issue and created a generator. You can find it by this name:

Radial Bar Chart Generator

This link should help you to customise the frames: http://hmaidasani.github.io/RadialChartImageGenerator/

Also you have same samples on git link. For 100 frames with a single arc frame you get around 1,8 MB on disk.



回答3:

Most of what is available on iOS is not present (yet) in WatchKit. In particular, several of the things you want to do are almost impossible. (Glimmer of hope in a moment). In particular, you cannot rotate an image. Or rather, you can rotate an image, but you have to do it on the phone and then pass that image up to the watch at runtime. Also, you cannot easily composite images - however, there is a way to do it.

One way would be to construct the entire rotated, composited image the way you want it on the phone and pass the final data up to the button using [WKInterfaceButton setBackgroundImage:]. Unfortunately, you will likely find this to be slow in the simulator, and most likely it will work poorly on the actual watch. Hard to know for sure because we don't have one, but this is sending the image on the fly over Bluetooth. So you won't get smooth animation or good response times.

A better way is to hack your way to it on the watch. This relies on two tricks: one, layering groups together with background images. Two, using -[WKInterfaceImage startAnimatingWithImagesInRange:duration:repeatCount:].

For the first trick, drop a Group into your layout, then put another group inside it, then (possibly) a button inside that. Then use -[WKInterfaceGroup setBackgroundImage:] and the images will composite together. Make sure you use proper transparency, etc.

For the second trick, refer to the official documentation - essentially, you will need a series of images, one for each possible rotation value, as erdekhayser said. Now, this may seem egregious (it is) and possibly impractical (it is not). This is actually how Apple recommends creating spinners and the like - at least for now. And, yes, that may mean generating 360 different images, although because of the small screen, my advice is to go every 3-5 degrees or so (nobody will be able to tell the difference).

Hope this helps.



回答4:

Nobody posts code??? Here it is! enjoy:

1) Create the images here: http://hmaidasani.github.io/RadialChartImageGenerator/

2) Drag n Drop a picker into your View Controller, and link it to some viewController.swift file. Set the Picker style to "Sequence" on the menu that appears on the right.

3) Add this code to the viewController.swift , and connect the picker to the IBOutlet and the IBAction:

import WatchKit
import Foundation

class PickerViewController: WKInterfaceController {

    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)
    }

    @IBOutlet var itemPicker: WKInterfacePicker!

    override func willActivate() {
        super.willActivate()

        let pickerItems: [WKPickerItem] = (0...100).map {
            let pickerItem = WKPickerItem()
            pickerItem.contentImage = WKImage(imageName: "singleArc\($0).png")
            return pickerItem
        }
        itemPicker.setItems(pickerItems)
    }

    @IBAction func pickerSelectedItemChanged(value: Int) {
        NSLog("Sequence Picker: \(value) selected.")
    }

    override func didDeactivate() {
        super.didDeactivate()
    }

}


回答5:

WKInterface classes are not able to be subclassed. Therefore, a custom control is not possible.

Also, animation is limited. In order to create an animation, you must store every single frame as an image. Then, you can have an image view in your WatchKit app that cycles through these images.

Store the images in the Images.xcassets folder in the watch target, and try to mess around with the changing the frame based on the percentage the activity is finished.

One extra note: having 100 images would not be efficient, as each WatchKit app has only a limited amount of space on the watch it can take up (I believe it is 20MB, but I am not sure). Maybe have an image for every 5%.



回答6:

No, there is not possible to creating this custom circle on watch kit, because UIView doesn't work on watch kit.

there is only solution of your problem is you have to put 100 images of each frames... and make sure that size of images is lesser than 20 MB. because the size of watch application is up to 20 MB