Reputation: 477
As a starting point to creating complications, static data can be presented in the following way by implementing the Complications delegate example code shown below:
This structure implies that I am only able to create one complication per complication family. Is this true? Are there any other options I have here?
For example, how would I create another modular small complication in addition to the one below, of a different type, ie. CLKComplicationTemplateModularSmallStackImage so that both would show in the modular small region?
Is this possibly a user-preference that can be managed?
#pragma mark - Placeholder Templates
- (void)getPlaceholderTemplateForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTemplate * __nullable complicationTemplate))handler {
// This method will be called once per supported complication, and the results will be cached
if (complication.family == CLKComplicationFamilyModularSmall){
CLKComplicationTemplateModularSmallStackText *template = [[CLKComplicationTemplateModularSmallStackText alloc] init];
// template.headerTextProvider = [CLKSimpleTextProvider textProviderWithText:@"Title Text"];
template.line1TextProvider = [CLKSimpleTextProvider textProviderWithText:@"TEXT1"];
template.line2TextProvider = [CLKSimpleTextProvider textProviderWithText:@"TEXT2"];
template.tintColor = [UIColor whiteColor];
handler(template);
} else if (complication.family == CLKComplicationFamilyModularLarge){
CLKComplicationTemplateModularLargeStandardBody *template = [[CLKComplicationTemplateModularLargeStandardBody alloc] init];
template.headerTextProvider = [CLKSimpleTextProvider textProviderWithText:@"Text1"];
template.body1TextProvider = [CLKSimpleTextProvider textProviderWithText:@"Text2"];
template.body2TextProvider = [CLKSimpleTextProvider textProviderWithText:@"Text3"];
UIImage *surfMain = [UIImage imageNamed:@"person"];
template.headerImageProvider = [CLKImageProvider imageProviderWithOnePieceImage:surfMain];
handler(template);
}
}
Upvotes: 1
Views: 1071
Reputation: 115
I propose here a code to do that in the ComplicationController
.
getComplicationDescriptors
method and create many CLKComplicationDescriptor
.func getComplicationDescriptors(handler: @escaping ([CLKComplicationDescriptor]) -> Void) {
let clkComplicationDescriptor = CLKComplicationDescriptor(identifier: "name", displayName: "First Name", supportedFamilies: [.graphicCorner])
let clkComplicationDescriptor2 = CLKComplicationDescriptor(identifier: "age", displayName: "Age", supportedFamilies: [.graphicCorner])
handler([clkComplicationDescriptor, clkComplicationDescriptor2]
}
This methods callback many CLKComplicationDescriptor
with 3 parameters:
identifier
: The identifier of your complications
displayName
: The name displayed when the user choose complications
supportedFamilies
: The families for your complication
getCurrentTimelineEntry
and create your complication with datasfunc getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
print("ID: \(complication.identifier.description)") // return the identifier of complication in the step 1
var firstName = "Ada"
var name = "Lovelace"
var age = 22
switch complication.family {
case .graphicCorner:
switch complication.identifier.description {
case "name":
let innerText = CLKSimpleTextProvider(text: name)
innerText.tintColor = innerTextColor
let outerText = CLKSimpleTextProvider(text: firstName)
outerText.tintColor = outerTextColor
let cornerTemplate = CLKComplicationTemplateGraphicCornerStackText(innerTextProvider: innerText, outerTextProvider: outerText)
let entry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: cornerTemplate)
handler(entry)
case "age":
let innerText = CLKSimpleTextProvider(text: age)
innerText.tintColor = innerTextColor
let outerText = CLKSimpleTextProvider(text: "old")
outerText.tintColor = outerTextColor
let cornerTemplate = CLKComplicationTemplateGraphicCornerStackText(innerTextProvider: innerText, outerTextProvider: outerText)
let entry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: cornerTemplate)
handler(entry)
default:
handler(nil)
}
default:
handler(nil)
}
}
You can choose now among several complications for the corner family.
Don't forget to implement getLocalizableSampleTemplate
.
Upvotes: 0
Reputation: 1018
The previous answer is outdated. WatchOS 7 onwards, we can now add multiple complications to the same complication family for our app.
Step 1:
In our ComplicationController.swift
file, we can make use of the getComplicationDescriptors
function, which allows us to describe what complications we are making available in our app.
In the descriptors
array, we can append one CLKComplicationDescriptor()
for each kind of complication per family that we want to build.
func getComplicationDescriptors(
handler: @escaping ([CLKComplicationDescriptor]) -> Void) {
var descriptors: [CLKComplicationDescriptor] = []
for progressType in dataController.getProgressTypes() {
var dataDict = Dictionary<AnyHashable, Any>()
dataDict = ["id": progressType.id]
// userInfo helps us know which type of complication was interacted with by the user
let userActivity = NSUserActivity(
activityType: "org.example.foo")
userActivity.userInfo = dataDict
descriptors.append(
CLKComplicationDescriptor(
identifier: "\(progressType.id)",
displayName: "\(progressType.title)",
supportedFamilies: CLKComplicationFamily.allCases, // you can replace CLKComplicationFamily.allCases with an array of complication families you wish to support
userActivity: userActivity)
)
}
handler(descriptors)
}
The app will now have multiple complications (equal to the length of the dataController.getProgressTypes()
array) for each complication family that you support.
But how do you now display different data and views for different complications?
Step 2:
In the getCurrentTimelineEntries
and getTimelineEntries
functions, we can then make use of the complication.identifier
value to identify the data that was passed along when this complication entry was called for.
Example, in the getTimelineEntries
function:
func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries after the given date
var entries: [CLKComplicationTimelineEntry] = []
...
...
...
var next: ProgressDetails
// Find the progressType to show using the complication identifier
if let progressType = dataController.getProgressAt(date: current).first(where: {$0.id == complication.identifier}) {
next = progressType
} else {
next = dataController.getProgressAt(date: current)[0] // Default to the first progressType
}
let template = makeTemplate(for: next, complication: complication)
let entry = CLKComplicationTimelineEntry(
date: current,
complicationTemplate: template)
entries.append(entry)
...
...
...
handler(entries)
}
You can similarly find the data that is passed in the getCurrentTimelineEntry
and the getLocalizableSampleTemplate
functions.
Step 3:
Enjoy!
Upvotes: 0
Reputation:
At this time, you can't offer multiple choices for a particular family. You're expected to choose the best template among the ones offered for that family.
For your particular example, there are seven different modular small templates. Even if you limited yourself to giving the user two choices, it wouldn't scale well if every app that supported complications doubled or tripled the number of choices they offered within a particular family.
UX perspective:
It avoids a disruptive user experience, from having to transition between scrolling by app to scrolling by an app's multiple choices, just for the user to get past your choices to a different app's complication that they want to select.
Developer perspective:
If a user could choose to show two or three of your app's different modular small complications at the same time, the complication server would have to call your particular data source multiple times just to keep every active complication of yours up to date. There's the daily budget to consider, not to mention our extensions becoming a bit harder to read and maintain, if we had to switch on family, and then by a particular template.
Apple appears to have chosen a good design for both users and developers by limiting it to one complication per family. If you have a reason to support more than one, you can submit a feature request to the Apple Watch team.
Upvotes: 4