4.3 KiB
Creating Views
Views are the building blocks of your application.
A view can be as simple as the Text widget you have seen in the previous tutorial, or as complex as the whole content of a single window.
Add Views to a Window
You've already seen how to add views to a window:
import Adwaita
@main
struct HelloWorld: App {
let id = "io.github.david_swift.HelloWorld"
var app: GTUIApp!
var scene: Scene {
Window(id: "content") { _ in
// These are the views:
HeaderBar.empty()
Text("Hello, world!")
.padding()
}
}
}
In this example, the widgets HeaderBar and Text are used.
padding is a view modifier, a function that modifies a view, which adds some padding around the text.
Create Custom Views
While directly adding widgets into the Window's body might work for simple "Hello World" apps,
it can get very messy very quickly.
You can create custom views by declaring types that conform to the View protocol:
// A custom view named "ContentView":
struct ContentView: View {
var view: Body {
HeaderBar.empty()
Text("Hello, world!")
.padding()
}
}
Properties
As every structure in Swift, custom views can have properties:
struct HelloView: View {
// The property "text":
var text: String
var view: Body {
Text("Hello, \(text)!")
.padding()
}
}
This view can be called via HelloView(text: "world") in another view.
State
Whenever you want to modify a property that is stored in the view's structure from your view,
wrap the property with the @State property wrapper:
struct MyView: View {
// This property can be modified form within the view:
@State private var text = "world"
var view: Body {
Text("Hello, \(text)!")
.padding()
Button("Change Text") {
text = Bool.random() ? "world" : "John"
}
.padding(10, .horizontal.add(.bottom))
}
}
In this example, the text property is set whenever you press the "Change Text" button.
Change the State in Child Views
You can access state properties in child views in the same way as you would access any other property
if the child view cannot modify the state (HelloView is defined above):
struct MyView: View {
@State private var text = "world"
var view: Body {
// "HelloView" can read the "text" property:
HelloView(text: text)
Button("Change Text") {
text = Bool.random() ? "world" : "John"
}
.padding(10, .horizontal.add(.bottom))
}
}
If the child view should be able to modify the state, use the Binding property wrapper in the child view
and pass the property with a dollar sign ($) to that view.
struct MyView: View {
@State private var text = "world"
var view: Body {
HelloView(text: text)
// Pass the editable text property to the child view:
ChangeTextView(text: $text)
}
}
struct ChangeTextView: View {
// Accept the editable text property:
@Binding var text: String
var view: Body {
Button("Change Text") {
// Binding properties can be used the same way as state properties:
text = Bool.random() ? "world" : "John"
}
.padding(10, .horizontal.add(.bottom))
}
}
If you have a more complex type and want to pass a property of the type as a binding, you can just access the property on the binding.
HelloView(text: $complexType.text)
Whenever you modify a state property (directly or indirectly through bindings), the user interface gets automatically updated to reflect that change.
Save State Values Between App Launches
It's possible to automatically save a value that conforms to Codable whenever it changes to a file.
The value in the file is read when the view containing the state value appears for the first time (e.g. when the user launches the app).
Use the following syntax, where "text" is a unique identifier.
@State("text") private var text = "world"
You can organize your content by specifying a custom folder path which will be appended to the XDG data home directory.
@State("text", folder: "io.github.david_swift.HelloWorld/my-view") private var text = "world"