Code Block Swiftimport SwiftUI |
|
extension URL { |
func loadImage(_ image: inout UIImage?) { |
if let data = try? Data(contentsOf: self), let loaded = UIImage(data: data) { |
image = loaded |
} else { |
image = nil |
} |
} |
|
func saveImage(_ image: UIImage?) { |
if let image = image { |
if let data = image.jpegData(compressionQuality: 1.0) { |
try? data.write(to: self) |
} |
} else { |
try? FileManager.default.removeItem(at: self) |
} |
} |
} |
|
extension UserDefaults { |
func color(forKey defaultName: String) -> Color? { |
if let data = data(forKey: defaultName) { |
if let color = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) { |
return Color(color) |
} |
} |
return nil |
} |
|
func set(_ value: Color?, forKey defaultName: String) { |
if let color = value { |
if let data = try? NSKeyedArchiver.archivedData(withRootObject: UIColor(color), requiringSecureCoding: false) { |
set(data, forKey: defaultName) |
} |
} else { |
removeObject(forKey: defaultName) |
} |
} |
} |
|
|
struct ContentView: View { |
@State private var showingTest = false |
@State private var backgroundImage: UIImage? |
@State private var backgroundColor: Color? |
|
private var imageURL: URL { |
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) |
return paths[0].appendingPathComponent("backgroundImage") |
} |
|
private func load() { |
if let color = UserDefaults.standard.color(forKey: "backgroundColor") { |
backgroundColor = color |
} else { |
imageURL.loadImage(&backgroundImage) |
} |
} |
|
var body: some View { |
ZStack { |
GeometryReader { geometry in |
if let image = backgroundImage { |
Image(uiImage: image) |
.resizable() |
.scaledToFill() |
.frame(width: geometry.size.width, height: geometry.size.height) |
} else if let color = backgroundColor { |
color |
} else { |
Color(.systemBackground) |
} |
} |
.ignoresSafeArea() |
|
Button("Test") { |
showingTest = true |
} |
} |
.onAppear(perform: load) |
.sheet(isPresented: $showingTest) { |
TestView(image: $backgroundImage, color: $backgroundColor) |
} |
} |
} |
|
struct TestView: View { |
private enum BackgroundType: String, CaseIterable, Identifiable { |
var id: String { self.rawValue } |
|
case color |
case image |
} |
|
@Environment(\.presentationMode) private var presentationMode |
|
@State private var backgroundType: BackgroundType = .color |
@State private var showingImagePicker = false |
@Binding var image: UIImage? |
@Binding var color: Color? |
|
private var imageURL: URL { |
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) |
return paths[0].appendingPathComponent("backgroundImage") |
} |
|
var body: some View { |
NavigationView { |
Form { |
Section(header: HStack { |
Text("BACKGROUND") |
|
Spacer() |
|
HStack { |
Picker("Types of background", selection: $backgroundType) { |
ForEach(BackgroundType.allCases) { |
Text($0.rawValue.capitalized).tag($0) |
} |
} |
.pickerStyle(SegmentedPickerStyle()) |
.frame(width: 120) |
} |
}) { |
|
switch backgroundType { |
case .color: |
if color != nil { |
ColorPicker("Change color", selection: Binding($color)!) |
.foregroundColor(.accentColor) |
} else { |
ColorPicker("Change color", selection: Binding<Color> { |
Color(.systemBackground) |
} set: { |
color = $0 |
}) |
} |
case .image: |
HStack { |
Button("Change image") { |
showingImagePicker = true |
} |
.sheet(isPresented: $showingImagePicker) { |
PhotoPicker(image: $image) |
.ignoresSafeArea() |
} |
|
Spacer() |
|
Image(uiImage: image ?? UIImage(systemName: "questionmark.diamond")!) |
.resizable() |
.scaledToFill() |
.frame(width: 40, height: 40) |
.clipShape(RoundedRectangle(cornerRadius: 10)) |
} |
} |
} |
} |
.toolbar { |
ToolbarItem(placement: .confirmationAction) { |
Button("Save") { |
presentationMode.wrappedValue.dismiss() |
|
switch backgroundType { |
case .color: |
image = nil |
case .image: |
color = nil |
} |
|
DispatchQueue.main.async { |
UserDefaults.standard.set(color, forKey: "backgroundColor") |
imageURL.saveImage(image) |
} |
} |
} |
} |
} |
.onAppear { |
if image == nil { |
backgroundType = .color |
} else { |
backgroundType = .image |
} |
} |
} |
} |
|
struct PhotoPicker: UIViewControllerRepresentable { |
class Coordinator: PHPickerViewControllerDelegate { |
let parent: PhotoPicker |
|
init(_ parent: PhotoPicker) { |
self.parent = parent |
} |
|
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { |
parent.presentationMode.wrappedValue.dismiss() |
|
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) { |
itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in |
DispatchQueue.main.async { |
guard let self = self, let image = image as? UIImage else { return } |
self.parent.image = image |
} |
} |
} |
} |
} |
|
@Environment(\.presentationMode) var presentationMode |
@Binding var image: UIImage? |
|
func makeUIViewController(context: Context) -> some UIViewController { |
var configuration = PHPickerConfiguration(photoLibrary: .shared()) |
configuration.filter = .images |
|
let picker = PHPickerViewController(configuration: configuration) |
picker.delegate = context.coordinator |
return picker |
} |
|
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {} |
|
func makeCoordinator() -> Coordinator { |
Coordinator(self) |
} |
} |