Recently I have faced with design-related requirements for Alert on my project - Image should be shown with rich description and additional actions.
A quick check of the existing Alert API provided by Apple shows that there is nothing exist for showing alert to the user with custom Content either Image either TextInput… So I decided to prepare it by myself.
idea
The very first that need to be designed - it’s Buttons for an alert. Let’s grab an idea from Apple and introduce our own Button with separate building functions - one for destructive and another one for the regular type of button.
I always prefer to separate full implementation into the simplest possible components and implement all of them separately. Also, let’s keep in mind the possibility of extending any part of our component.
Now, when we already separate components into simple parts, let’s try to implement them.
implementation
So let’s start and implement this. To do so, we can define struct for this:
And we need to add builders for buttons. Putting all together, we can have next:
Note: private init - this will restrict anyone to create uncategorized buttons for Alert.
Now it’s time to design Alert itself. This should be a View that can be constructed from Content and attach some buttons (UniAlertButton) that we already have.
Thus we would like to build our Alert within Content with View type, we need to define this at struct description:
next - add input param for View to store Content and as it is done within Apple Alert - @State about visibility of Alert, and don’t forget about buttons (UniAlertButton).
Now we should be able to create a convenient way of presenting Alert - using View extension modifiers:
And if we create some preview for testing purpose with a body like this:
we can get unexpected result:
Heh, good - we know that our content can be rendered as expected. Let’s add all other components and update their position by adding GeometryReader and by calculating the positioning of all components in required places.
Before we proceed, let’s recap how system Alert handle 2 and 3 or more buttons:
Ok, keeping this in mind we should define different building blocks:
determine which approach to use for buttons - position horizontally or vertically (requireHorizontalPositioning)
determine presenting context color (backgroundColorView)
determine builders for horizontal and vertical buttons with appropriate layouts (verticalButtonPad and horizontalButtonsPad)
First items is quite easy to achive - just check number of buttons and we are ready to go:
Context color also not a problem:
note u may vant to use @ViewBuilder instead like:
Ok, and last but not least - positioning of content:
Let’s start from easiet part - vertical buttons pad for case when we have 3 or more buttons:
note contentPadding - property that we will use for controlling content padding all over the Alert
var contentPadding: CGFloat = 16
We just iterate through all buttons and put them in VStack with Divider between them. And adjusting padding.
Next part - is to position horizontal buttons. In similar approach let’s iterate over buttons and put them in HStack with Divider:
We can organize auto-layout selection like:
Ok, now we should combine all together in to body of the Alert:
And result:
Ok, so here we can see, that content is stretched to width of View and it hasn’t any background color. We should add few more properties for Alert setup and use them in combination with GeometryReader:
Result:
Ok, much better, but we can see some misalignment for buttons… To fix this, we need to adjust a bit the process how horizontalPad is configured. To do so - pass width of View in to building function and apply few changes:
Let’s check the result:
Looks good.
Ok,let’s apply few changes in to preview - to check appearence of our Alert:
Result is quite unexpected:
Wow! But the reason is quite simple - we need to tell explicitly what exactly the view is shown and what not. To do so - let’s add one more modification:
Looks like we are done. Let’s try again:
Great! That’s exactly what we would like to have.
But wait, how about 3 and more buttons? Let’s check:
Exactly what we expect.
complete solution
The complete solution is available here
bonus
The Alert that we build is good for very simple cases. But let’s think about what we will receive if we present this alert on View that is in ZStack or on View that in TabBar or similar case?. Yes, we will not cover the whole screen, but just a part of presented view. That’s not always expected…
How to solve this?
I believe many solutions depend on a few factors. At least from the iOS supported version. I’m thinking about iOS 13+, so I ended up with combination this solution within FullScreenPresenter modifier that was covered previously and described here.
Off cause u need to modify a bit solution code like remove presenter reference (thus we use special context for Alert presentation) and modification of extension with modifier that we used to create an Alert:
You can also think about some extension that will simplify the way how to create an alert body