30 Days of SwiftUI - Day 27: Power Wrappers, Image Magic, and User Engagement: Advanced SwiftUI Techniques
Hello Swift innovators! Today, we're diving into some advanced SwiftUI topics, including property wrappers, image processing, and user engagement. Let's get started!
The Magic Behind: How Property Wrappers Become Structs
Property wrappers are a powerful feature in Swift that allow you to add extra functionality to properties. They're essentially structs that wrap a property and provide custom behavior.
Explanation:
- Property wrappers provide a layer of abstraction between the property and its underlying storage.
- They can perform tasks like validation, observation, and data transformation.
- SwiftUI uses property wrappers extensively for state management (
@State
,@Binding
,@Environment
, etc.). - Under the hood, property wrappers are structs that conform to the
propertyWrapper
protocol and have awrappedValue
property.
Example (Conceptual):
@propertyWrapper
struct Clamped<Value: Comparable> {
var value: Value
let range: ClosedRange<Value>
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: Value {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct ClampedValueDemo {
@Clamped(0...100) var number = 50
}
Reactive Updates: Responding to State Changes Using onChange()
onChange()
allows you to perform actions when a state variable changes.
Example: Responding to Text Field Changes
import SwiftUI
struct ChangeResponder: View {
@State private var text = ""
var body: some View {
TextField("Enter text", text: $text)
.onChange(of: text) { oldValue, newValue in
print("Text changed from \(oldValue) to \(newValue)")
}
.padding()
}
}
struct ChangeResponder_Previews: PreviewProvider {
static var previews: some View {
ChangeResponder()
}
}
Visual Representation:
- A text field that prints changes to the console.
User Choice: Showing Multiple Options with confirmationDialog()
confirmationDialog()
allows you to present multiple options to the user in a dialog.
Example: Confirmation Dialog
import SwiftUI
struct ConfirmationDialogDemo: View {
@State private var showDialog = false
@State private var selectedOption = "None"
var body: some View {
Button("Show Dialog") {
showDialog = true
}
.confirmationDialog("Select an option", isPresented: $showDialog) {
Button("Option 1") { selectedOption = "Option 1" }
Button("Option 2") { selectedOption = "Option 2" }
Button("Cancel", role: .cancel) { }
} message: {
Text("Please select an option.")
}
Text("Selected: \(selectedOption)")
}
}
struct ConfirmationDialogDemo_Previews: PreviewProvider {
static var previews: some View {
ConfirmationDialogDemo()
}
}
Visual Representation:
- A button that shows a confirmation dialog with options.
Image Processing: Integrating Core Image with SwiftUI
Core Image allows you to apply filters and effects to images.
Example: Core Image Filter
import SwiftUI
import CoreImage
import CoreImage.CIFilterBuiltins
struct CoreImageDemo: View {
@State private var inputImage: UIImage?
@State private var processedImage: Image?
var body: some View {
VStack {
if let image = processedImage {
image.resizable().scaledToFit().frame(width: 200, height: 200)
}
Button("Load Image") {
inputImage = UIImage(named: "ExampleImage") // Replace with your image
applyFilter()
}
}
}
func applyFilter() {
guard let inputImage = inputImage, let ciImage = CIImage(image: inputImage) else { return }
let filter = CIFilter.sepiaTone()
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(0.5, forKey: kCIInputIntensityKey)
if let outputCIImage = filter.outputImage, let cgImage = CIContext().createCGImage(outputCIImage, from: outputCIImage.extent) {
processedImage = Image(cgImage, scale: inputImage.scale, orientation: .up)
}
}
}
struct CoreImageDemo_Previews: PreviewProvider {
static var previews: some View {
CoreImageDemo()
}
}
Visual Representation:
- An image with a sepia tone filter applied.
Empty State Handling: Showing Empty States with ContentUnavailableView
ContentUnavailableView
provides a standard way to display empty states in your app.
Example: Empty State View
import SwiftUI
struct EmptyStateDemo: View {
@State private var items: [String] = []
var body: some View {
if items.isEmpty {
ContentUnavailableView("No Items", systemImage: "tray.fill", description: Text("Add items to see them here."))
} else {
List(items, id: \.self) { item in
Text(item)
}
}
}
}
struct EmptyStateDemo_Previews: PreviewProvider {
static var previews: some View {
EmptyStateDemo()
}
}
Visual Representation:
- An empty state view with a message and icon when the list is empty.
Photo Access: Loading Photos from the User's Photo Library
We'll learn how to access the user's photo library using PhotosPicker
.
Example: Photos Picker
import SwiftUI
import PhotosUI
struct PhotoPickerDemo: View {
@State private var selectedImage: UIImage?
@State private var selectedItem: PhotosPickerItem?
var body: some View {
VStack {
if let image = selectedImage {
Image(uiImage: image).resizable().scaledToFit().frame(width: 200, height: 200)
}
PhotosPicker("Select Photo", selection: $selectedItem, matching: .images)
.onChange(of: selectedItem) { oldValue, newValue in
Task {
if let data = try? await newValue?.loadTransferable(type: Data.self), let uiImage = UIImage(data: data) {
selectedImage = uiImage
}
}
}
}
}
}
struct PhotoPickerDemo_Previews: PreviewProvider {
static var previews: some View {
PhotoPickerDemo()
}
}
Visual Representation:
- A button that opens the photo picker, and an image view that displays the selected photo.
Content Sharing: How to Let the User Share Content with ShareLink
ShareLink
allows users to easily share content from your app.
Example: Share Link
import SwiftUI
struct ShareLinkDemo: View {
let textToShare = "Check out this awesome app!"
var body: some View {
ShareLink(item: textToShare) {
Label("Share", systemImage: "square.and.arrow.up")
}
}
}
struct ShareLinkDemo_Previews: PreviewProvider {
static var previews: some View {
ShareLinkDemo()
}
}
Visual Representation:
- A share button that opens the share sheet.
App Store Reviews: How to Ask the User to Leave an App Store Review
You can prompt users to leave a review using SKStoreReviewController
.
Conceptual Example:
import StoreKit
func requestReview() {
if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
SKStoreReviewController.requestReview(in: scene)
}
}
Filter Customization: Customizing Our Filter Using confirmationDialog()
We'll learn how to allow users to customize Core Image filters using confirmationDialog()
.
Example: Customizable Filter
import SwiftUI
import CoreImage
import CoreImage.CIFilterBuiltins
struct CustomizableFilter: View {
@State private var inputImage: UIImage?
@State private var processedImage: Image?
@State private var showFilterOptions = false
@State private var filterIntensity: Double = 0.5
var body: some View {
VStack {
if let image = processedImage {
image.resizable().scaledToFit().frame(width: 200, height: 200)
}
Button("Load Image") {
inputImage = UIImage(named: "ExampleImage") // Replace with your image
applyFilter()
}
Button("Filter Options") {
showFilterOptions = true
}
}
.confirmationDialog("Filter Options", isPresented: $showFilterOptions) {
Slider(value: $filterIntensity, in: 0...1) {
Text("Intensity")
}
Button("Apply") {
applyFilter()
}
Button("Cancel", role: .cancel) {}
} message: {
Text("Adjust filter intensity")
}
}
func applyFilter() {
guard let inputImage = inputImage, let ciImage = CIImage(image: inputImage) else { return }
let filter = CIFilter.sepiaTone()
filter.setValue(ciImage, forKey: kCIInputImageKey)
filter.setValue(filterIntensity, forKey: kCIInputIntensityKey)
if let outputCIImage = filter.outputImage, let cgImage = CIContext().createCGImage(outputCIImage, from: outputCIImage.extent) {
processedImage = Image(cgImage, scale: inputImage.scale, orientation: .up)
}
}
}
struct CustomizableFilter_Previews: PreviewProvider {
static var previews: some View {
CustomizableFilter()
}
}
Visual Representation:
- An image with a filter that can be customized using a slider in a confirmation dialog.
Image Sharing: Sharing an Image Using ShareLink
We'll learn how to share an image using ShareLink
.
Example: Sharing an Image
import SwiftUI
struct ImageShareLink: View {
@State private var image: Image?
var body: some View {
VStack {
if let image = image {
image.resizable().scaledToFit().frame(width: 200, height: 200)
ShareLink(item: image, preview: SharePreview("Image", image: image))
} else {
Text("Load an image to share")
}
Button("Load Example Image"){
image = Image("ExampleImage")
}
}
}
}
struct ImageShareLink_Previews: PreviewProvider {
static var previews: some View {
ImageShareLink()
}
}
Visual Representation:
- An image view with a share button that opens the share sheet.
🔥 Conclusion
Day 27 has been a deep dive into advanced SwiftUI techniques. We've explored property wrappers, image processing with Core Image, user interaction with confirmationDialog()
, photo library access, and content sharing with ShareLink
. These skills will empower you to build more sophisticated and engaging apps. Keep experimenting and building!
Follow me on Linkedin: igatitech 🚀🚀🚀
Comments
Post a Comment