30 Days of SwiftUI - Day 30: SwiftUI Challenge Day: Image Magic, Maps, and Local Notification!
Hello Swift learners! Today, we're putting our knowledge to the test with some practical questions based on what we've learned in the past few days. Let's sharpen our skills and solidify our understanding of image processing, maps, and advanced techniques.
Practical Tests
Test 1: Image Filter with Customization
Question: Create a SwiftUI view that loads an image from the user's photo library using PhotosPicker
. Apply a CIGaussianBlur
filter to the image. Allow the user to adjust the blur radius using a Slider
within a confirmationDialog
.
Test 2: Map with Editable Annotations
Question: Create a SwiftUI view that displays a map with annotations. Allow the user to select an annotation. When an annotation is selected, display a sheet with text fields to edit the annotation's latitude and longitude. Update the annotation's position on the map when the user saves the changes.
Test 3: MVVM with Local Notifications
Question: Create a simple task list app using MVVM. The ViewModel should manage the list of tasks and handle adding and deleting tasks. Add a button that schedules a local notification to remind the user of an upcoming task.
Answers and Complete SwiftUI Code
Answer 1: Image Filter with Customization
import SwiftUI
import PhotosUI
import CoreImage
import CoreImage.CIFilterBuiltins
struct ImageFilterTest: View {
@State private var selectedImage: UIImage?
@State private var selectedItem: PhotosPickerItem?
@State private var processedImage: Image?
@State private var showFilterOptions = false
@State private var blurRadius: Double = 5.0
var body: some View {
VStack {
if let image = processedImage {
image.resizable().scaledToFit().frame(width: 200, height: 200)
}
PhotosPicker("Select Photo", selection: $selectedItem, matching: .images)
.onChange(of: selectedItem) { _, newValue in
Task {
if let data = try? await newValue?.loadTransferable(type: Data.self), let uiImage = UIImage(data: data) {
selectedImage = uiImage
applyFilter()
}
}
}
Button("Filter Options") {
showFilterOptions = true
}
}
.confirmationDialog("Filter Options", isPresented: $showFilterOptions) {
Slider(value: $blurRadius, in: 0...20) {
Text("Blur Radius")
}
Button("Apply") {
applyFilter()
}
Button("Cancel", role: .cancel) {}
} message: {
Text("Adjust blur radius")
}
}
func applyFilter() {
guard let inputImage = selectedImage, let ciImage = CIImage(image: inputImage) else { return }
let filter = CIFilter.gaussianBlur()
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(blurRadius, forKey: kCIInputRadiusKey)
if let outputCIImage = filter.outputImage, let cgImage = CIContext().createCGImage(outputCIImage, from: outputCIImage.extent) {
processedImage = Image(cgImage, scale: inputImage.scale, orientation: .up)
}
}
}
struct ImageFilterTest_Previews: PreviewProvider {
static var previews: some View {
ImageFilterTest()
}
}
Answer 2: Map with Editable Annotations
import SwiftUI
import MapKit
struct EditableAnnotation: Identifiable {
let id = UUID()
var coordinate: CLLocationCoordinate2D
}
struct MapAnnotationEditTest: View {
@State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
@State private var annotations = [
EditableAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194)),
EditableAnnotation(coordinate: CLLocationCoordinate2D(latitude: 37.7858, longitude: -122.4065))
]
@State private var selectedAnnotation: EditableAnnotation?
@State private var editLatitude = ""
@State private var editLongitude = ""
var body: some View {
Map(coordinateRegion: $region, annotationItems: annotations, selection: $selectedAnnotation) { annotation in
MapMarker(coordinate: annotation.coordinate)
}
.sheet(item: $selectedAnnotation) { annotation in
VStack {
TextField("Latitude", text: $editLatitude)
TextField("Longitude", text: $editLongitude)
Button("Save Changes") {
if let latitude = Double(editLatitude), let longitude = Double(editLongitude) {
if let index = annotations.firstIndex(where: {$0.id == annotation.id}) {
annotations[index].coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}
selectedAnnotation = nil
}
}
.padding()
}
.onAppear {
editLatitude = "\(annotation.coordinate.latitude)"
editLongitude = "\(annotation.coordinate.longitude)"
}
}
}
}
struct MapAnnotationEditTest_Previews: PreviewProvider {
static var previews: some View {
MapAnnotationEditTest()
}
}
Answer 3: MVVM with Local Notifications
import SwiftUI
import UserNotifications
struct Task: Identifiable {
let id = UUID()
var title: String
}
@Observable
class TaskViewModel {
var tasks = [Task]()
func addTask(title: String) {
tasks.append(Task(title: title))
}
func deleteTask(at offsets: IndexSet) {
tasks.remove(atOffsets: offsets)
}
func scheduleNotification(taskTitle: String) {
let content = UNMutableNotificationContent()
content.title = "Task Reminder"
content.body = "Don't forget: \(taskTitle)"
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error scheduling notification: \(error.localizedDescription)")
} else {
print("Notification scheduled")
}
}
}
}
struct TaskListView: View {
@State private var viewModel = TaskViewModel()
@State private var newTaskTitle = ""
var body: some View {
VStack {
TextField("New Task", text: $newTaskTitle)
Button("Add Task") {
viewModel.addTask(title: newTaskTitle)
viewModel.scheduleNotification(taskTitle: newTaskTitle)
newTaskTitle = ""
}
List {
ForEach(viewModel.tasks) { task in
Text(task.title)
}
.onDelete(perform: viewModel.deleteTask)
}
}
.onAppear(perform: requestAuthorization)
}
func requestAuthorization() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
if success {
print("Authorization granted!")
} else if let error = error {
print(error.localizedDescription)
}
}
}
}
struct TaskListView_Previews: PreviewProvider {
static var previews: some View {
TaskListView()
}
}
Key Takeaways
- Image Processing: We combined photo library access with Core Image filtering, allowing for user customization.
- Map Annotations: We implemented editable map annotations, demonstrating how to interact with map data.
- MVVM and Notifications: We built a task list app using MVVM and added local notifications, showcasing how to combine architectural patterns with system features.
By working through these practical tests, you've strengthened your understanding of key SwiftUI concepts. Keep practicing, and you'll be building more complex and engaging apps!
Follow me on Linkedin: igatitech 🚀🚀🚀
Comments
Post a Comment