The best way to learn SwifUI is to write a program in SwiftUI
Introducing SwiftUI: Building Your First App
Note: Some of the APIS used in the video have been deprecated in subsequent versions. The code in this article has been adjusted according to the latest VERSION of the API to ensure that the Demo project can run
One: Write the first SwiftUI program
- Create the SwiftUI project
-
On the left is the code area
-
On the right side is the Canvas
As the code is written, the Canvas on the right shows a real-time UI preview of the code
-
Write UI layout code
-
Add UI controls by dragging and dropping
- VStack: A common layout element used by SwiftUI for vertically stacking views
- HStack: horizontal stack view
-
CMD + click VStack, insert HStack
HStack { VStack { Text("Rooms") Text("20 people")}}Copy the code
-
Add an image to the left of the text
HStack { // 'photo' is a picture in the system's own resource library Image(systemName: "photo") VStack { Text("Rooms") Text("20 people")}}Copy the code
-
Change the VStack alignment to the left on the Canvas
-
Sets the font size of Text
HStack { Image(systemName: "photo") VStack(alignment: .leading) { Text("Rooms") Text("20 people") .font(.subheadline) } } Copy the code
We call.font(.subheadline) the modifier, which is used to define the appearance or behavior of the view
-
Set the color of Text to secondary
HStack { Image(systemName: "photo") VStack(alignment: .leading) { Text("Rooms") Text("20 people") .font(.subheadline) .foregroundColor(.secondary) } } Copy the code
-
Replace HStack with List
-
-
Setting the data source
-
Adding Room model
In SwiftUI, you need to have the model follow the Identifiable protocol, implementing the ID attribute
-
Use Room data in the ContentView to present the UI
When the code changes significantly (such as adding attributes), Xcode pauses the preview until we are ready to update it again (click the Resum button).
-
-
Enrich UI content
-
Set image corners to be rounded
-
By dragging the Modifier library to achieve
-
-
Set Navigation, NavigationTitle, and jump for each cell
NavigationView { List(rooms) { room in NavigationLink(destination: Text(room.name)) { Image(systemName: "photo") .cornerRadius(8.0) VStack(alignment: .leading) { Text(room.name) Text("\(room.capacity) people") .font(.subheadline) .foregroundColor(.secondary) } } } .navigationTitle(Text("Rooms"))}Copy the code
-
-
Enter real-time mode to view the effect
-
Commissions subviews into a separate view
-
Create a new page: RoomDetail
struct RoomDetail: View { let room: Room var body: some View { Image(room.imageName) .resizable() .aspectRatio(contentMode: .fit) } } struct RoomDetail_Previews: PreviewProvider { static var previews: some View { RoomDetail(room: testData[0])}}Copy the code
-
Set the navigationBarTitle
Image(room.imageName) .resizable() .aspectRatio(contentMode: .fit) .navigationBarTitle(Text(room.name)) Copy the code
The page will not be updated at this point because there is no NavigationView in the RoomDetail as a context
-
Add the NavigationView context in the preview
struct RoomDetail_Previews: PreviewProvider { static var previews: some View { NavigationView { RoomDetail(room: testData[0]) } } } Copy the code
-
Change the navigationBarTitle display mode
-
-
Change the jump to RoomCell to go to the RoomDetail page
struct RoomCell: View { let room: Room var body: some View { NavigationLink(destination: RoomDetail(room: room)) { / /...}}}Copy the code
Two: How Swift UI works
2.1 the View
-
A View Defines a Piece of UI
In SwiftUI, a View is a structure that follows the View protocol, rather than a class that inherits from the base class
-
A View Defines its Dependencies
2.2 Status Attributes
-
@State
-
When SwiftUI sees a view with an @state State variable, it allocates storage for that variable in the name of the view.
- The green part is the APP’s memory
- The purple is the memory managed by SwiftUI
SwiftUI can see that the @state variable is being read and written, and SwiftUI knows that Zoom is reading from the body. SwiftUI will refresh the render with the new State value when the @state variable changes.
Example: in RoomDetail, click on the picture to modify the fill mode
struct RoomDetail: View {
let room: Room
@State private var zoomed = false
var body: some View {
Image(room.imageName)
.resizable()
.aspectRatio(contentMode: zoomed ? .fit : .fill) // Modify the fill mode according to the zoomed value
.navigationBarTitle(Text(room.name), displayMode: .inline)
.onTapGesture {
self.zoomed.toggle() // Click to modify the zoomed value}}}Copy the code
2.3 Source of facts
In SwiftUI, the UI can be in different states based on different data. We call the data used to draw the UI “fact sources”. “Fact sources” consist of state variables and models.
-
Attributes can be simply divided into Source of Truth and Derived Value.
The Zoomed variable is a fact source from which the contentMode is derived. When the system sees a zoomed variable change, SwiftUI frame requests a new body, refreshes the render, regenerates a new aspect ratio view, and then overrides the contentMode.
Like room detail, the room property is also a derived value.
-
Data Flow Primitives
SwiftUI is data-driven, not event-driven
Iii: Improve Rooms APP
-
Increase the animation
.onTapGesture { withAnimation { self.zoomed.toggle() } } Copy the code
-
Add a ZStack
ZStack(alignment: .topLeading) { Image(room.imageName) .resizable() .aspectRatio(contentMode: zoomed ? .fit : .fill) .navigationBarTitle(Text(room.name), displayMode: .inline) .onTapGesture { withAnimation { self.zoomed.toggle() } } Image(systemName: "video.fill") .font(.title) .padding(.all) } Copy the code
-
Fixed the icon position
Image(room.imageName) .frame(minWidth:0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) Copy the code
Buiding – > Buiding Custom Views with SwiftUI
-
Preview multiple views simultaneously
struct RoomDetail_Previews: PreviewProvider { static var previews: some View { Group { NavigationView { RoomDetail(room: testData[0])}NavigationView { RoomDetail(room: testData[1])}}}}Copy the code
-
Increase the dynamic effect
- Add transition motion to video ICONS
if room.hasVideo && ! zoomed { Image(systemName: "video.fill") .font(.title) .padding(.all) .transition(.move(edge: .leading)) }Copy the code
-
Prolong the motion of the picture
.onTapGesture { withAnimation(.easeInOut(duration: 2)) { self.zoomed.toggle() } } Copy the code
-
Dynamic increase
Monitor changes to the data model and update the UI in real time
-
Create RoomStore to store the Room model
import SwiftUI class RoomStore { var rooms: [Room] init(rooms: [Room] = []) { self.rooms = rooms } } Copy the code
-
Observe the ObservableObject protocol
class RoomStore: ObservableObject { @Published var rooms: [Room] / /... } Copy the code
-
Declare the type EnvironmentObject variable
@EnvironmentObject var store: RoomStore Copy the code
-
Pass in the type EnvironmentObject variable
struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() .environmentObject(RoomStore(rooms: testData)) } } Copy the code
-
Add a button to the list
List { Button(action:{ }) { Text("Add Room")}ForEach creates a view ForEach of its collection items ForEach(store.rooms) { room in RoomCell(room: room) } } Copy the code
Button(action:addRoom) { Text("Add Room")}func addRoom(a) { store.rooms.append(Room(name: "Hall 2", capacity: 2000))}Copy the code
-
-
Change the style of the List
-
Modify the listStyle
NavigationView {List {// ···}.navigationBarTitle(Text("Rooms")).listStyle(GroupedListStyle())}Copy the code
-
Set up the group
List { Section { Button(action:addRoom) { Text("Add Room")}}Section { ForEach(store.rooms) { room in RoomCell(room: room) } } } Copy the code
-
-
Dynamic deletion
func delete(at offsets: IndexSet) { store.rooms.remove(atOffsets: offsets) Copy the code
ForEach(store.rooms) { room in RoomCell(room: room) } .onDelete(perform: delete) Copy the code
-
Set the NavigationBarItem
NavigationView { List { } .navigationBarItems(trailing: EditButton()) } Copy the code
-
Support for list reordering
func move(from source: IndexSet, to destination: Int) { store.rooms.move(fromOffsets: source, toOffset: destination) } Copy the code
ForEach(store.rooms) { room in RoomCell(room: room) } .onDelete(perform: delete) .onMove(perform: move) Copy the code
-
Setting the Preview environment
Group { ContentView() .environmentObject(RoomStore(rooms: TestData)) // Environment ContentView().environmentobject (RoomStore(rooms: TestData)).environment(\.sizecategory,.extraextralarge) // Dark Mode ContentView().environmentobject (RoomStore(rooms: TestData)).environment(\.colorScheme,.dark) // Layout direction ContentView().environmentobject (RoomStore(rooms: testData)) .environment(\.layoutDirection, .rightToLeft) .environment(\.locale, Locale(identifier: "ar")) }Copy the code
conclusion
-
SwiftUI’s four main design principles:
- Declarative
- Compositional
- Automatic
- Consistent
-
SwiftUI uses declarative grammar
-
In SwiftUI, Xcode Preview lets us browse, edit, and debug apps without even having to run a project