SwiftUI : Brief Introduction to Button
Button in SwiftUI is … very interesting
A Button is a control that performs an action when triggered.
Although the definition for a button is very straight forward, it is a whole other story when examining its initialize methods …
So there are overall 4 methods that we can use to create a single button ><…
Which one should I use ?
First, let’s understand the structure of a Button.
A Button is a struct that adapts View protocol and has a generic type Label that also adapts View protocol.
Depending on the type of the Label, you will use different initiation methods.
- Label : View → Use it when you wish to customized your button.
public init(action: @escaping () -> Void,
@ViewBuilder label: () -> Label)
- Label == Text
public init(_ titleKey: LocalizedStringKey,
action: @escaping () -> Void)public init<S>(_ title: S,
action: @escaping () -> Void)
where S : StringProtocol
This initializer is very straight forward. You can simply use String or LocalizedStringKey to initialize a Button :
Not so pretty eh?
- Label == PrimitiveButtonStyleConfiguration.Label
public init(_ configuration: PrimitiveButtonStyleConfiguration)
A PrimitiveButtonStyleConfiguration contains the properties of a `Button` instance being created.
This initializer is a bit confusing, it took me a long hours to kind of know where to use it.
First, let’s take a look at PrimitiveButtonStyleConfiguration :
It is :
- a struct that holds the properties of a Button instance.
- contains a struct Label, which describes the effect of calling action.
- contains a
triggerfunction, which performs the button’s action.
It seems that PrimitiveButtonStyleConfiguration has something to do with PrimitiveButtonStyle, let’s take a look at it:
A PrimitiveButtonStyle :
- is a protocol that defines the implementation of all `Button` instances within a view
- The current PrimitiveButtonStyle can be configured using
- contains a
makeBodyfunction in it, which takes PrimitiveButtonStyleConfiguration as a parameter.
Can you tell where to use the initializer? …
Did you guess that answer? Good for you, but not me … ><
In order to use the initializer, you will need to create a struct that adapts PrimitiveButtonStyle, and call the initializer within
Note : By calling configuration.trigger() buttons will be clicked whenever this style applied.
Next, we need to apply this style to the button by calling
.buttonStyle() at the main View with buttons that you wish to apply the same modification.
As you can see, the button background is now filled with orange, this is also the area where button can be pressed.
However, you might also notice the background area is not the same size as the border. This is because, the padding is applied to Button and not the content of Button. To fix this, all you need to do is to move the padding inside the second block :
If you wish to detect if the button is pressed inside
makeBody, then adapts ButtonStyle instead of PrimitiveButtonStyle :
When pressed :
Now that a new MyStyle is made, here is how it was applied to the button :
Note: I have removed the padding from before, that’s why the first button is now shorter.
How else can we modify a Button ?
The answer is → ViewModifier.
A ViewModifier is a modifier that can be applied to a view or other view modifier, resulting in a different version of the original value.
We also got other modifiers, including AnimtableModifier and EnvironmentalModifier, which are also protocols.
Some of the modifiers that we have used in the example above include : clipped and cornerRadius,
Ever wonder how they are created?
Think no further, let’s create our own ViewModifier.
Creating our ViewModifier
First, we need to create a struct that adapts ViewModifier :
As you have notice, an error has occurred :
Type 'MyButtonModifier' does not conform to protocol 'ViewModifier'
If you let Xcode fix it for you, it will ask you to state your typealias Body. However, that is not the way to fix this issue :
Even if you defined your typealias, you will still get the same error.
To fix this, you will need to implement the function
However, again, a new error occurred :
Reference to invalid associated type 'Body' of type 'MyButtonModifier'
Here, simply change your _STRUCT_NAME.Body to some View would get rid of that error :
Now the good news is, the previous errors are now gone, and only one new error remained :
Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
Basically what it means is that, you need to return some kind of View in
body. Here, we will return the content and now no error at all :
You might wondered, what exactly is the content ?
Unfortunately, I don’t really have a solid answer. My best guess is that, a content is the View that is using this modifier.
Now, you can apply any modifier you wish to the content :
And apply that to the button you’ve created before :
SwiftUI gives developers a whole new way to design buttons.
With UIKit, to customized a button, we need to create a class and inherit UIButton. However, with SwiftUI, all we need is a struct that inherit ButtonStyle or ViewModifier and apply to the View of interest.
Although ButtonStyle and PrimitiveButtonStyle seem similar, but they have different usage.
- ButtonStyle allows us knowing when the button is clicked, while a
- PrimitiveButtonStyle allows us to trigger the button.
Last but not least, I couldn’t understand how a Button is created and customized without the help from Alejandro Martinez. He had some really amazing articles on SwiftUI and I urge you to take a look at them. Also, I have some links in the reference
Thank you for reading my article, if you found it useful, please give me a clap.
Also, if you have any question or comment, please leave a message.