Spriter
Spriter

Reputation: 653

How to add Buttons to a iOS 14 Widget

I'm trying to add buttons to a widget having family type ".systemLarge". I want to execute some code without opening the app.

Anyone knows how to do that?

For example the Shortcuts App Widget includes buttons you can tap to execute a shortcut without opening the app.

Upvotes: 15

Views: 13156

Answers (3)

Kyle Venn
Kyle Venn

Reputation: 8038

This is now available in iOS 17!

Details here

Starting with iOS 17, iPadOS 17, or macOS 14, widgets and Live Activities can include buttons and toggles to offer specific app functionality without launching the app

For example, a Reminders widget allows people to mark a task as completed with a toggle. On a locked device, buttons and toggles are inactive and the system doesn’t perform actions unless a person authenticates and unlocks their device.

Add the functionality as an intent

  1. Create a struct
    • For Widget, adopt the AppIntent protocol
    • For Live Activity, adopt the LiveActivityIntent protocol
  2. For input parameters, define via the @Parameter property wrapper (this has to conform to AppEntity)
  3. In perform() function, add the action you want to perform on click

Example:

@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
struct SuperCharge: AppIntent {
    
    static var title: LocalizedStringResource = "Emoji Ranger SuperCharger"
    static var description = IntentDescription("All heroes get instant 100% health.")
    
    func perform() async throws -> some IntentResult {
        EmojiRanger.superchargeHeros()
        return .result()
    }
}

Notes

  • The perform() function is asynchronous AND marked as throws.
  • When you return from the perform() function, the system reloads the widget’s timeline (via timeline provider).

Add the button

struct EmojiRangerWidgetEntryView: View {
    var entry: SimpleEntry
    
    @Environment(\.widgetFamily) var family
    
    @ViewBuilder
    var body: some View {
        switch family {
        
        // Code for other widget sizes.
           
        case .systemLarge:
            if #available(iOS 17.0, *) {
                HStack(alignment: .top) {
                    Button(intent: SuperCharge()) {
                        Image(systemName: "bolt.fill")
                    }
                }
                .tint(.white)
                .padding()
            }
            // ...rest of view
       }
    }
}

Pre-iOS 17 Solution

There are some limitations, but it can be done

  1. You can't just execute code, you can only deeplink to your app which then handles the click.
  2. systemSmall widget is treated as one clickable area (no granular control over multiple touch targets)
  3. systemMedium and systemLarge can have more touch targets and use Link to deeplink to your app with a specific action.

To see an example of how this can be done, you can refer to this post.

Upvotes: 7

Thyraz
Thyraz

Reputation: 2432

You can add Link controls on medium and large size widgets to get a button like behavior.

This won't allow you to create interactive widgets, which change their content by clicking elements inside the widgets. But you will be able to tell which "link" was clicked based on the widgetUrl provided when your app gets activated through the widget.

See the chapter Respond to User Interactions here: https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension

Upvotes: 7

DanubePM
DanubePM

Reputation: 1781

Widgets are read only. The Shortcuts app is an exception.

https://developer.apple.com/documentation/widgetkit/creating-a-widget-extension

Widgets present read-only information and don’t support interactive elements such as scrolling elements or switches. WidgetKit omits interactive elements when rendering a widget’s content.

Upvotes: 8

Related Questions