Apple Watch的WatchKit中是否有ActivityIndicator(或类似的东西)?您如何向用户提供有关某些持续较长的后台活动的反馈?
Apple Watch的WatchKit中是否有ActivityIndicator(或类似的东西)?您如何向用户提供有关某些持续较长的后台活动的反馈?
仅作为补充,我在GitHub上创建了一个名为JBWatchActivityIndicator
的项目,它可以让您生成自己的图像序列:https://github.com/mikeswanson/JBWatchActivityIndicator
如果您不想创建自己的图像序列,它还包括苹果风格的活动指示器动画。
编辑:这篇答案最初发布于Apple Watch没有蜂窝数据和wifi连接的型号问世之前,因此在新款设备上可能不再适用(考虑到重大的性能改进)。
这个苹果开发者论坛的帖子有一位苹果工程师关于为什么你不应该在 Apple Watch 上执行网络操作的权威回答。
有两个重要原因不能从您的手表应用程序/扩展程序执行网络操作:
用户只与其手表交互了很短的时间。请参见人机界面指南。
如果您将 iOS 应用与用户的互动衡量为几分钟,则可以预计 WatchKit 应用与用户的互动将被衡量为几秒钟。因此,交互应该简短,界面应该简单。
如果网络请求未完成,则系统可能会死锁。
我们的建议是,通常情况下,您不应在 WatchKit Extension 内执行复杂的网络操作......
[苹果建议开发者]有一个负责更新数据库中信息(可能是您的 iOS 应用)的单个进程,然后您的扩展程序将具有(基本上)只读访问权限以访问这个[缓存的]数据库...
话虽如此。如果你真的需要 UIActivityIndicator,rdar:// 19363748(我不认为这个已经被官方支持了),开发人员已经提交了请求。
您可以创建一系列符合您选择的活动指示器样式的图像,然后使用startAnimatingWithImagesInRange:duration:repeatCount:
API 对它们进行动画处理。请参考 Apple 的 Lister 应用程序以获取 wkinterfaceimage 动画的实例。SwiftUI内置了一种解决方案:
ProgressView()
它会像这样:
在WatchKit Framework中没有显示ActivityIndicator的方法。然而,您可以准备一些圆形图像并轻松地自己创建无限动画。
准备图片并将它们命名为:
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.
我使用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
我认为尝试创建自己的Spinner会消耗过多的资源。如果苹果公司认为这是个好主意,他们会建议的。
相反,我会只使用一个Image并调整它的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];
}
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
设置为停止加载器