iOS WidgetKit 小部件中的右对齐 Text(Date(), style: .timer) 文本

Right-aligning Text(Date(), style: .timer) text in an iOS WidgetKit widget

这里有一个有趣的难题:我想制作一个可靠地“滴答作响”的计时器,同时在可预测的位置呈现符号,这样我就可以通过添加背景来装饰计时器。由于 WidgetKit 的限制,我无法可靠地每秒渲染自己的文本,必须依赖特殊视图,例如 Text(Date(), style: .timer)。但是,此视图可以将时间呈现为 XX:XXX:XX ,具体取决于剩余时间,这样就可以了,除此之外,它也都占用容器的整个宽度并向左对齐,这使得最后一个 :XX 根据剩余时间移动。

这是一个例子:

生成它的代码:

struct MyWidgetEntryView : View {    
    var body: some View {
        VStack {
            Text(Date().addingTimeInterval(1000), style: .timer)
                .font(.body.monospacedDigit())
                .background(Color.red)
            
            Text(Date().addingTimeInterval(100), style: .timer)
                .background(Color.green)
                .font(.body.monospacedDigit())
        }
    }
}

问题:有没有一种方法可以在 WidgetKit 小部件中可靠地更新时间显示,使分钟和秒的符号始终呈现在相同的位置并且不会根据剩余时间移动?

我想不通,请帮帮我!

–巴格兰

仍在寻找更好的答案,但这里有一个“概念验证”黑客来实现我的目标:

struct _T1WidgetEntryView : View {
    
    struct SizePreferenceKey: PreferenceKey {
        static var defaultValue: CGSize = .zero
        static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
            value = nextValue()
        }
    }
    
    @State private var digitSize: CGSize = .zero
    @State private var semicolonSize: CGSize = .zero
    
    var body: some View {
        ZStack {
                        
            Text("0")
                .overlay(
                    GeometryReader { proxy in
                        Color.green
                            .preference(key: SizePreferenceKey.self, value: proxy.size)
                    }
                    .onPreferenceChange(SizePreferenceKey.self) { digitSize = [=10=] }
                )
                .hidden()
            
            Text(":")
                .overlay(
                    GeometryReader { proxy in
                        Color.green
                            .preference(key: SizePreferenceKey.self, value: proxy.size)
                    }
                    .onPreferenceChange(SizePreferenceKey.self) { semicolonSize = [=10=] }
                )
                .hidden()
            
            
            Color.clear
                .frame(width: digitSize.width * 4 + semicolonSize.width, height: digitSize.width * 4 + semicolonSize.width)
                .overlay(
                    Text(Date().addingTimeInterval(100 + 3600 * 200), style: .timer)
                        .frame(width: digitSize.width * 7 + semicolonSize.width * 2)
                    ,
                    alignment: .trailing
                )
                .clipped()
            
        }
        .font(.body.monospacedDigit())
    }

}

结果是:

此代码假定所有数字的宽度相同(因此 .monospacedDigit() 字体修饰符)。

这是它的作用:

  1. 计算数字符号和分号的大小;
  2. 通过添加 200 小时来“规范化”时间字符串以确保 XXX:XX:XX 格式;
  3. 设置文本大小以容纳格式为 XXX:XX:XX;
  4. 的字符串
  5. 设置容器的大小以容纳格式为 XX:XX;
  6. 的字符串
  7. 对齐叠加层中的文本 .trailing;
  8. 将整个内容裁剪到容器的大小。

再次强调,如果有更好的解决方案,我很乐意学习!

–巴格兰

设置多行文本对齐方式:

Text(Date(), style: .timer)
    .multilineTextAlignment(.trailing)

这不是多行文本,但它有效!