Apple Watch 的 WatchKit 中是否有 ActivityIndi​​cator?

Is there an ActivityIndicator in WatchKit for Apple Watch?

Apple Watch 的 WatchKit 中是否有 ActivityIndi​​cator(或类似的东西)?你们如何向用户反馈一些更持久的背景activity?

WatchKit Framework 中没有显示ActivityIndi​​cator 的方法。然而,您可以准备一些圆形图像并轻松地自己创建无限动画。 准备图片并这样命名 frame-0, frame-1, frame-2...frame-n

然后在您的代码中:

    [self.yourInterfaceImage setImageNamed:@"firstFrame-"]; //setting first frame
    [self.yourInterfaceImage startAnimatingWithImagesInRange:[self.model imageRange]
                                               duration:0.4
                                            repeatCount:0];
    // [self.model imageRange] will return NSRange from 0 to n
    // repeatCount == 0 means infinity. Of course you can set some limit, like 100.

希望对您有所帮助。

编辑: 这个答案最初是在推出带有蜂窝和 wifi 连接的 Apple Watch 型号之前发布的,因此可能不再适用于较新型号的设备(考虑到显着的性能改进)。


thread on the Apple Developer forums 有 Apple 工程师关于为什么您不应该使用 Apple Watch 执行网络操作的权威答案。

不从您的手表应用程序/扩展程序执行网络操作的主要原因有两个:

  1. 用户与手表的互动时间很短。请参阅此 Human Interface guidelines

    If you measure interactions with your iOS app in minutes, you can expect interactions with your WatchKit app to be measured in seconds. So interactions should be brief and interfaces should be simple.

  2. 如果网络请求没有完成,系统可能会死锁。

    Our recommendation is that, in general, you should not perform complex networking operations inside a WatchKit Extension...

    [Apple recommends that developers] have a single process that is in charge of updating the information in your database (likely your iOS app), and then your extensions would have (essentially) read-only access to this [cached] database....


话虽这么说。如果你真的需要一个UIActivityIndi​​cator,rdar://19363748(我认为这个还没有开放雷达),开发者已经提出了官方支持的请求。

您可以在您选择的 activity 指标样式中创建一系列图像,然后使用 startAnimatingWithImagesInRange:duration:repeatCount: API 对其进行动画处理。有关 动画的示例,请参阅 Apple 的 Lister 应用程序。

或者,查看 here for a WatchKit Animation tutorial and included "spinner" graphics

为了添加选项,我在 GitHub 上创建了一个 JBWatchActivityIndicator 项目,让您可以生成自己的图像序列:https://github.com/mikeswanson/JBWatchActivityIndicator

如果您不想创建自己的指示器动画,它还包括类似 Apple 的 activity 指示器动画。

在我看来,尝试创建自己的 Spinner 会占用过多的资源。如果 Apple 认为这是个好主意,他们就会提出建议。

我只想拥有一张您调整 Alpha 的图像。使用布尔值来查看您是否应该添加或减去 Alpha。

if (add)
    {
        count=count+5;
        if (count==100)
        {
            add=false;
        }
    }
    else
    {
        count=count-5;
        if (count==0)
        {
            add=true;
        }
    }

    float thealpha=((float)count/100);
    [self.scanb setAlpha:thealpha];

}

我为 Apple Watch 构建了一个简单的 activity 指示器,可在此处获取 https://github.com/tijoinc/WatchActivityIndicator

这是一个简单的文本指示器,它使用了@State 属性:

struct MyView: View {
    private let loaderSpeed = 0.1 // seconds per state
    private let loaderStates = [
        "•       ",
        " •      ",
        "  •     ",
        "   •    ",
        "    •   ",
        "     •  ",
        "      • ",
        "       •",
        "      • ",
        "     •  ",
        "    •   ",
        "   •    ",
        "  •     ",
        " •      ",
    ]
    @State private var loaderMessage = ""
    @State private var loaderState = 0 {
        didSet {
            if self.loaderState > 0 {
                DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + self.loaderSpeed) {
                    if self.loaderState > 0 {
                        self.loaderMessage = self.loaderStates[self.loaderState-1]
                        if self.loaderState >= self.loaderStates.count {
                             self.loaderState = 1
                        } else {
                            self.loaderState += 1
                        }
                    }
                }

            }
        }
    }

    var body: some View {
        HStack() {
            Spacer()
            Text("Loading:")
            Text(loaderMessage).onAppear { self.loaderState = 1 }
            Spacer()
        }
    }
}

设置loaderState = 1启动加载器

设置loaderState = 0停止加载器

我用 swiftUI 使它类似于 watchOS 指示器。

import SwiftUI

struct ActivityIndicatorView: View {

    // MARK: - Value
    // MARK: Public
    @Binding var isAnimating: Bool


    // MARK: Private
    private let radius: CGFloat = 24.0
    private let count = 18
    private let interval: TimeInterval = 0.1

    private let point = { (index: Int, count: Int, radius: CGFloat, frame: CGRect) -> CGPoint in
        let angle   = 2.0 * .pi / Double(count) * Double(index)
        let circleX = radius * cos(CGFloat(angle))
        let circleY = radius * sin(CGFloat(angle))

       return CGPoint(x: circleX + frame.midX, y: circleY + frame.midY)
   }

   private let timer = Timer.publish(every: 1.8, on: .main, in: .common).autoconnect()     // every(1.8) = count(18) / interval(0.1)

   @State private var scale: CGFloat  = 0
   @State private var opacity: Double = 0

   // MARK: - View
   var body: some View {
       GeometryReader { geometry in
            ForEach(0..<self.count) { index in
                Circle()
                    .fill(Color.white)
                    .frame(width: 3.0, height: 3.0)
                    .animation(nil)
                    .opacity(self.opacity)
                    .scaleEffect(self.scale)
                    .position(self.point(index, self.count, self.radius, geometry.frame(in: .local)))
                    .animation(
                        Animation.easeOut(duration: 1.0)
                            .repeatCount(1, autoreverses: true)
                            .delay(TimeInterval(index) * self.interval)
                     )
             }
             .onReceive(self.timer) { output in
                self.update()
             }
        }
        .rotationEffect(.degrees(10.0))
        .opacity(isAnimating == false ? 0 : 1.0)
        .onAppear {
            self.update()
        }
    }



    // MARK: - Function
    // MARK: Private
    private func update() {
        scale   = 0 < scale ? 0 : 1.0
        opacity = 0 < opacity ? 0 : 1.0

        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            self.scale   = 0
            self.opacity = 0
        }
    }
}

#if DEBUG
struct ActivityIndicatorView_Previews: PreviewProvider {

    static var previews: some View {
        let view = ActivityIndicatorView(isAnimating: .constant(true))

        return Group {
             view
                 .previewDevice("Apple Watch Series 5 - 44mm")

             view
                 .previewDevice("Apple Watch Series 4 - 40mm")

             view
                 .previewDevice("Apple Watch Series 3 - 42mm")

             view
                 .previewDevice("Apple Watch Series 3 - 38mm")
         }
     }
 }
 #endif

SwiftUI 有一个内置的解决方案:

ProgressView()

看起来像这样: