30 Days of SwiftUI - Day 24: Crafting, Editing, and Storing: Building Custom Components, Mastering SwiftData, and More!
Hello Swift creators! Today, we're diving deep into some exciting areas of SwiftUI development. We'll build custom components, handle multi-line text input, explore SwiftData, and even create a custom star rating component. Let's get started!
Dynamic Duo: Creating a Custom Component with @Binding
We'll learn how to create reusable components that can modify parent view state using @Binding
.
Example: Custom Toggle Component
import SwiftUI
struct CustomToggle: View {
@Binding var isOn: Bool
let label: String
var body: some View {
HStack {
Text(label)
Spacer()
Toggle("", isOn: $isOn)
}
.padding()
}
}
struct CustomToggleDemo: View {
@State private var isEnabled = false
var body: some View {
VStack {
CustomToggle(isOn: $isEnabled, label: "Enable Feature")
Text("Feature is \(isEnabled ? "enabled" : "disabled")")
}
}
}
struct CustomToggleDemo_Previews: PreviewProvider {
static var previews: some View {
CustomToggleDemo()
}
}
Visual Representation:
- A toggle with a label, and a text view that shows the state.
Unleashing Words: Accepting Multi-Line Text Input with TextEditor
We'll learn how to handle multi-line text input using TextEditor
.
Example: Multi-Line Text Input
import SwiftUI
struct MultiLineText: View {
@State private var text = "Enter your text here..."
var body: some View {
TextEditor(text: $text)
.padding()
.border(Color.gray, width: 1)
.frame(height: 200)
}
}
struct MultiLineText_Previews: PreviewProvider {
static var previews: some View {
MultiLineText()
}
}
Visual Representation:
- A text editor with a border, allowing multi-line text input.
Data Persistence Made Easy: Introduction to SwiftData and SwiftUI
SwiftData is a powerful data persistence framework that integrates seamlessly with SwiftUI.
Conceptual Overview:
- SwiftData uses the
@Model
macro to define data models. - It provides a
ModelContainer
for managing the data store. @Query
is used to fetch data from the store.
Movie Magic: Creating Movies with SwiftData
Let's create a simple movie tracking app using SwiftData.
Example: SwiftData Movie Model
import SwiftUI
import SwiftData
@Model
class Movie {
var title: String
var director: String
var year: Int
init(title: String, director: String, year: Int) {
self.title = title
self.director = director
self.year = year
}
}
struct MovieList: View {
@Environment(\.modelContext) var modelContext
@Query var movies: [Movie]
var body: some View {
List(movies) { movie in
Text(movie.title)
}
Button("Add Movie"){
let movie = Movie(title: "New Movie", director: "New Director", year: 2024)
modelContext.insert(movie)
}
}
}
Visual Representation:
- A list of movies fetched from SwiftData, and a button to add new movies.
Star Power: Adding a Custom Star Rating Component
We'll create a custom star rating component using SwiftUI.
Example: Star Rating Component
import SwiftUI
struct StarRating: View {
@Binding var rating: Int
let maxRating = 5
var body: some View {
HStack {
ForEach(1...maxRating, id: \.self) { index in
Image(systemName: index <= rating ? "star.fill" : "star")
.foregroundColor(.yellow)
.onTapGesture {
rating = index
}
}
}
}
}
struct StarRatingDemo: View {
@State private var rating = 3
var body: some View {
StarRating(rating: $rating)
Text("Rating: \(rating)")
}
}
struct StarRatingDemo_Previews: PreviewProvider {
static var previews: some View {
StarRatingDemo()
}
}
Visual Representation:
- A row of stars that can be tapped to set the rating.
Data Fetching: Building a List with @Query
We'll use @Query
to fetch Movie
objects from the SwiftData store and display them in a List
.
Example: Querying and Displaying Movies
import SwiftUI
import SwiftData
@Model
class Movie {
var title: String
var director: String
var year: Int
init(title: String, director: String, year: Int) {
self.title = title
self.director = director
self.year = year
}
}
struct MovieQueryList: View {
@Environment(\.modelContext) var modelContext
@Query var movies: [Movie] // Fetch movies using @Query
var body: some View {
NavigationStack {
List(movies) { movie in
NavigationLink(destination: MovieDetailView(movie: movie)) {
VStack(alignment: .leading) {
Text(movie.title)
.font(.headline)
Text("Director: \(movie.director)")
.font(.subheadline)
}
}
}
.navigationTitle("Movies")
.toolbar {
Button("Add Movie") {
let newMovie = Movie(title: "New Movie", director: "New Director", year: 2024)
modelContext.insert(newMovie)
}
}
}
}
}
struct MovieDetailView: View {
let movie: Movie
var body: some View {
VStack(alignment: .leading) {
Text(movie.title).font(.title)
Text("Director: \(movie.director)")
Text("Year: \(movie.year)")
}
.padding()
}
}
struct MovieQueryList_Previews: PreviewProvider {
static var previews: some View {
MovieQueryList()
.modelContainer(for: Movie.self) // provide a model container for previews
}
}
Movie Details: Showing Movie Details
We'll create a view to display detailed information about a movie.
Example: Movie Detail View
import SwiftUI
struct MovieDetailView: View {
let movie: Movie
var body: some View {
VStack(alignment: .leading) {
Text(movie.title).font(.title)
Text("Director: \(movie.director)")
Text("Year: \(movie.year)")
}
.padding()
}
}
struct MovieDetailViewWrapper: View {
let movie: Movie
var body: some View {
NavigationStack {
MovieDetailView(movie: movie)
}
}
}
Ordered Data: Sorting SwiftData Queries Using SortDescriptor
We'll learn how to sort SwiftData queries using SortDescriptor
.
Example: Sorting Movies by Year
import SwiftUI
import SwiftData
struct SortedMovieList: View {
@Environment(\.modelContext) var modelContext
@Query(sort: \Movie.year) var movies: [Movie]
var body: some View {
List(movies) { movie in
Text("\(movie.title) (\(movie.year))")
}
}
}
Data Removal: Deleting from a SwiftData Query
We'll learn how to delete items from a SwiftData query.
Example: Deleting Movies
import SwiftUI
import SwiftData
struct DeletableMovieList: View {
@Environment(\.modelContext) var modelContext
@Query var movies: [Movie]
var body: some View {
List {
ForEach(movies) { movie in
Text(movie.title)
}
.onDelete(perform: deleteMovies)
}
}
func deleteMovies(at offsets: IndexSet) {
for index in offsets {
modelContext.delete(movies[index])
}
}
}
Navigational Control: Using an Alert to Pop a NavigationLink
Programmatically
We'll use an alert to programmatically pop a NavigationLink
.
Example: Alert-Driven Navigation Pop
import SwiftUI
struct NavigationPop: View {
@State private var showAlert = false
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
Button("Show Alert") {
showAlert = true
}
}
.alert(isPresented: $showAlert) {
Alert(title: Text("Confirm"), message: Text("Pop View?"), primaryButton: .default(Text("Yes")) {
dismiss()
}, secondaryButton: .cancel())
}
}
}
struct NavLinkWrapper: View {
var body: some View {
NavigationStack{
NavigationLink("Go to Next", destination: NavigationPop())
}
}
}
🔥 Conclusion
Day 24 has been a deep dive into advanced SwiftUI development. We've covered custom components, multi-line text input, SwiftData, and more. Keep exploring, and you'll be building more complex and feature-rich apps!
Follow me on Linkedin: igatitech 🚀🚀🚀
Comments
Post a Comment