Scene
A scene contains the view hierarchy of your app.
1 | import SwiftUI |
In this sample, body returns the primary scene WindowGroup
, which describes the view hierarchy of the sample’s main window.
WindowGroup: It provides platform-specific behaviors for your app, such as supporting multiple windows in macOS and iPadOS.
The computed body
property can return one or more primary and secondary scenes.
View Layout
Text and symbols
Text
A Text view displays read-only text.
1 | Text("Hamlet") |
Symbols
Symbols, such as the iconography
that SF Symbols
provides
You can customize their colors and sizes using standard view modifiers provided in SwiftUI.
Even though you specify a system or custom symbol in an Image, treat SF Symbols more like text.
1 | HStack { |
Labels
To use both text and a symbol to represent a single element in your app, use a Label.
1 | Label("Favorite Books", systemImage: "books.vertical") |
Controls
use the controlSize(_:)
modifier to make a control smaller or larger, or you can use the progressViewStyle(_:)
modifier to choose a linear or circular appearance for a progress bar.
There are general-purpose controls like
Menu
andLink
, and specialized views likeEditButton
andColorPicker
. Use these views to provide familiar UI elements rather than creating custom controls that you’ll need to maintain.
Images and shapes
Images
Display photos and other rich graphics in an Image. By default, an Image displays at the asset’s original size. You can add modifiers like resizable(capInsets:resizingMode:)
and scaledToFit()
or scaledToFill()
to scale it to the available space.
1 | Image("Yellow_Daisy") |
accessing an image asset from a server, use an AsyncImage to handle the download while keeping your app responsive.
Fitting images into available space
Shapes
SwiftUI provides several common shapes, and modifiers to change their size, color, or other aspects of their appearance.
1 | HStack { |
The HStack
provides some default spacing between each shape and, to give each shape a square space to fill, the aspectRatio(_:contentMode:)
modifier **makes the HStack three times as wide **as it is tall.
For an example of the rich possibilities of composing shapes, see Drawing paths and shapes.
Scaling views to complement text
When you need to provide a numeric value that adapts to the environment’s effective font size, use the ScaledMetric
property wrapper.
1 | import SwiftUI |
using ScaledMetric to scale dimensions in proportion to text, see Applying custom fonts to text.
Layering content
Define an overlay
When you arrange content on the z-axis
, you can use a ZStack
or an overlay
or background
modifier, like overlay(alignment:content:)
or background(_:in:fillStyle:)
,respectively. A ZStack
sizes each view based on the available space, without consideration for the other views in the stack. To specify that the size of some content depends on the size of other content, define this secondary content inside one of the overlay or background modifiers.
ZStack可以定义stack里每个view的大小
如果一个view的大小依赖于另一个,使用overlay或background
背景修饰符将其内容放在它所修改的视图的后面,而不是前面
hide a View
the data determining whether to hide a view might be a Binding
, or an Environment
value.
1 | import SwiftUI |
Use an opacity
modifier when you don’t want other content to shift around as the view appears or disappears.
1 | import SwiftUI |
Organzing and aligning content with stacks
nested stacks
firstTextBaseline
A guide that marks the top-most text baseline in a view.
1 | struct VerticalAlignmentFirstTextBaseline: View { |
Add padding around subviews
You can add padding to the outer edges of a view to put some space between that view and any neighboring views
, or to the edge of a window or scene.
padding(_:_:)
without any parameters puts space around all four edges.
Add a view to create space
This Spacer()
between views pushes the content views as far apart as possible.
State and data flow
State and binding
Indicate data dependencies in a view using state
, and share those dependencies with other views using bindings
.
Separate properties and imperative code from the view
当视图需要管理多个状态数据时,在单独的特定于视图的结构中(a separate view-specific structure)管理这些数据可能会很有帮助。这种方法通过将属性和命令式代码(imperative code)移出视图,使视图的声明性接口代码(declarative interface code)更具可读性。它还有助于使单元测试状态更改更容易实现。
Bind the view to its state data
1 | import SwiftUI |
Binding
属性包装器为视图所需的数据提供了双向的读写绑定。RecipeEditor
将绑定变量config
传递给RecipeEditorForm
。它将变量作为绑定传递,通过在变量名config
前加上$
符号来表示。由于RecipeEditorForm
将config
作为绑定接收,因此表单可以向config
读写数据。
Create a state variable in another view
1 | import SwiftUI |
recipeEditorConfig
声明包含State
属性包装器的属性,它告诉SwiftUI
创建和管理recipeEditorConfig
的实例。每次视图状态发生变化,也就是说,recipeEditorConfig
包含的数据发生变化时,SwiftUI
将重新初始化视图,将recipeEditorConfig
实例重新连接到视图,并重新构建在计算属性body
中定义的视图,该属性反映了数据的当前状态。有关更多信息,请参见模型数据。
1 | var body: some View { |
修饰符表(ispresent:onDismiss:content:
)接收一个由美元符号($
)前缀表示的绑定。此绑定允许工作表读取和写入属性。例如,当一个人通过向下滑动来取消工作表时,工作表将设置recipeEditorConfig.ispresent
to false
。这个改变导致SwiftUI重新初始化并重建视图。
Creating a custom input control that binds to a value
1 | import SwiftUI |
- By defining rating as a
binding
variable,StarRating
can read and write the value even though another view is responsible for creating the value. - The
id
parameter is of typeID
, which isHashable
. TheForEach
structure uses this parameter to identify the data, that is, the integer values 1 through 5. The parameter value is the identity key path\.self
, which specifies an Int instance for each integer. Because Int is hashable, using this key path satisfies the requirements of theForEach
initializer methodinit(_:id:content:)
. And because the data is an increasing range of integers that will never have duplicate values, it’s okay to use each integer value as its identifier.
Defining the source of truth using a custom binding
Limit its use to use cases where using a state variable or object isn’t possible.
1 | import SwiftUI |
-
The computed
recipe
property doesn’t return aRecipe
. Instead, it returns a custom Binding of typeRecipe
. This allows the view to share therecipe
as a source of truth with other views. -
A
Binding
provides read and write access to a value.uses theinit(get:set:)
initializer method to create a binding. -
Because the computed property
recipe
returns aBinding
, it isn’t necessary to include the dollar sign ($)
prefix that’s required when passing a state variable as a binding.RecipeDetailView(recipe: recipe)
-
A
wrappedValue
is the underlying value referenced by the binding.recipe.wrappedValue.title
gets the wrappedValue of the recipe bindingThe navigationTitle(_:) modifier accepts a string value not a binding to a string value