Building Custom Views with SwiftUI
Description: Learn how to build custom views and controls in SwiftUI with advanced composition, layout, graphics, and animation. See a demo of a high performance, animatable control and watch it made step by step in code. Gain a deeper understanding of the layout system of SwiftUI.
Viewalways has the size of its body
rootViewhas the dimensions of the device minus the safe area insets.
- The top layer of any view with a body is always what we call layout neutral. Because its view bounds are defined by the bounds of its body, regardless of what’s above.
- The root view offers our view a proposed size (the safe layout area)
- The view answers with the size it would like
- The root view, know with its child view size knowledge, put the child somewhere (by default at the center of its size, both vertically and horizontally)
In SwiftUI there's no way to force a size on the view's child: the parent has to respect that choice.
- Parent proposes a size for child
- Child chooses its own size
- Parent places child in parent’s coordinate space
- (Bonus) SwiftUI rounds coordinates to the nearest pixel
Since every view controls its own size, when we build a view, we get to decide how and when it resizes.
The bounds of a
Text view never stretch beyond the height and width of its displayed lines.
When we need to fight for space, especially in stack views, we can raise the layout priority by setting the view
0 by default).
Need of an alignment between views of different stacks?
- SwiftUI has some built in shapes (
Circlefor example) that conform to the
Shapeprotocol and have their own modifiers, since all shapes are effectively rendered as
Views, modifiers of shapes can be applied to views and vice versa.
Stylingis used to convert shapes into views, available predefined shapes are:
- We can create custom shapes by conforming to the
Shapeprotocol and implementing the function
path(in:)that draws a path representing the desired shape.
- By implementing the
animatableDataproperty in our custom shapes, we can tell SwiftUI how this shape can be interpolated for animations.
- When a view appears/disappears, the default transition animation is to fade-in/fade-out. We can customize these transitions by creating a custom
ViewModifierand specifying how the view should change between the end states of the animation, then including that
Views defined as a function of some other view. This can be clearly seen when creating a custom
ViewModifierwhere we will have to implement a
bodyfunction, which takes a generic
Contentas a parameter (which is constrained to conform to
View) and returns
- By default, SwiftUI renders all views natively as either a
NSView(based on the platform it's rendering for), and that's typically what we want for normal controls like
TextFields. However, if we need better rendering performance for many views, then we can flatten our views inside a
drawingGroup, which is a special way of rendering graphics, where our view hierarchy is flatten into a single
NSView, which renders all our views using Metal.