From 1e7199337f4223ea49b74d411ee3d88196a65db3 Mon Sep 17 00:00:00 2001 From: Trey t Date: Sun, 13 Feb 2022 13:34:41 -0600 Subject: [PATCH] WIP - custom widget creator --- Feels.xcodeproj/project.pbxproj | 8 + .../bolt2.imageset/Contents.json | 21 ++ .../bolt2.imageset/bolt-solid.svg | 1 + .../dollar.imageset/Contents.json | 21 ++ .../dollar.imageset/dollar-sign-solid.svg | 1 + .../fire.imageset/Contents.json | 21 ++ .../fire.imageset/fire-solid.svg | 1 + Shared/Models/CenterTiledImage.swift | 37 ++ Shared/Models/UserDefaultsStore.swift | 2 + Shared/views/ContentView.swift | 2 +- Shared/views/CreateIconView.swift | 321 ++++++++++++++++++ Shared/views/EmptyView.swift | 6 +- Shared/views/SettingsView.swift | 19 ++ 13 files changed, 457 insertions(+), 4 deletions(-) create mode 100644 Shared/Assets.xcassets/bolt2.imageset/Contents.json create mode 100644 Shared/Assets.xcassets/bolt2.imageset/bolt-solid.svg create mode 100644 Shared/Assets.xcassets/dollar.imageset/Contents.json create mode 100644 Shared/Assets.xcassets/dollar.imageset/dollar-sign-solid.svg create mode 100644 Shared/Assets.xcassets/fire.imageset/Contents.json create mode 100644 Shared/Assets.xcassets/fire.imageset/fire-solid.svg create mode 100644 Shared/Models/CenterTiledImage.swift create mode 100644 Shared/views/CreateIconView.swift diff --git a/Feels.xcodeproj/project.pbxproj b/Feels.xcodeproj/project.pbxproj index 83a2d11..0a913f1 100644 --- a/Feels.xcodeproj/project.pbxproj +++ b/Feels.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 1C02589C27B9677A00EB91AC /* CreateIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C02589B27B9677A00EB91AC /* CreateIconView.swift */; }; + 1C02589E27B9821700EB91AC /* CenterTiledImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C02589D27B9821700EB91AC /* CenterTiledImage.swift */; }; 1C0DAB45279DB0FB003B1F21 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB47279DB0FB003B1F21 /* Localizable.strings */; }; 1C10E24E27A1AB110047948B /* UserDefaultsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5F4977279C945E0092F1B4 /* UserDefaultsStore.swift */; }; 1C10E24F27A1AB1D0047948B /* OnboardingData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5F4975279C84090092F1B4 /* OnboardingData.swift */; }; @@ -117,6 +119,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1C02589B27B9677A00EB91AC /* CreateIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateIconView.swift; sourceTree = ""; }; + 1C02589D27B9821700EB91AC /* CenterTiledImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterTiledImage.swift; sourceTree = ""; }; 1C0DAB46279DB0FB003B1F21 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 1C0DAB48279DB116003B1F21 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; 1C2618FD27960A4F00FDC148 /* FilterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterViewModel.swift; sourceTree = ""; }; @@ -268,6 +272,7 @@ 1CAD602C27A5C1C800C520BD /* SettingsView.swift */, 1CAD602B27A5C1C800C520BD /* SmallRollUpHeaderView.swift */, 1CAD603D27A6ECCD00C520BD /* SwitchableView.swift */, + 1C02589B27B9677A00EB91AC /* CreateIconView.swift */, ); path = Views; sourceTree = ""; @@ -380,6 +385,7 @@ 1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */, 1C358FAC27ADD0C3002C83A6 /* Theme.swift */, 1C5F4977279C945E0092F1B4 /* UserDefaultsStore.swift */, + 1C02589D27B9821700EB91AC /* CenterTiledImage.swift */, ); path = Models; sourceTree = ""; @@ -602,6 +608,7 @@ 1CAD603A27A5C1C800C520BD /* BGView.swift in Sources */, 1C26190727960DC900FDC148 /* ChartViewItemBuildable.swift in Sources */, 1CD90B5D278C7EAD001C4FEA /* Random.swift in Sources */, + 1C02589E27B9821700EB91AC /* CenterTiledImage.swift in Sources */, 1C2618FE27960A4F00FDC148 /* FilterViewModel.swift in Sources */, 1C744F2C278CE15600953A57 /* AppDelegate.swift in Sources */, 1CD90B63278C7EBA001C4FEA /* Mood.swift in Sources */, @@ -618,6 +625,7 @@ 1CD90B18278C7DE0001C4FEA /* FeelsApp.swift in Sources */, 1CA03777279A295600D26164 /* OnboardingTitle.swift in Sources */, 1C358FAD27ADD0C3002C83A6 /* Theme.swift in Sources */, + 1C02589C27B9677A00EB91AC /* CreateIconView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Shared/Assets.xcassets/bolt2.imageset/Contents.json b/Shared/Assets.xcassets/bolt2.imageset/Contents.json new file mode 100644 index 0000000..af1f790 --- /dev/null +++ b/Shared/Assets.xcassets/bolt2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bolt-solid.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Shared/Assets.xcassets/bolt2.imageset/bolt-solid.svg b/Shared/Assets.xcassets/bolt2.imageset/bolt-solid.svg new file mode 100644 index 0000000..bd3d9d0 --- /dev/null +++ b/Shared/Assets.xcassets/bolt2.imageset/bolt-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Shared/Assets.xcassets/dollar.imageset/Contents.json b/Shared/Assets.xcassets/dollar.imageset/Contents.json new file mode 100644 index 0000000..bdaf956 --- /dev/null +++ b/Shared/Assets.xcassets/dollar.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "dollar-sign-solid.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Shared/Assets.xcassets/dollar.imageset/dollar-sign-solid.svg b/Shared/Assets.xcassets/dollar.imageset/dollar-sign-solid.svg new file mode 100644 index 0000000..32fefc5 --- /dev/null +++ b/Shared/Assets.xcassets/dollar.imageset/dollar-sign-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Shared/Assets.xcassets/fire.imageset/Contents.json b/Shared/Assets.xcassets/fire.imageset/Contents.json new file mode 100644 index 0000000..95efb2c --- /dev/null +++ b/Shared/Assets.xcassets/fire.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "fire-solid.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Shared/Assets.xcassets/fire.imageset/fire-solid.svg b/Shared/Assets.xcassets/fire.imageset/fire-solid.svg new file mode 100644 index 0000000..9ab0ec0 --- /dev/null +++ b/Shared/Assets.xcassets/fire.imageset/fire-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Shared/Models/CenterTiledImage.swift b/Shared/Models/CenterTiledImage.swift new file mode 100644 index 0000000..d486d40 --- /dev/null +++ b/Shared/Models/CenterTiledImage.swift @@ -0,0 +1,37 @@ +// +// CenterTiledImage.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/13/22. +// + +import SwiftUI + +struct CenterTiledImage: View { + let imageName: String + let imageSize: CGSize + + var body: some View { + GeometryReader { geoReader in + let horizontalTilesNeeded = ceil(geoReader.size.width / imageSize.width / 2) * 2 + 1 + let verticalTilesNeeded = ceil(geoReader.size.height / imageSize.height / 2) * 2 + 1 + + Image(imageName) + .resizable(resizingMode: .tile) + .frame( + width: horizontalTilesNeeded * imageSize.width, + height: verticalTilesNeeded * imageSize.height + ) + .position(x: geoReader.size.width * 0.5, y: geoReader.size.height * 0.5) + } + } + + init?(imageName: String) { + guard let imageSize = UIImage(named: imageName)?.size else { + return nil + } + + self.imageName = imageName + self.imageSize = imageSize + } +} diff --git a/Shared/Models/UserDefaultsStore.swift b/Shared/Models/UserDefaultsStore.swift index e36fbe6..3f75aea 100644 --- a/Shared/Models/UserDefaultsStore.swift +++ b/Shared/Models/UserDefaultsStore.swift @@ -16,6 +16,8 @@ class UserDefaultsStore { case mainViewTopHeaderIndex case theme + case customIcon + case contentViewCurrentSelectedHeaderViewBackDays case contentViewHeaderTag case contentViewHeaderTagViewOneViewType diff --git a/Shared/views/ContentView.swift b/Shared/views/ContentView.swift index 2a3ff08..418c8cc 100644 --- a/Shared/views/ContentView.swift +++ b/Shared/views/ContentView.swift @@ -269,7 +269,7 @@ struct ContentView: View { settingsButtonView if viewModel.hasNoData { Spacer() - EmptyView(viewModel: viewModel) + EmptyContentView(viewModel: viewModel) Spacer() } else { ZStack { diff --git a/Shared/views/CreateIconView.swift b/Shared/views/CreateIconView.swift new file mode 100644 index 0000000..320d269 --- /dev/null +++ b/Shared/views/CreateIconView.swift @@ -0,0 +1,321 @@ +// +// CreateIconView.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/13/22. +// + +import SwiftUI + + +struct CustomIcon { + let leftEye: Image + let rightEye: Image + let mouth: Image + + let background: AnyView? + let bgColor: Color + let innerColor: Color +} + +enum BackGroundOptions: String, CaseIterable { + case horrible + case bad + case average + case good + case great + case random + + static var selectable: [BackGroundOptions] { + return [.great, .good, .average, .bad, .horrible] + } +} + +enum Eyes { + case left + case right +} + +enum EyeOptions: String, CaseIterable { + case fire = "fire" + case bolt = "bolt2" + case dollar = "dollar" + + static public var defaultOption: AnyView { + let image = Image(EyeOptions.fire.rawValue, bundle: .main) + .resizable() + .frame(width: 20, height: 20) + return AnyView(image) + + } +} + +enum MouthOptions: String, CaseIterable { + case moonFill = "moon.fill" + case flame = "flame" + case flameCircle = "flame.circle" + + static public var defaultOption: MouthOptions { + return MouthOptions.flameCircle + } +} + +struct CreateIconView: View { + @State private var bgColor = Color.red + @State private var innerColor = Color.green + @State private var bgPattern = [(AnyView, UUID)]() + + @State private var leftEye: AnyView = EyeOptions.defaultOption + @State private var rightEye: AnyView = EyeOptions.defaultOption + + @State private var mouth: Image = Image(systemName: MouthOptions.defaultOption.rawValue) + + private var randomElements: [AnyView] = [ + AnyView(Image(BackGroundOptions.selectable.randomElement()!.rawValue) + .resizable() + .frame(width: 20, height: 20)), + AnyView(Image(BackGroundOptions.selectable.randomElement()!.rawValue) + .resizable() + .frame(width: 20, height: 20)), + AnyView(Image(BackGroundOptions.selectable.randomElement()!.rawValue) + .resizable() + .frame(width: 20, height: 20)), + AnyView(Image(BackGroundOptions.selectable.randomElement()!.rawValue) + .resizable() + .frame(width: 20, height: 20)) + ] + + let columns = [ + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0), + GridItem(.fixed(20), spacing: 0) + ] + + func update(eye: Eyes, eyeOption: EyeOptions) { + let image = Image(eyeOption.rawValue, bundle: .main) + .resizable() + .frame(width: 20, height: 20) + switch eye { + case .left: + leftEye = AnyView(image) + case .right: + rightEye = AnyView(image) + } + } + + func createRandom() { + bgColor = Color( + red: .random(in: 0...1), + green: .random(in: 0...1), + blue: .random(in: 0...1) + ) + + innerColor = Color( + red: .random(in: 0...1), + green: .random(in: 0...1), + blue: .random(in: 0...1) + ) + + update(eye: .left, eyeOption: EyeOptions.allCases.randomElement()!) + update(eye: .right, eyeOption: EyeOptions.allCases.randomElement()!) + update(mouthOption: MouthOptions.allCases.randomElement()!) + + update(background: BackGroundOptions.allCases.randomElement()!) + } + + func update(mouthOption: MouthOptions) { + mouth = Image(systemName: mouthOption.rawValue) + } + + func update(background: BackGroundOptions) { + bgPattern.removeAll() + + if background == .random { + for _ in 0...120 { + let image = Image(BackGroundOptions.selectable.randomElement()!.rawValue, bundle: .main) + + let sizedImage = image + .resizable() + .frame(width: 20, height: 20) + + bgPattern.append((AnyView(sizedImage), UUID())) + } + return + } + let image = Image(background.rawValue, bundle: .main) + + let sizedImage = image + .resizable() + .frame(width: 20, height: 20) + + for _ in 0...120 { + bgPattern.append((AnyView(sizedImage), UUID())) + } + } + + var mixBG: some View { + VStack { + HStack { + randomElements[0] + randomElements[1] + } + HStack { + randomElements[2] + randomElements[3] + } + } + } + + var iconView: some View { + ZStack { + LazyVGrid(columns: columns, spacing: 0) { + ForEach(bgPattern, id: \.self.1) { + $0.0 + } + } + .frame(width: 200, height: 200) + .position(x: 100, y: 110) + + + Circle() + .strokeBorder(Color.black, lineWidth: 12) + .background(Circle().fill(innerColor)) + .frame(width: 120, height: 120, alignment: .center) + + VStack { + Spacer() + .frame(height: 70) + HStack { + Spacer() + .frame(width: 72) + leftEye + Spacer() + .frame(width: 24) + rightEye + Spacer() + } + Spacer() + .frame(height: 28) + mouth + Spacer() + } + } + .frame(width: 200, height: 200) + .background( + bgColor + ) + .cornerRadius(10) + } + + var body: some View { + VStack { + iconView + .padding() + Spacer() + + VStack { + ColorPicker("Set the background color", selection: $bgColor) + .padding([.leading, .trailing]) + ColorPicker("Set the inner color", selection: $innerColor) + .padding([.leading, .trailing]) + } + + HStack { + Spacer() + Menu("Left Eye") { + ForEach(EyeOptions.allCases, id: \.self) { option in + Button(action: { + update(eye: .left, eyeOption: option) + }, label: { + Label(option.rawValue, image: option.rawValue) + }) + } + } + Spacer() + Menu("Right Eye") { + ForEach(EyeOptions.allCases, id: \.self) { option in + Button(action: { + update(eye: .right, eyeOption: option) + }, label: { + Label(option.rawValue, image: option.rawValue) + }) + } + } + Spacer() + Menu("Mouth") { + ForEach(MouthOptions.allCases, id: \.self) { option in + Button(action: { + update(mouthOption: option) + }, label: { + Image(systemName: option.rawValue) + }) + } + } + Spacer() + } + .padding(.top, 10) + + VStack{ + Text("Background") + + HStack { + ForEach(BackGroundOptions.selectable, id: \.self) { bg in + Image(bg.rawValue, bundle: .main) + .resizable() + .frame(width: CGFloat(50), height: CGFloat(50), alignment: .center) + .onTapGesture { + update(background: bg) + } + } + mixBG + .onTapGesture { + update(background: .random) + } + } + } + .padding(.top, 10) + + Button(action: { + createRandom() + }, label: { + Text("Random") + .font(.title) + .fontWeight(.bold) + .foregroundColor(Color(UIColor.white)) + .frame(minWidth: 0, maxWidth: .infinity) + .background(.blue) + }) + + Button(action: { +// let newIcon = CustomIcon(leftEye: leftEye, +// rightEye: rightEye, +// mouth: mouth, +// background: bgPattern, +// bgColor: bgColor, +// innerColor: innerColor) + }, label: { + Text("Save") + .font(.title) + .fontWeight(.bold) + .foregroundColor(Color(UIColor.white)) + .frame(minWidth: 0, maxWidth: .infinity) + .background(.green) + }) + } + } +} + +struct CreateIconView_Previews: PreviewProvider { + static var previews: some View { + CreateIconView() + } +} diff --git a/Shared/views/EmptyView.swift b/Shared/views/EmptyView.swift index afe8b64..ea502cd 100644 --- a/Shared/views/EmptyView.swift +++ b/Shared/views/EmptyView.swift @@ -7,7 +7,7 @@ import SwiftUI -struct EmptyView: View { +struct EmptyContentView: View { @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system let viewModel: ContentModeViewModel @@ -38,8 +38,8 @@ struct EmptyView: View { } } -struct EmptyView_Previews: PreviewProvider { +struct EmptyContentView_Previews: PreviewProvider { static var previews: some View { - EmptyView(viewModel: ContentModeViewModel()) + EmptyContentView(viewModel: ContentModeViewModel()) } } diff --git a/Shared/views/SettingsView.swift b/Shared/views/SettingsView.swift index dc05022..82cadd5 100644 --- a/Shared/views/SettingsView.swift +++ b/Shared/views/SettingsView.swift @@ -18,6 +18,7 @@ struct SettingsView: View { @State private var showSpecialThanks = false @State private var showWhyBGMode = false + @State private var showCreateCustomWidget = false @ObservedObject var syncMonitor = SyncMonitor.shared @AppStorage(UserDefaultsStore.Keys.useCloudKit.rawValue, store: GroupUserDefaults.groupDefaults) private var useCloudKit = false @@ -35,6 +36,7 @@ struct SettingsView: View { canDelete changeIcon themePicker + createCustomWidget showOnboardingButton whyBackgroundMode specialThanksCell @@ -58,6 +60,9 @@ struct SettingsView: View { showOnboarding = false }) } + .sheet(isPresented: $showCreateCustomWidget) { + CreateIconView() + } .background( theme.currentTheme.bg .edgesIgnoringSafeArea(.all) @@ -283,6 +288,20 @@ struct SettingsView: View { .fixedSize(horizontal: false, vertical: true) .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) } + + private var createCustomWidget: some View { + ZStack { + Color(theme.currentTheme.secondaryBGColor) + Button(action: { + showCreateCustomWidget = true + }, label: { + Text("Create Custom Widget") + }) + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) + } } struct SettingsView_Previews: PreviewProvider {