Building Declarative UIs with SwiftUI
1. Creating SwiftUI Views
| Property | Values | Description |
|---|---|---|
| View | Text("Hello, World!") |
Basic SwiftUI view displaying text |
| View | Image("icon") |
Displays an image resource |
| View | Button("Tap me") { ... } |
Interactive button with action |
| View | NavigationView { ... } |
Container for navigation stack |
| View | ForEach(items) { item in ... } |
Dynamic list rendering |
Example: Basic Text View
import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, World!")
}
}
Example: Button with Action
struct ContentView: View {
@State private var count = 0
var body: some View {
Button("Tap me") {
count += 1
}
}
}
View protocol.
2. Using Text and Label Views
| Property | Values | Description |
|---|---|---|
| Text | Text("Hello, World!") |
Displays static or dynamic text content |
| Label | Label("Save", systemImage: "square.and.arrow.down") |
Combines text with a system or custom image |
Example: Basic Text Usage
struct ContentView: View {
var body: some View {
Text("Welcome to SwiftUI")
.font(.largeTitle)
}
}
Example: Dynamic Text with Binding
struct CounterView: View {
@State private var count = 0
var body: some View {
Text("Count: \(count)")
.onTapGesture {
count += 1
}
}
}
Example: Label with System Image
struct SaveButton: View {
var body: some View {
Label("Save", systemImage: "square.and.arrow.down")
.padding()
}
}
Text view supports localization via Text(LocalizedStringKey).
Text inside a ForEach loop without a unique identifier; use id modifier to prevent UI flickering.
3. Using Button and Toggle
| Property | Values | Description |
|---|---|---|
| Button | Text("Tap me") |
Basic button with text label |
| Button | Image("icon") |
Button with image label |
| Button | Label("Save", systemImage: "square.and.arrow.down") |
Button with system image and text |
| Toggle | isOn: Binding<Bool> |
Binds to a Boolean state variable |
| Toggle | label: Text |
Label for the toggle control |
Example: Basic Button
struct ContentView: View {
var body: some View {
Button("Press Me") {
print("Button tapped!")
}
}
}
Example: Toggle with Binding
struct ContentView: View {
@State private var isToggled = false
var body: some View {
Toggle("Enable Feature", isOn: $isToggled)
}
}
Example: Button with Image
struct ContentView: View {
var body: some View {
Button(action: {
// Action logic here
}) {
Image(systemName: "heart.fill")
.foregroundColor(.red)
}
}
}
Button in SwiftUI is a stateless component that triggers an action when tapped. It supports various content types including text, images, and labels.
Toggle control is used to switch between two states (on/off) and requires a binding to a Boolean value.
4. Working with Image Views
| Property | Values | Description |
|---|---|---|
| Image | Image("imageName") |
Displays an image from the asset catalog |
| Image(systemName:) | Image(systemName: "heart.fill") |
Displays a system SF Symbol image |
| Image(uiImage:) | Image(uiImage: UIImage(named: "customImage")!) |
Displays a UIKit UIImage |
Example: Displaying an Image from Asset Catalog
struct ContentView: View {
var body: some View {
Image("swiftLogo")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
Example: Using SF Symbols
struct ContentView: View {
var body: some View {
Image(systemName: "sun.max.fill")
.foregroundColor(.yellow)
.font(.system(size: 50))
}
}
Example: Resizing and Cropping Images
struct ContentView: View {
var body: some View {
Image("heroImage")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 200, height: 150)
}
}
5. Using VStack, HStack, ZStack
SwiftUI provides three fundamental stacking views to arrange UI elements: VStack, HStack, and ZStack. These views are used to build declarative layouts by stacking elements vertically, horizontally, or on top of each other.
| View | Description | Usage |
|---|---|---|
| VStack | Arranges child views vertically (top to bottom). | VStack { Text("Top"); Text("Bottom") } |
| HStack | Arranges child views horizontally (left to right). | HStack { Text("Left"); Text("Right") } |
| ZStack | Overlays child views on top of each other, centered by default. | ZStack { Text("Background"); Text("Foreground") } |
Example: VStack Usage
struct ContentView: View {
var body: some View {
VStack(spacing: 10) {
Text("Header")
.font(.largeTitle)
Text("Content")
.font(.body)
Text("Footer")
.font(.caption)
}
}
}
Example: HStack Usage
struct ContentView: View {
var body: some View {
HStack {
Image(systemName: "star")
.foregroundColor(.yellow)
Text("Rating")
.font(.subheadline)
}
}
}
Example: ZStack Usage
struct ContentView: View {
var body: some View {
ZStack {
Color.blue
.frame(width: 200, height: 200)
Text("Overlay")
.foregroundColor(.white)
.font(.largeTitle)
}
}
}
VStack, HStack, and ZStack can be nested to create complex layouts. Each stacking view supports alignment, spacing, and padding modifiers to customize the arrangement.
6. Using List and ForEach
| Property | Value | Description |
|---|---|---|
| List | View |
A SwiftUI view that displays a list of data |
| ForEach | View |
A SwiftUI view that iterates over a collection to build a list of views |
Example: Basic List
struct ContentView: View {
let items = ["Item 1", "Item 2", "Item 3"]
var body: some View {
List(items, id: \.self) { item in
Text(item)
}
}
}
Example: ForEach with Custom Data
struct ContentView: View {
let items = [
Item(id: 1, name: "Apple"),
Item(id: 2, name: "Banana"),
Item(id: 3, name: "Cherry")
]
var body: some View {
List(items, id: \.id) { item in
Text(item.name)
}
}
}
struct Item: Identifiable {
let id: Int
let name: String
}
Example: ForEach with Nested Views
struct ContentView: View {
let items = ["Red", "Green", "Blue"]
var body: some View {
VStack {
ForEach(items, id: \.self) { color in
Text(color)
.foregroundColor(.init(color))
}
}
}
}
List and ForEach are fundamental for building dynamic UIs in SwiftUI. They provide a declarative way to handle collections of data.
7. Working with ScrollView
ScrollView is a fundamental SwiftUI view for creating scrollable content. It supports both vertical and horizontal scrolling, and can be customized with various modifiers to control behavior and appearance.
| Modifier | Description | Example |
|---|---|---|
| .scrollContentBackground(_: | Controls the background of the scrollable content area. | .scrollContentBackground(.hidden) |
| .scrollIndicators(_: | Controls visibility of scroll indicators. | .scrollIndicators(.never) |
| .scrollPosition(_: | Manages the current scroll position. | .scrollPosition(.constant(.top)) |
Example: Basic ScrollView with Text
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView {
ForEach(0..<100) { index in
Text("Item \\(index)")
.padding()
}
}
}
}
Example: Horizontal ScrollView
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView(.horizontal) {
HStack {
ForEach(0..<20) { index in
Rectangle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.overlay(
Text("\\(index)")
.foregroundColor(.white)
)
}
}
}
}
}
8. Using NavigationStack and NavigationLink
| Property | Value | Description |
|---|---|---|
| NavigationStack | NavigationStack |
Container for navigation views in SwiftUI |
| NavigationLink | NavigationLink |
Creates a link that navigates to another view |
Example: Basic NavigationStack with NavigationLink
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationStack {
List {
NavigationLink("Go to Detail", destination: DetailView())
}
}
}
}
struct DetailView: View {
var body: some View {
Text("This is the detail view")
}
}
Example: Navigation with Data
struct ContentView: View {
let items = ["Item 1", "Item 2", "Item 3"]
var body: some View {
NavigationStack {
List(items, id: \.self) { item in
NavigationLink(item, destination: DetailView(item: item))
}
}
}
}
struct DetailView: View {
let item: String
var body: some View {
Text("You selected: \(item)")
}
}
9. Using TabView for Navigation
TabView is a powerful SwiftUI component that allows you to create tab-based navigation interfaces, similar to the native iOS tab bar. It's particularly useful for organizing content into distinct sections or views that users can switch between.
| Property | Description | Example |
|---|---|---|
| TabView | Root view for creating tab-based navigation | TabView { ... } |
| .tabItem | Defines the label and icon for each tab | Text("Home").tabItem { Image(systemName: "house") } |
| .tag | Uniquely identifies each tab for selection | Text("Profile").tag("profile").tabItem { Image(systemName: "person") } |
| .selection | Binds to a state variable to control which tab is selected | @State private var selection = "home" |
Example: Basic TabView Implementation
import SwiftUI
struct ContentView: View {
@State private var selection = "home"
var body: some View {
TabView(selection: $selection) {
Text("Home Content")
.tabItem {
Image(systemName: "house")
Text("Home")
}
.tag("home")
Text("Profile Content")
.tabItem {
Image(systemName: "person")
Text("Profile")
}
.tag("profile")
}
}
}
.tag is used to identify each tab and should be unique within the TabView. It is used in conjunction with .selection to control which view is displayed.
Example: TabView with Nested Navigation
import SwiftUI
struct ContentView: View {
@State private var selection = "home"
var body: some View {
TabView(selection: $selection) {
NavigationStack {
Text("Home")
.navigationTitle("Home")
}
.tabItem {
Image(systemName: "house")
Text("Home")
}
.tag("home")
NavigationStack {
Text("Settings")
.navigationTitle("Settings")
}
.tabItem {
Image(systemName: "gear")
Text("Settings")
}
.tag("settings")
}
}
}
10. Applying View Modifiers
| Modifier | Description | Usage |
|---|---|---|
| .padding() | Adds padding around the view | Text("Hello").padding() |
| .frame(width:height:alignment:) | Sets the frame size and alignment | Image("icon").frame(width: 50, height: 50) |
| .background() | Applies a background to the view | Text("Hello").background(Color.blue) |
| .foregroundColor() | Changes the text color | Text("Hello").foregroundColor(.red) |
| .cornerRadius() | Rounds the corners of a view | Rectangle().cornerRadius(10) |
| .border() | Adds a border around the view | Text("Hello").border(Color.gray, width: 2) |
| .shadow() | Adds a shadow effect | Text("Hello").shadow(radius: 5) |
| .overlay() | Overlays another view on top | Text("Hello").overlay(Rectangle().fill(Color.red)) |
Example: Combining Modifiers
Text("Hello World")
.padding()
.frame(width: 200, height: 100)
.background(Color.gray)
.cornerRadius(10)
.foregroundColor(.white)
11. Understanding View Composition
SwiftUI's declarative UI framework is built around the concept of view composition, where complex user interfaces are constructed by combining smaller, reusable views. This approach promotes modularity, reusability, and maintainability of UI code.
| View Type | Description | Usage Example |
|---|---|---|
| View | The base protocol for all SwiftUI views. Every view must conform to this protocol. | struct ContentView: View { ... } |
| Container View | Views that can contain other views, such as VStack, HStack, and ZStack. |
|
| Leaf View | Views that do not contain other views, such as Text, Image, and Button. |
Text("Hello World") |
Example: Composing a Simple UI
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
Text("Welcome to SwiftUI")
.font(.largeTitle)
HStack {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
Text("5 Stars")
}
Button("Tap Me") {
print("Button tapped!")
}
}
}
}
body property in a View must return a single view or a view builder expression. SwiftUI uses the some View return type to ensure that only valid views are returned.Key Concepts of View Composition
- Modularity: Views can be broken down into smaller, reusable components.
- Nesting: Views can be nested inside other views to create complex layouts.
- Reusability: Custom views can be reused across different parts of an app.
- Flexibility: SwiftUI's view composition allows for dynamic and adaptive UIs.