Corner complication with arching text label

I'm trying to build a WatchOS 9 corner complication with WidgetKit (using Xcode 14 beta 3).

I'd like my complication to look like the battery one in the bottom right corner. Here's my view:

struct HydrationProgressCorner: View {
    var entry: Provider.Entry

    var body: some View {
        Text("\((100 * entry.progress / entry.target).rounded(.towardZero).formatted())%")
            .widgetLabel {
                ProgressView(value: entry.progress, total: entry.target)
                    .tint(.blue)
                    .widgetAccentable()
            }
    }
}

How can I get my text to follow the curvature of the watch face?

Accepted Reply

There is finally a solution in WatchOS 10 using the widgetCurvesContent() modifier.

var body: some View {
    Text("Hi")
        .widgetCurvesContent()
        .widgetLabel("World!")
}
  • I presume there's no chance of this being back ported to WatchOS 9.... some of my users will never update for various reasons including having an iPhone X stuck on iOS 16

Add a Comment

Replies

Same here... 😓

I don't think this is possible for non-Apple complications. It's not in the public APIs.

I thought about rotating the text a number of degrees, but you can't tell which corner of the Watch face your complication is on, so it would only ever be correct in one of four places

¯\ _ (ツ) _ /¯

There is another thread on this issue where one user got feedback from an Apple engineer:

https://developer.apple.com/forums/thread/707827

So I got an answer from a framework engineer on Twitter and it seems that the curved text in the corner won't be supported in watchOS 9. We're supposed to work around by setting a font like .system(size: 28, weight: .semibold, design: .rounded) to get a similar text size but it won't be curved. The other "issue" with the gradient gauge was a misunderstanding of mine: I thought it's only possible to tint it in combination with a gauge style but we can of course just add a .tint(gradient) modifier

This bug/oversight is currently blocking my migration to WidgetKit-based complications since you cannot combine the two frameworks for different slots.

There is finally a solution in WatchOS 10 using the widgetCurvesContent() modifier.

var body: some View {
    Text("Hi")
        .widgetCurvesContent()
        .widgetLabel("World!")
}
  • I presume there's no chance of this being back ported to WatchOS 9.... some of my users will never update for various reasons including having an iPhone X stuck on iOS 16

Add a Comment

@rolandkajatin How would you handle this in watchOS 9, though? I don't mean, "Can we use it in watchOS 9" - we can't; it's watchOS 10+.

I mean, if our Watch app targets watchOS 9, how do you support it for both watchOS 9 and 10? How would this code look?

var body: some View {
    Text("Hi")
        .widgetCurvesContent()  // <-- What do you put here so that this compiles for both 9 & 10?
        .widgetLabel("World!")
}
  • I don't. I just target the latest WatchOS. Everyone upgrades anyways.

Add a Comment

Okay, a lot of searching came up with something by Dave DeLong here: https://davedelong.com/blog/2021/10/09/simplifying-backwards-compatibility-in-swift/

public struct Backport<Content> {
	public let content: Content

	public init(_ content: Content) {
		self.content = content
	}
}

extension View {
	var backport: Backport<Self> { Backport(self) }
}

extension Backport where Content: View {
	@ViewBuilder func widgetCurvesContent() -> some View {
		if #available(watchOS 10.0, iOSApplicationExtension 17.0, iOS 17.0, macOSApplicationExtension 14.0, *) {
			content.widgetCurvesContent()

		} else {
			content
		}
	}
  // You can put multiple funcs in here
}

To use it:

ZStack {
	Text("Hello, world!)
		.font(.system(size: 21.0, weight: .bold))
}
.backport.widgetCurvesContent()
.widgetLabel {
...
... etc.