16

.timer Text style expands it to fill the width, instead of taking space to fit the contained string. Is there a way to change this behavior?

Text(entry.timeFinished, style: .timer).multilineTextAlignment(.leading).opacity(0.5).background(Color.red)

enter image description here

Text("3:41").opacity(0.5).background(Color.blue)

enter image description here

Entire View:

struct TimePieceWidgetEntryView: View {
    
    var entry: Provider.Entry
    
    var body: some View {
        VStack {
            Text("Timer ⏱")
            Text(Date().addingTimeInterval(600), style: .timer).opacity(0.5)
        }.font(.title.bold().monospacedDigit())
        .padding(5)
        .widgetURL(entry.url)
    }
    
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
Alexey Primechaev
  • 907
  • 1
  • 6
  • 13

1 Answers1

0

I have zero insight into how Apple is building Live Activities and widgets. My guess is that the widget is rendered and then cached. That is why things like standard timers/publishers don't update the UI. My guess is that these Text timers fill the space so that Apple can render a label on top of the cached view without requiring a new layout from the widget when the text size changes. I am saying this because I think the filling of the container is a feature and not a bug, so it probably won't change any time soon.

To get around this I force the size of the Text view to be the largest it should be. There are still some issues, however for the most part this solution looks ok.

struct TextTimer: View {
    // Return the largest width string for a time interval
    private static func maxStringFor(_ time: TimeInterval) -> String {
        if time < 600 { // 9:99
            return "0:00"
        }
        
        if time < 3600 { // 59:59
            return "00:00"
        }
        
        if time < 36000 { // 9:59:59
            return "0:00:00"
        }
        
        return "00:00:00"// 99:59:59
    }
    init(_ date: Date, font: UIFont, width: CGFloat? = nil) {
        self.date = date
        self.font = font
        if let width {
            self.width = width
        } else {
            let fontAttributes = [NSAttributedString.Key.font: font]
            let time = date.timeIntervalSinceNow
            let maxString = Self.maxStringFor(time)
            self.width = (maxString as NSString).size(withAttributes: fontAttributes).width
        }
    }
    
    let date: Date
    let font: UIFont
    let width: CGFloat
    var body: some View {
        Text(timerInterval: Date.now...date)
            .font(Font(font))
            .frame(width: width > 0 ? width : nil)
            .minimumScaleFactor(0.5)
            .lineLimit(1)
    }
}
datinc
  • 3,404
  • 3
  • 24
  • 33