Code Block Swift| import 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) |
| } |
| } |