From 923a15c1260c354a0ce91980eec02e50f2dca443 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Thu, 16 Jul 2020 12:40:18 +1000 Subject: [PATCH 01/17] Create UIImage extension and Pixel struct. --- Project SF.xcodeproj/project.pbxproj | 4 ++ .../Extensions/UIImage+PixelData.swift | 52 +++++++++++++++++++ .../Profile/Settings/ProfileSettings.swift | 3 +- 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 Project SF/Utilities/Extensions/UIImage+PixelData.swift diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index 6123a1b..0ee6d03 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 308FAD9124B987F000126F3F /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 308FAD9024B987F000126F3F /* SignUpView.swift */; }; 308FAD9E24B9A0DC00126F3F /* NavigationBarLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 308FAD9D24B9A0DC00126F3F /* NavigationBarLabel.swift */; }; 3090918224B9480F000E3B11 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3090918124B9480F000E3B11 /* RoundedButton.swift */; }; + 30B2818824BFF2F600647B83 /* UIImage+PixelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */; }; 30BFC8CE24B75E6C00DAC6D9 /* ProjectSFApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */; }; 30BFC8D024B75E6C00DAC6D9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */; }; 30BFC8D224B75E6F00DAC6D9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30BFC8D124B75E6F00DAC6D9 /* Assets.xcassets */; }; @@ -89,6 +90,7 @@ 308FAD9024B987F000126F3F /* SignUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpView.swift; sourceTree = ""; }; 308FAD9D24B9A0DC00126F3F /* NavigationBarLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarLabel.swift; sourceTree = ""; }; 3090918124B9480F000E3B11 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; + 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+PixelData.swift"; sourceTree = ""; }; 30BFC8CA24B75E6C00DAC6D9 /* Project SF.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Project SF.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSFApp.swift; sourceTree = ""; }; 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -189,6 +191,7 @@ 9C7B4D0724B89AF100FC4456 /* Double+ConvertFromRangeToRange.swift */, 9C7B4D0524B89AC700FC4456 /* View+ForegroundModifier.swift */, 9CCFDD2324B9745E00162B0F /* UIApplication+IsTesting.swift */, + 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */, ); path = Extensions; sourceTree = ""; @@ -558,6 +561,7 @@ 9CCFDD1F24B971D600162B0F /* NetworkManager.swift in Sources */, 9CCFDD2624B9753F00162B0F /* Main.swift in Sources */, 30D321C124BC1886009CD9D0 /* NavScrollView.swift in Sources */, + 30B2818824BFF2F600647B83 /* UIImage+PixelData.swift in Sources */, 30BFC8E924B804CC00DAC6D9 /* ActivityRingView.swift in Sources */, 3067712F24BDE0080085F152 /* FriendsCell.swift in Sources */, 30D321BD24BB65D5009CD9D0 /* ProfileSettings.swift in Sources */, diff --git a/Project SF/Utilities/Extensions/UIImage+PixelData.swift b/Project SF/Utilities/Extensions/UIImage+PixelData.swift new file mode 100644 index 0000000..a17a81c --- /dev/null +++ b/Project SF/Utilities/Extensions/UIImage+PixelData.swift @@ -0,0 +1,52 @@ +// +// UIImage+PixelData.swift +// Project SF +// +// Created by Roman Esin on 16.07.2020. +// + +import UIKit + +// swiftlint:disable identifier_name +struct Pixel { + var a: UInt8 + var r: UInt8 + var g: UInt8 + var b: UInt8 +} + +extension Array where Element == Pixel { + static func random(width: Int, height: Int) -> [Pixel] { + var pixels: [Pixel] = [] + for _ in 0..<(width * height) { + pixels.append(Pixel(a: 255, + r: .random(in: 0...255), + g: .random(in: 0...255), + b: .random(in: 0...255))) + } + return pixels + } +} + +extension UIImage { + convenience init?(pixels: [Pixel], width: Int, height: Int) { + guard width > 0 && height > 0, pixels.count == width * height else { return nil } + var data = pixels + guard let providerRef = CGDataProvider(data: Data(bytes: &data, count: data.count * MemoryLayout.size) as CFData) + else { return nil } + guard let cgim = CGImage( + width: width, + height: height, + bitsPerComponent: 8, + bitsPerPixel: 32, + bytesPerRow: width * MemoryLayout.size, + space: CGColorSpaceCreateDeviceRGB(), + bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue), + provider: providerRef, + decode: nil, + shouldInterpolate: true, + intent: .defaultIntent) + else { return nil } + self.init(cgImage: cgim) + } +} diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift index d69801d..b2f067d 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift @@ -23,7 +23,8 @@ struct ProfileSettings: View { // so ScrollView isn't the topmost view. NavScrollView { Button(action: { - showImageSelectionView = true +// showImageSelectionView = true + profilePicture = UIImage(pixels: .random(width: 10, height: 10), width: 10, height: 10) }, label: { if profilePicture == nil { Image(systemName: "person.crop.circle.badge.plus") From 3d9f96ee17473a6b96cc71d3053d6ea941dec868 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Thu, 16 Jul 2020 12:43:36 +1000 Subject: [PATCH 02/17] Update UIImage+PixelData.swift --- Project SF/Utilities/Extensions/UIImage+PixelData.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Project SF/Utilities/Extensions/UIImage+PixelData.swift b/Project SF/Utilities/Extensions/UIImage+PixelData.swift index a17a81c..a421907 100644 --- a/Project SF/Utilities/Extensions/UIImage+PixelData.swift +++ b/Project SF/Utilities/Extensions/UIImage+PixelData.swift @@ -32,7 +32,8 @@ extension UIImage { convenience init?(pixels: [Pixel], width: Int, height: Int) { guard width > 0 && height > 0, pixels.count == width * height else { return nil } var data = pixels - guard let providerRef = CGDataProvider(data: Data(bytes: &data, count: data.count * MemoryLayout.size) as CFData) + guard let providerRef = CGDataProvider(data: Data(bytes: &data, + count: data.count * MemoryLayout.size) as CFData) else { return nil } guard let cgim = CGImage( width: width, From 4cbc036b80e1bfdc51090a5cb41e16db32b30a6f Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Thu, 16 Jul 2020 13:25:04 +1000 Subject: [PATCH 03/17] Add random symmetrical images. --- .../Extensions/UIImage+PixelData.swift | 29 +++++++++++++++++++ .../Profile/Settings/ProfileSettings.swift | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Project SF/Utilities/Extensions/UIImage+PixelData.swift b/Project SF/Utilities/Extensions/UIImage+PixelData.swift index a421907..bc06e39 100644 --- a/Project SF/Utilities/Extensions/UIImage+PixelData.swift +++ b/Project SF/Utilities/Extensions/UIImage+PixelData.swift @@ -26,6 +26,35 @@ extension Array where Element == Pixel { } return pixels } + + static func randomSymmetrical(width: Int, height: Int) -> [Pixel] { + // Yes, this makes sence. + let width = 2 * width / 2 + let height = 2 * height / 2 + + var pixels: [Pixel] = [] + let clear = Pixel(a: 0, r: 0, g: 0, b: 0) + let color = Pixel(a: 255, + r: .random(in: 0...255), + g: .random(in: 0...255), + b: .random(in: 0...255)) + + var slice: [Pixel] = [] + for _ in 0.. Date: Thu, 16 Jul 2020 13:29:30 +1000 Subject: [PATCH 04/17] Create PixelImage --- .../Extensions/UIImage+PixelData.swift | 28 ++++++++++++++----- .../Profile/Settings/ProfileSettings.swift | 2 +- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Project SF/Utilities/Extensions/UIImage+PixelData.swift b/Project SF/Utilities/Extensions/UIImage+PixelData.swift index bc06e39..e21a309 100644 --- a/Project SF/Utilities/Extensions/UIImage+PixelData.swift +++ b/Project SF/Utilities/Extensions/UIImage+PixelData.swift @@ -15,19 +15,23 @@ struct Pixel { var b: UInt8 } -extension Array where Element == Pixel { - static func random(width: Int, height: Int) -> [Pixel] { +class PixelImage { + var width: Int + var height: Int + var pixels: [Pixel] + + static func random(width: Int, height: Int) -> PixelImage { var pixels: [Pixel] = [] for _ in 0..<(width * height) { pixels.append(Pixel(a: 255, - r: .random(in: 0...255), + r: .random(in: 0...255), g: .random(in: 0...255), b: .random(in: 0...255))) } - return pixels + return PixelImage(width: width, height: height, pixels: pixels) } - static func randomSymmetrical(width: Int, height: Int) -> [Pixel] { + static func randomSymmetrical(width: Int, height: Int) -> PixelImage { // Yes, this makes sence. let width = 2 * width / 2 let height = 2 * height / 2 @@ -53,12 +57,22 @@ extension Array where Element == Pixel { } } } - return pixels + return PixelImage(width: width, height: height, pixels: pixels) + } + + init(width: Int, height: Int, pixels: [Pixel]) { + self.width = width + self.height = height + self.pixels = pixels } } extension UIImage { - convenience init?(pixels: [Pixel], width: Int, height: Int) { + convenience init?(pixelImage: PixelImage) { + let width = pixelImage.width + let height = pixelImage.height + let pixels = pixelImage.pixels + guard width > 0 && height > 0, pixels.count == width * height else { return nil } var data = pixels guard let providerRef = CGDataProvider(data: Data(bytes: &data, diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift index 3e605b6..f0fe3bb 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift @@ -24,7 +24,7 @@ struct ProfileSettings: View { NavScrollView { Button(action: { // showImageSelectionView = true - profilePicture = UIImage(pixels: .randomSymmetrical(width: 6, height: 6), width: 6, height: 6) + profilePicture = UIImage(pixelImage: .randomSymmetrical(width: 8, height: 8)) }, label: { if profilePicture == nil { Image(systemName: "person.crop.circle.badge.plus") From 133368e69ca3f0bc8f1f9a915846c318c4eebfae Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Thu, 16 Jul 2020 13:32:04 +1000 Subject: [PATCH 05/17] Refactored. --- Project SF.xcodeproj/project.pbxproj | 4 + Project SF/Models/Pixel.swift | 73 +++++++++++++++++++ .../Extensions/UIImage+PixelData.swift | 60 --------------- .../Profile/Settings/ProfileSettings.swift | 2 +- 4 files changed, 78 insertions(+), 61 deletions(-) create mode 100644 Project SF/Models/Pixel.swift diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index 0ee6d03..bd6f1f9 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 308FAD9E24B9A0DC00126F3F /* NavigationBarLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 308FAD9D24B9A0DC00126F3F /* NavigationBarLabel.swift */; }; 3090918224B9480F000E3B11 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3090918124B9480F000E3B11 /* RoundedButton.swift */; }; 30B2818824BFF2F600647B83 /* UIImage+PixelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */; }; + 30B2818A24C001A000647B83 /* Pixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818924C001A000647B83 /* Pixel.swift */; }; 30BFC8CE24B75E6C00DAC6D9 /* ProjectSFApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */; }; 30BFC8D024B75E6C00DAC6D9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */; }; 30BFC8D224B75E6F00DAC6D9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30BFC8D124B75E6F00DAC6D9 /* Assets.xcassets */; }; @@ -91,6 +92,7 @@ 308FAD9D24B9A0DC00126F3F /* NavigationBarLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarLabel.swift; sourceTree = ""; }; 3090918124B9480F000E3B11 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+PixelData.swift"; sourceTree = ""; }; + 30B2818924C001A000647B83 /* Pixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pixel.swift; sourceTree = ""; }; 30BFC8CA24B75E6C00DAC6D9 /* Project SF.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Project SF.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSFApp.swift; sourceTree = ""; }; 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -381,6 +383,7 @@ 302CF88924BA199E00FF79D7 /* RingType.swift */, 302CF88B24BA1A4000FF79D7 /* ActivityResult.swift */, 30D3217C24BB4EDE009CD9D0 /* ActivityRings.swift */, + 30B2818924C001A000647B83 /* Pixel.swift */, ); path = Models; sourceTree = ""; @@ -578,6 +581,7 @@ 30F1EA3B24B951CA00FF89FC /* OnboardingView.swift in Sources */, 30D3217824BB430E009CD9D0 /* RoundedTextField.swift in Sources */, 9CCFDD2024B971D600162B0F /* HealthKitController.swift in Sources */, + 30B2818A24C001A000647B83 /* Pixel.swift in Sources */, 9C8FAD4624B9E39E00571947 /* Record.swift in Sources */, 30D321B924BB65D5009CD9D0 /* ImageSelectionView.swift in Sources */, 30D3217D24BB4EDE009CD9D0 /* ActivityRings.swift in Sources */, diff --git a/Project SF/Models/Pixel.swift b/Project SF/Models/Pixel.swift new file mode 100644 index 0000000..de41e20 --- /dev/null +++ b/Project SF/Models/Pixel.swift @@ -0,0 +1,73 @@ +// +// Pixel.swift +// Project SF +// +// Created by Roman Esin on 16.07.2020. +// + +import Foundation + +// swiftlint:disable identifier_name +struct Pixel { + var a: UInt8 + var r: UInt8 + var g: UInt8 + var b: UInt8 +} + +class PixelImage { + var width: Int + var height: Int + var pixels: [Pixel] + + static func random(width: Int, height: Int) -> PixelImage { + var pixels: [Pixel] = [] + for _ in 0..<(width * height) { + pixels.append(Pixel(a: 255, + r: .random(in: 0...255), + g: .random(in: 0...255), + b: .random(in: 0...255))) + } + return PixelImage(width: width, height: height, pixels: pixels) + } + + /// Returns random symmetrixal `PixelImage` with given size. + /// - Parameters: + /// - width: Width of the image. + /// - height: Height of the image. + /// - Returns: Random symmetrixal `PixelImage` with given size. + static func randomSymmetrical(width: Int, height: Int) -> PixelImage { + // Yes, this makes sence. + let width = 2 * width / 2 + let height = 2 * height / 2 + + var pixels: [Pixel] = [] + let clear = Pixel(a: 0, r: 0, g: 0, b: 0) + let color = Pixel(a: 255, + r: .random(in: 0...255), + g: .random(in: 0...255), + b: .random(in: 0...255)) + + var slice: [Pixel] = [] + for _ in 0.. PixelImage { - var pixels: [Pixel] = [] - for _ in 0..<(width * height) { - pixels.append(Pixel(a: 255, - r: .random(in: 0...255), - g: .random(in: 0...255), - b: .random(in: 0...255))) - } - return PixelImage(width: width, height: height, pixels: pixels) - } - - static func randomSymmetrical(width: Int, height: Int) -> PixelImage { - // Yes, this makes sence. - let width = 2 * width / 2 - let height = 2 * height / 2 - - var pixels: [Pixel] = [] - let clear = Pixel(a: 0, r: 0, g: 0, b: 0) - let color = Pixel(a: 255, - r: .random(in: 0...255), - g: .random(in: 0...255), - b: .random(in: 0...255)) - - var slice: [Pixel] = [] - for _ in 0.. Date: Thu, 16 Jul 2020 23:01:21 +1000 Subject: [PATCH 06/17] Do some fixes and optimization. --- Project SF.xcodeproj/project.pbxproj | 4 ++ Project SF/Models/Pixel.swift | 15 +++---- .../Extensions/UIImage+PixelData.swift | 2 +- .../Settings/ProfileImageCreator.swift | 20 ++++++++++ .../Profile/Settings/ProfileSettings.swift | 40 ++++++++++++++++--- 5 files changed, 68 insertions(+), 13 deletions(-) create mode 100644 Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index bd6f1f9..ada36d2 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 3090918224B9480F000E3B11 /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3090918124B9480F000E3B11 /* RoundedButton.swift */; }; 30B2818824BFF2F600647B83 /* UIImage+PixelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */; }; 30B2818A24C001A000647B83 /* Pixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818924C001A000647B83 /* Pixel.swift */; }; + 30B2818C24C07F4700647B83 /* ProfileImageCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */; }; 30BFC8CE24B75E6C00DAC6D9 /* ProjectSFApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */; }; 30BFC8D024B75E6C00DAC6D9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */; }; 30BFC8D224B75E6F00DAC6D9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30BFC8D124B75E6F00DAC6D9 /* Assets.xcassets */; }; @@ -93,6 +94,7 @@ 3090918124B9480F000E3B11 /* RoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedButton.swift; sourceTree = ""; }; 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+PixelData.swift"; sourceTree = ""; }; 30B2818924C001A000647B83 /* Pixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pixel.swift; sourceTree = ""; }; + 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImageCreator.swift; sourceTree = ""; }; 30BFC8CA24B75E6C00DAC6D9 /* Project SF.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Project SF.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSFApp.swift; sourceTree = ""; }; 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -328,6 +330,7 @@ 30D321AF24BB65D5009CD9D0 /* PermissionSettings.swift */, 30D321B024BB65D5009CD9D0 /* PrivacyAbout.swift */, 30D321B124BB65D5009CD9D0 /* ProfileSettings.swift */, + 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */, ); path = Settings; sourceTree = ""; @@ -594,6 +597,7 @@ 30BFC8E724B8041C00DAC6D9 /* ActivityRingsView.swift in Sources */, 3090918224B9480F000E3B11 /* RoundedButton.swift in Sources */, 9CCFDD2424B9745E00162B0F /* UIApplication+IsTesting.swift in Sources */, + 30B2818C24C07F4700647B83 /* ProfileImageCreator.swift in Sources */, 9CCFDD2824B9758A00162B0F /* TestApp.swift in Sources */, 302CF88C24BA1A4000FF79D7 /* ActivityResult.swift in Sources */, 3067713124BDE3950085F152 /* FriendDetailView.swift in Sources */, diff --git a/Project SF/Models/Pixel.swift b/Project SF/Models/Pixel.swift index de41e20..43ff7f5 100644 --- a/Project SF/Models/Pixel.swift +++ b/Project SF/Models/Pixel.swift @@ -37,22 +37,23 @@ class PixelImage { /// - height: Height of the image. /// - Returns: Random symmetrixal `PixelImage` with given size. static func randomSymmetrical(width: Int, height: Int) -> PixelImage { - // Yes, this makes sence. - let width = 2 * width / 2 - let height = 2 * height / 2 - var pixels: [Pixel] = [] - let clear = Pixel(a: 0, r: 0, g: 0, b: 0) + let clear = Pixel(a: 255, + r: 240, + g: 240, + b: 240) + let color = Pixel(a: 255, r: .random(in: 0...255), g: .random(in: 0...255), b: .random(in: 0...255)) var slice: [Pixel] = [] + let num = width % 2 for _ in 0.. Date: Thu, 16 Jul 2020 23:34:07 +1000 Subject: [PATCH 07/17] Create ProfileImageCreator. --- Project SF/Views/NavigationBarLabel.swift | 28 ++++++++++++++ .../Settings/ProfileImageCreator.swift | 36 +++++++++++++++++- .../Profile/Settings/ProfileSettings.swift | 37 +++++++------------ 3 files changed, 75 insertions(+), 26 deletions(-) diff --git a/Project SF/Views/NavigationBarLabel.swift b/Project SF/Views/NavigationBarLabel.swift index 93b7ea0..ccf041b 100644 --- a/Project SF/Views/NavigationBarLabel.swift +++ b/Project SF/Views/NavigationBarLabel.swift @@ -63,6 +63,34 @@ struct NavigationLabel: View { } } +struct NavigationBarButton: View { + + let title: String? + let systemName: String + let action: () -> Void + + var body: some View { + Button( + action: action, + label: { + if let title = title { + Label(title, systemImage: systemName) + .font(.title2) + } else { + Image(systemName: systemName) + .font(.title2) + } + } + ) + } + + init(title: String? = nil, systemName: String, action: @escaping () -> Void) { + self.title = title + self.systemName = systemName + self.action = action + } +} + struct NavigationButton: View { let title: String? = nil diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift index 5bd0014..fde92a5 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift @@ -8,13 +8,45 @@ import SwiftUI struct ProfileImageCreator: View { + + @Environment(\.presentationMode) var presentationMode + @Binding var image: UIImage? + var body: some View { - Text("Hello, World!") + NavigationView { + VStack { + if let image = image { + Image(uiImage: image) + .interpolation(.none) + .renderingMode(.original) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 200, height: 200) + .clipShape(Circle()) + .animation(.spring()) + } + RoundedButton("Generate image") { + withAnimation { + image = UIImage(pixelImage: .randomSymmetrical(width: 7, height: 7)) + } + } + .padding(.top) + } + .padding(.horizontal) + .navigationTitle("Profile Image Creator") + .navigationBarItems(trailing: NavigationBarButton(systemName: "xmark.circle.fill", action: { + presentationMode.wrappedValue.dismiss() + })) + } + } + + init(_ image: Binding) { + _image = image } } struct ProfileImageCreator_Previews: PreviewProvider { static var previews: some View { - ProfileImageCreator() + ProfileImageCreator(.constant(UIImage(pixelImage: .randomSymmetrical(width: 10, height: 10)))) } } diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift index c73ffc1..1ba96f3 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift @@ -16,8 +16,9 @@ struct ProfileSettings: View { @StateObject var keyboard = KeyboardManager() @State var profilePicture: UIImage? - @State var showImageSelectionView = false - @State var showProfileImageCreator = false + + @State var showSheet = false + @State var selectedSheet = 0 @State var showSelectAlert = false var body: some View { @@ -44,23 +45,6 @@ struct ProfileSettings: View { } } -// if profilePicture == nil { -// Image(systemName: "person.crop.circle.badge.plus") -// .resizable() -// .aspectRatio(contentMode: .fit) -// .frame(width: 100, height: 100) -// .padding(.bottom) -// } else { -// Image(uiImage: profilePicture!) -// .interpolation(.none) -// .renderingMode(.original) -// .resizable() -// .aspectRatio(contentMode: .fill) -// .frame(width: 100, height: 100) -// .clipShape(Circle()) -// .padding(.bottom) -// } - TextField("Name", text: $name) .font(.headline) .padding(.horizontal) @@ -111,14 +95,19 @@ struct ProfileSettings: View { message: nil, buttons: [ .default(Text("Open profile image creator"), action: { - showProfileImageCreator = true -// profilePicture = UIImage(pixelImage: .randomSymmetrical(width: 7, height: 7)) + selectedSheet = 0 + showSheet = true }), .default(Text("Select from image gallery"), action: { - showImageSelectionView = true + selectedSheet = 1 + showSheet = true }), .cancel()]) } - .sheet(isPresented: $showImageSelectionView) { - ImageSelectionView(image: $profilePicture) + .sheet(isPresented: $showSheet) { + if selectedSheet == 0 { + ProfileImageCreator($profilePicture) + } else { + ImageSelectionView(image: $profilePicture) + } } } } From 6c1f6c5e30f1b4848ab41e60ac0926f3ee2a1865 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Thu, 16 Jul 2020 23:54:27 +1000 Subject: [PATCH 08/17] Add Color picker. --- Project SF.xcodeproj/project.pbxproj | 4 ++ Project SF/Models/Pixel.swift | 17 +++++--- .../Utilities/Extensions/Color+UIColor.swift | 39 +++++++++++++++++++ .../Settings/ProfileImageCreator.swift | 9 ++++- 4 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 Project SF/Utilities/Extensions/Color+UIColor.swift diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index ada36d2..54cb98d 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 30B2818824BFF2F600647B83 /* UIImage+PixelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */; }; 30B2818A24C001A000647B83 /* Pixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818924C001A000647B83 /* Pixel.swift */; }; 30B2818C24C07F4700647B83 /* ProfileImageCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */; }; + 30B2818E24C091A400647B83 /* Color+UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818D24C091A400647B83 /* Color+UIColor.swift */; }; 30BFC8CE24B75E6C00DAC6D9 /* ProjectSFApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */; }; 30BFC8D024B75E6C00DAC6D9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */; }; 30BFC8D224B75E6F00DAC6D9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30BFC8D124B75E6F00DAC6D9 /* Assets.xcassets */; }; @@ -95,6 +96,7 @@ 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+PixelData.swift"; sourceTree = ""; }; 30B2818924C001A000647B83 /* Pixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pixel.swift; sourceTree = ""; }; 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImageCreator.swift; sourceTree = ""; }; + 30B2818D24C091A400647B83 /* Color+UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+UIColor.swift"; sourceTree = ""; }; 30BFC8CA24B75E6C00DAC6D9 /* Project SF.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Project SF.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSFApp.swift; sourceTree = ""; }; 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -196,6 +198,7 @@ 9C7B4D0524B89AC700FC4456 /* View+ForegroundModifier.swift */, 9CCFDD2324B9745E00162B0F /* UIApplication+IsTesting.swift */, 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */, + 30B2818D24C091A400647B83 /* Color+UIColor.swift */, ); path = Extensions; sourceTree = ""; @@ -590,6 +593,7 @@ 30D3217D24BB4EDE009CD9D0 /* ActivityRings.swift in Sources */, 307648C924BC7DC5005D8531 /* GrantDataAccessView.swift in Sources */, 30D321BF24BB65D5009CD9D0 /* FriendsView.swift in Sources */, + 30B2818E24C091A400647B83 /* Color+UIColor.swift in Sources */, 302CF88A24BA199E00FF79D7 /* RingType.swift in Sources */, 9C7B4D0624B89AC700FC4456 /* View+ForegroundModifier.swift in Sources */, 30CCF30724BED9EA00103C1E /* ActivityOverview.swift in Sources */, diff --git a/Project SF/Models/Pixel.swift b/Project SF/Models/Pixel.swift index 43ff7f5..4d9aaa4 100644 --- a/Project SF/Models/Pixel.swift +++ b/Project SF/Models/Pixel.swift @@ -5,7 +5,7 @@ // Created by Roman Esin on 16.07.2020. // -import Foundation +import SwiftUI // swiftlint:disable identifier_name struct Pixel { @@ -36,17 +36,24 @@ class PixelImage { /// - width: Width of the image. /// - height: Height of the image. /// - Returns: Random symmetrixal `PixelImage` with given size. - static func randomSymmetrical(width: Int, height: Int) -> PixelImage { + static func randomSymmetrical(color: Color, width: Int, height: Int) -> PixelImage { var pixels: [Pixel] = [] let clear = Pixel(a: 255, r: 240, g: 240, b: 240) + let uiColor = color.uiColor() + var a: CGFloat = 0 + var r: CGFloat = 0 + var g: CGFloat = 0 + var b: CGFloat = 0 + uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) + let color = Pixel(a: 255, - r: .random(in: 0...255), - g: .random(in: 0...255), - b: .random(in: 0...255)) + r: UInt8(r * 255), + g: UInt8(g * 255), + b: UInt8(b * 255)) var slice: [Pixel] = [] let num = width % 2 diff --git a/Project SF/Utilities/Extensions/Color+UIColor.swift b/Project SF/Utilities/Extensions/Color+UIColor.swift new file mode 100644 index 0000000..a1de7ef --- /dev/null +++ b/Project SF/Utilities/Extensions/Color+UIColor.swift @@ -0,0 +1,39 @@ +// +// Color+UIColor.swift +// Project SF +// +// Created by Roman Esin on 16.07.2020. +// + +import SwiftUI + +// Reference: https://stackoverflow.com/a/58531033/10616784 +extension Color { + + func uiColor() -> UIColor { + + let components = self.components() + return UIColor(red: components.r, green: components.g, blue: components.b, alpha: components.a) + } + + // swiftlint:disable large_tuple + // swiftlint:disable identifier_name + private func components() -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) { + + let scanner = Scanner(string: self.description.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)) + var hexNumber: UInt64 = 0 + var r: CGFloat = 0.0 + var g: CGFloat = 0.0 + var b: CGFloat = 0.0 + var a: CGFloat = 0.0 + + let result = scanner.scanHexInt64(&hexNumber) + if result { + r = CGFloat((hexNumber & 0xff000000) >> 24) / 255 + g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255 + b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255 + a = CGFloat(hexNumber & 0x000000ff) / 255 + } + return (r, g, b, a) + } +} diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift index fde92a5..b2a7288 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift @@ -11,6 +11,7 @@ struct ProfileImageCreator: View { @Environment(\.presentationMode) var presentationMode @Binding var image: UIImage? + @State var color = Color.accentColor var body: some View { NavigationView { @@ -24,10 +25,14 @@ struct ProfileImageCreator: View { .frame(width: 200, height: 200) .clipShape(Circle()) .animation(.spring()) + + // TODO: Dynamically change color of the image + ColorPicker("Select foreground color", selection: $color) + .padding(.top) } RoundedButton("Generate image") { withAnimation { - image = UIImage(pixelImage: .randomSymmetrical(width: 7, height: 7)) + image = UIImage(pixelImage: .randomSymmetrical(color: color, width: 7, height: 7)) } } .padding(.top) @@ -47,6 +52,6 @@ struct ProfileImageCreator: View { struct ProfileImageCreator_Previews: PreviewProvider { static var previews: some View { - ProfileImageCreator(.constant(UIImage(pixelImage: .randomSymmetrical(width: 10, height: 10)))) + ProfileImageCreator(.constant(UIImage(pixelImage: .randomSymmetrical(color: .red, width: 10, height: 10)))) } } From eede9a2e63f1117ae289c6fdadd362832cc3cdf3 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Fri, 17 Jul 2020 00:06:52 +1000 Subject: [PATCH 09/17] Update some stuff. --- .../Views/Tabs/Profile/Settings/ProfileImageCreator.swift | 5 +---- Project SF/Views/Tabs/Profile/Settings/SettingsView.swift | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift index b2a7288..ea031fe 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift @@ -27,7 +27,7 @@ struct ProfileImageCreator: View { .animation(.spring()) // TODO: Dynamically change color of the image - ColorPicker("Select foreground color", selection: $color) + ColorPicker("Select foreground color", selection: $color, supportsOpacity: false) .padding(.top) } RoundedButton("Generate image") { @@ -39,9 +39,6 @@ struct ProfileImageCreator: View { } .padding(.horizontal) .navigationTitle("Profile Image Creator") - .navigationBarItems(trailing: NavigationBarButton(systemName: "xmark.circle.fill", action: { - presentationMode.wrappedValue.dismiss() - })) } } diff --git a/Project SF/Views/Tabs/Profile/Settings/SettingsView.swift b/Project SF/Views/Tabs/Profile/Settings/SettingsView.swift index f9d107a..0fa94c6 100644 --- a/Project SF/Views/Tabs/Profile/Settings/SettingsView.swift +++ b/Project SF/Views/Tabs/Profile/Settings/SettingsView.swift @@ -21,6 +21,9 @@ struct AboutFooter: View { } struct SettingsView: View { + + @Environment(\.presentationMode) var presentationMode + var body: some View { List { Section(header: Text("General")) { From d1abc8991cc033d40d2c5d1b962918d66df65193 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Fri, 17 Jul 2020 19:11:40 +1000 Subject: [PATCH 10/17] Revert "Merge branch 'development' into profile-image-generator" This reverts commit 4b511dcf7a5380a1062a2fd937c801edf48b866b, reversing changes made to eede9a2e63f1117ae289c6fdadd362832cc3cdf3. --- Project SF.xcodeproj/project.pbxproj | 32 --- .../shadow.colorset/Contents.json | 38 ---- .../Int+ConvertFromRangeToRange copy.swift | 21 -- .../Tabs/Competitions/CompetitionCell.swift | 88 +++----- .../CompetitionDetail/CompetitionDetail.swift | 73 ------- .../CompetitionDetail/CompetitorCell.swift | 70 ------- .../CompetitionDetail/CompetitorDetail.swift | 69 ------- .../CompetitionDetail/PointsGraph.swift | 195 ------------------ .../Tabs/Competitions/CompetitionsView.swift | 90 ++------ .../Tabs/Competitions/CreateCompetition.swift | 46 ++--- .../Tabs/Competitions/PlaceBadgeView.swift | 94 --------- .../Views/Tabs/Friends/FriendDetailView.swift | 31 +-- 12 files changed, 59 insertions(+), 788 deletions(-) delete mode 100644 Project SF/Assets.xcassets/shadow.colorset/Contents.json delete mode 100644 Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift delete mode 100644 Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitionDetail.swift delete mode 100644 Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorCell.swift delete mode 100644 Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorDetail.swift delete mode 100644 Project SF/Views/Tabs/Competitions/CompetitionDetail/PointsGraph.swift delete mode 100644 Project SF/Views/Tabs/Competitions/PlaceBadgeView.swift diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index c02c7a5..54cb98d 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -7,14 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 3019CD9924BC9792002564AD /* PlaceBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */; }; - 30251C1924BFC1A50058D6D2 /* CompetitorDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */; }; - 30251C1B24BFC1B10058D6D2 /* CompetitorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */; }; - 30251C1E24BFE1D50058D6D2 /* PointsGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */; }; - 30278E9324BC553A00E87E80 /* CompetitionDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */; }; 302CF88A24BA199E00FF79D7 /* RingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302CF88924BA199E00FF79D7 /* RingType.swift */; }; 302CF88C24BA1A4000FF79D7 /* ActivityResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302CF88B24BA1A4000FF79D7 /* ActivityResult.swift */; }; - 3050123624C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */; }; 3067712F24BDE0080085F152 /* FriendsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3067712E24BDE0080085F152 /* FriendsCell.swift */; }; 3067713124BDE3950085F152 /* FriendDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3067713024BDE3950085F152 /* FriendDetailView.swift */; }; 307648C924BC7DC5005D8531 /* GrantDataAccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 307648C824BC7DC4005D8531 /* GrantDataAccessView.swift */; }; @@ -88,14 +82,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceBadgeView.swift; sourceTree = ""; }; - 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitorDetail.swift; sourceTree = ""; }; - 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitorCell.swift; sourceTree = ""; }; - 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointsGraph.swift; sourceTree = ""; }; - 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitionDetail.swift; sourceTree = ""; }; 302CF88924BA199E00FF79D7 /* RingType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RingType.swift; sourceTree = ""; }; 302CF88B24BA1A4000FF79D7 /* ActivityResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityResult.swift; sourceTree = ""; }; - 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+ConvertFromRangeToRange copy.swift"; sourceTree = ""; }; 3067712E24BDE0080085F152 /* FriendsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendsCell.swift; sourceTree = ""; }; 3067713024BDE3950085F152 /* FriendDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendDetailView.swift; sourceTree = ""; }; 307648C824BC7DC4005D8531 /* GrantDataAccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrantDataAccessView.swift; sourceTree = ""; }; @@ -186,17 +174,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 30251C1C24BFC1B80058D6D2 /* CompetitionDetail */ = { - isa = PBXGroup; - children = ( - 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */, - 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */, - 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */, - 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */, - ); - path = CompetitionDetail; - sourceTree = ""; - }; 3029AF5024B8869E003F0324 /* Views */ = { isa = PBXGroup; children = ( @@ -218,7 +195,6 @@ isa = PBXGroup; children = ( 9C7B4D0724B89AF100FC4456 /* Double+ConvertFromRangeToRange.swift */, - 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */, 9C7B4D0524B89AC700FC4456 /* View+ForegroundModifier.swift */, 9CCFDD2324B9745E00162B0F /* UIApplication+IsTesting.swift */, 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */, @@ -332,11 +308,9 @@ 30D321A624BB65D5009CD9D0 /* Competitions */ = { isa = PBXGroup; children = ( - 30251C1C24BFC1B80058D6D2 /* CompetitionDetail */, 30D321A724BB65D5009CD9D0 /* CompetitionsView.swift */, 30D321A824BB65D5009CD9D0 /* CompetitionCell.swift */, 30D321A924BB65D5009CD9D0 /* CreateCompetition.swift */, - 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */, ); path = Competitions; sourceTree = ""; @@ -622,10 +596,8 @@ 30B2818E24C091A400647B83 /* Color+UIColor.swift in Sources */, 302CF88A24BA199E00FF79D7 /* RingType.swift in Sources */, 9C7B4D0624B89AC700FC4456 /* View+ForegroundModifier.swift in Sources */, - 30251C1B24BFC1B10058D6D2 /* CompetitorCell.swift in Sources */, 30CCF30724BED9EA00103C1E /* ActivityOverview.swift in Sources */, 30D321BA24BB65D5009CD9D0 /* NotificationSettings.swift in Sources */, - 30251C1E24BFE1D50058D6D2 /* PointsGraph.swift in Sources */, 30BFC8E724B8041C00DAC6D9 /* ActivityRingsView.swift in Sources */, 3090918224B9480F000E3B11 /* RoundedButton.swift in Sources */, 9CCFDD2424B9745E00162B0F /* UIApplication+IsTesting.swift in Sources */, @@ -635,8 +607,6 @@ 3067713124BDE3950085F152 /* FriendDetailView.swift in Sources */, 9C6C74AC24BADD1800C657B0 /* RoundedNavigationButton.swift in Sources */, 30D321BB24BB65D5009CD9D0 /* PermissionSettings.swift in Sources */, - 3050123624C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift in Sources */, - 30278E9324BC553A00E87E80 /* CompetitionDetail.swift in Sources */, 30D321B824BB65D5009CD9D0 /* SettingsView.swift in Sources */, 307648CB24BC81C8005D8531 /* iCloudErrorView.swift in Sources */, ); @@ -791,7 +761,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "Project SF/Project SF.entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"Project SF/Preview Content\""; DEVELOPMENT_TEAM = W9K99K5JN4; @@ -804,7 +773,6 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.RomanEsin.ProjectSF; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/Project SF/Assets.xcassets/shadow.colorset/Contents.json b/Project SF/Assets.xcassets/shadow.colorset/Contents.json deleted file mode 100644 index dd489ec..0000000 --- a/Project SF/Assets.xcassets/shadow.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "0.300", - "blue" : "0.000", - "green" : "0.000", - "red" : "0.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "0.300", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift b/Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift deleted file mode 100644 index 020c8f0..0000000 --- a/Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Int+ConvertFromRangeToRange.swift -// Project SF -// -// Created by Christian Privitelli on 16/7/20. -// - -import Foundation - -extension Int { - - public func convert(fromRange: (Int, Int), toRange: (Int, Int)) -> Int { - var value = self - value -= fromRange.0 - value /= Int(fromRange.1 - fromRange.0) - value *= toRange.1 - toRange.0 - value += toRange.0 - return value - } - -} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionCell.swift b/Project SF/Views/Tabs/Competitions/CompetitionCell.swift index 0713c3c..435fb5f 100644 --- a/Project SF/Views/Tabs/Competitions/CompetitionCell.swift +++ b/Project SF/Views/Tabs/Competitions/CompetitionCell.swift @@ -8,90 +8,50 @@ import SwiftUI struct CompetitionCell: View { - - let activity = ActivityRings( - moveCurrent: 350, - moveGoal: 300, - exerciseCurrent: 4, - exerciseGoal: 30, - standCurrent: 1, - standGoal: 12 - ) - let competition: Competition + let competitionName: String + let startDate: Date + let endDate: Date var body: some View { NavigationLink( - destination: CompetitionDetail(competition: competition), + destination: Text("Destination"), label: { HStack { - VStack(spacing: 8) { - PlaceBadgeView( - place: competition.place, - flippable: false, - activityRings: .constant(activity), - font: .system(size: 23), - innerPadding: 10, - outerPadding: 4 - ) + VStack { + Text("1st") + .font(.largeTitle) + .bold() + Text("5 points") + .foregroundColor(.secondary) + .font(.subheadline) } - .frame(width: 65) - .padding(.trailing, 8) - + .padding(.horizontal, 8) + VStack(alignment: .leading) { - Text("\(competition.creatingUser.points) points, \(competition.endDate, style: .relative) \(competition.endDate < Date() ? "ago" : "left")") + Spacer() + Text("\(endDate, style: .relative) \(endDate < Date() ? "ago" : "")") .foregroundColor(.secondary) .font(.subheadline) - .lineLimit(1) - .minimumScaleFactor(0.6) Spacer() - Text(competition.name) - .font(.title2) - .fontWeight(.bold) + Text(competitionName) + .font(.headline) + Spacer() } - .padding(.vertical, 8) + Spacer() } + .frame(maxWidth: .infinity, alignment: .leading) }) .padding(.vertical, 8) } - - init(_ competition: Competition) { - self.competition = competition - } } struct CompetitionCell_Previews: PreviewProvider { - static var previews: some View { - var competitions: [Competition] = [ - Competition( - name: "Competition1", - startDate: Date() - 100000, - endDate: Date() + 100000, - creatingUser: CompetingPerson(name: "Me", points: 300), - people: [ - CompetingPerson(name: "Person1", points: 100), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 6000) - ] - ), - Competition( - name: "Competition2", - startDate: Date(), - endDate: Date() + 1000000, - creatingUser: CompetingPerson(name: "Me", points: 5500), - people: [ - CompetingPerson(name: "Person1", points: 5000), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 500) - ] - ) - ] - - return VStack(spacing: 32) { - CompetitionCell(competitions[0]) - .frame(height: 100) + VStack(spacing: 32) { + CompetitionCell(competitionName: "test", startDate: Date(), endDate: Date() + 1000) + .frame(width: 500, height: 40) - CompetitionCell(competitions[1]) + CompetitionCell(competitionName: "test", startDate: Date(), endDate: Date() - 1000) .frame(width: 500, height: 40) } } diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitionDetail.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitionDetail.swift deleted file mode 100644 index 34234ab..0000000 --- a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitionDetail.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// CompetitionDetail.swift -// Project SF -// -// Created by Christian Privitelli on 13/7/20. -// - -import SwiftUI - -struct CompetitionDetail: View { - - @EnvironmentObject var healthKit: HealthKitController - @Environment(\.colorScheme) var colorScheme - let competition: Competition - - var body: some View { - List { - HStack { - PlaceBadgeView( - place: competition.place, - flippable: true, - activityRings: $healthKit.latestActivityData - ) - VStack(alignment: .leading) { - Text("\(competition.creatingUser.points) points") - .font(.title) - .fontWeight(.medium) - HStack(spacing: 0) { - Text(competition.endDate, style: .relative) - Text(" to go.") - } - .font(.title3) - .foregroundColor(Color(.tertiaryLabel)) - } - } - .padding(.vertical) - .padding(.horizontal) - - Section(header: Text("Leaderboard")) { - ForEach(competition.people.sorted { $0.points > $1.points }) { person in - CompetitorCell(competition: competition, person: person) - } - } - Section(header: Text("Your Points History")) { - PointsGraph() - .frame(height: 220) - } - } - .listStyle(InsetGroupedListStyle()) - .navigationTitle(competition.name) - } -} - -struct CompetitionDetail_Previews: PreviewProvider { - static var previews: some View { - NavigationView { - CompetitionDetail( - competition: Competition( - name: "CompetitionName", - startDate: Date() - 100000, - endDate: Date() + 100000, - creatingUser: CompetingPerson(name: "Me", points: 150), - people: [ - CompetingPerson(name: "Person1", points: 100), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 0) - ] - ) - ) - .environmentObject(HealthKitController()) - } - } -} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorCell.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorCell.swift deleted file mode 100644 index 493a571..0000000 --- a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorCell.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// CompetitorCell.swift -// Project SF -// -// Created by Christian Privitelli on 16/7/20. -// - -import SwiftUI - -struct CompetitorCell: View { - - let competition: Competition - let person: CompetingPerson - - @ViewBuilder - var body: some View { - if competition.creatingUser == person { - cellBody() - } else { - NavigationLink(destination: CompetitorDetail(competition: competition, person: person)) { - cellBody() - } - } - } - - func cellBody() -> some View { - HStack { - Text("\(competition.people.sorted { $0.points > $1.points }.firstIndex(of: person)! + 1)") - .font(.caption) - .foregroundColor(Color(.tertiaryLabel)) - Image(systemName: "circle.fill") - .resizable() - .frame(width: 40, height: 40) - .foregroundColor(.init( - red: Double.random(in: 0...1), - green: Double.random(in: 0...1), - blue: Double.random(in: 0...1) - ) - ) - .padding(.leading, 4) - VStack(alignment: .leading) { - Text(person.name) - .fontWeight(.bold) - Text("\(person.points) points") - .font(.caption) - .foregroundColor(Color(.secondaryLabel)) - } - } - } -} - -struct CompetitorCell_Previews: PreviewProvider { - static var previews: some View { - CompetitorCell( - competition: Competition( - name: "CompetitionName", - startDate: Date() - 100000, - endDate: Date() + 100000, - creatingUser: CompetingPerson(name: "Me", points: 150), - people: [ - CompetingPerson(name: "Person1", points: 100), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 0), - CompetingPerson(name: "Me", points: 150) - ] - ), - person: CompetingPerson(name: "Me", points: 150) - ) - } -} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorDetail.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorDetail.swift deleted file mode 100644 index cd6832b..0000000 --- a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorDetail.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// CompetitorDetail.swift -// Project SF -// -// Created by Christian Privitelli on 16/7/20. -// - -import SwiftUI - -struct CompetitorDetail: View { - - @EnvironmentObject var healthKit: HealthKitController - let competition: Competition - let person: CompetingPerson - - var body: some View { - List { - Section { - HStack { - PlaceBadgeView( - place: competition.people.sorted { $0.points > $1.points }.firstIndex(of: person)! + 1, - flippable: true, - activityRings: $healthKit.latestActivityData // TODO: Add real user activity here - ) - VStack(alignment: .leading) { - Text("\(person.points) points") - .font(.title) - .fontWeight(.medium) - } - } - .padding(.vertical) - .padding(.horizontal) - } - - Section(header: Text("\(person.name)s Point History")) { - PointsGraph() - .frame(height: 230) - } - - Section { - Button("Send friend request.") { - // TODO: Send friend request here. - } - } - } - .navigationTitle(person.name) - .listStyle(InsetGroupedListStyle()) - } -} - -struct CompetitorDetail_Previews: PreviewProvider { - static var previews: some View { - CompetitorDetail( - competition: Competition( - name: "CompetitionName", - startDate: Date() - 100000, - endDate: Date() + 100000, - creatingUser: CompetingPerson(name: "Me", points: 150), - people: [ - CompetingPerson(name: "Person1", points: 100), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 0), - CompetingPerson(name: "Me", points: 150) - ] - ), - person: CompetingPerson(name: "Me", points: 150) - ) - } -} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetail/PointsGraph.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetail/PointsGraph.swift deleted file mode 100644 index f69f824..0000000 --- a/Project SF/Views/Tabs/Competitions/CompetitionDetail/PointsGraph.swift +++ /dev/null @@ -1,195 +0,0 @@ -// -// PointsGraph.swift -// Project SF -// -// Created by Christian Privitelli on 16/7/20. -// - -import Foundation -import SwiftUI - -struct PointsGraph: View { - - @State var currentPage: Int = 0 - @State var translation: CGFloat = 0 - var startDate: Date - let history: [CGFloat] - let bars = 7 - - init(history: [CGFloat] = [10, 40, 500, 250, 5, 65, 92, 90, 95, 76, 29]) { - self.history = history - self.startDate = Date() - self.startDate = Calendar.current.date(byAdding: .day, value: -history.count, to: Date())! - } - - var body: some View { - VStack { - GeometryReader { masterGeo in - HStack(spacing: 0) { - ForEach(1 ..< pages() + 1) { page in - VStack { - Text(dateRange()) - .font(.caption) - .foregroundColor(Color(.tertiaryLabel)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal) - GeometryReader { innerGeo in - HStack { - ForEach(history.indices[start(page) ..< end(page)], - id: \.self) { index in - bar(currentPage == page ? barHeight(history[index], - height: innerGeo.size.height, - page: page) : 0) - if index == end(page) - 1 { - VStack { - Text("\(Int(maxFor(page)))") - Spacer() - Text("0") - } - .font(.caption) - .foregroundColor(Color(.tertiaryLabel)) - } - } - } - .padding(.horizontal) - .frame(width: masterGeo.size.width) - } - } - } - } - .offset(x: calcOffset(width: masterGeo.size.width)) - .frame(width: masterGeo.size.width*CGFloat(pages())) - .gesture( - DragGesture() - .onChanged { value in - translation = value.translation.width - } - .onEnded { _ in - withAnimation(.spring()) { - if translation < -100 { - currentPage = min(pages(), currentPage+1) - } else if translation > 100 { - currentPage = max(1, currentPage-1) - } - translation = 0 - } - } - ) - } - .onAppear { - currentPage = pages() - } - HStack { - ForEach(1 ..< pages() + 1) { dot in - Circle() - .foregroundColor(dot == currentPage ? Color(.systemGray) : Color(.tertiaryLabel)) - .frame(width: 8, height: 8) - } - } - .padding(.top, 6) - } - } - - /// Calculates date range for current page. - /// - Returns: A closed range of dates. - func dateRange() -> ClosedRange { - let startPageValue = (currentPage - 1) * bars - var lastPageValue = 0 - - if currentPage == pages() { // Then it's the last page - let lastPageAmount = (history.count - (pages() - 1) * bars) - 1 - lastPageValue = startPageValue+lastPageAmount - } else { - lastPageValue = bars * currentPage - 1 - } - - let start = Calendar.current.date(byAdding: .day, value: startPageValue, to: startDate)! - let endDate = Calendar.current.date(byAdding: .day, value: lastPageValue, to: startDate)! - return start...endDate - } - - /// Calculates the offset and page of the view - /// - Parameter width: Parent view width. - /// - Returns: An offset as a CGFloat. - func calcOffset(width: CGFloat) -> CGFloat { - if currentPage == 1 { - return 0 + translation - } else { - let negativeWidth = -width - let calculated = negativeWidth * CGFloat(currentPage - 1) - return calculated + translation - } - } - - /// Maps pages to be opposite so latest data shows first. - /// - Parameter page: The current non converted page. - /// - Returns: Int of converted range page. - func converted(_ page: Int) -> Int { - let range = (1, pages()) - let reversedRange = (pages(), 1) - let converted = page.convert(fromRange: range, toRange: reversedRange) - return Int(converted) - } - - /// Function to calculate how many pages there should be based on provided array. - /// - Returns: Int for number of pages. - func pages() -> Int { - return Int(ceil(Double(history.count) / Double(bars))) - } - - /// Gets the max value on a certain page. - /// - Parameter page: Page to find max value. - /// - Returns: Max value on certain page as CGFloat. - func maxFor(_ page: Int) -> CGFloat { - return history[start(page) ..< end(page)].max() ?? 0 - } - - /// Start index for provided array. Use with `end()` function. - /// - Parameter page: Current page. - /// - Returns: Provides the start index for certain page. - func start(_ page: Int) -> Int { - if page == 1 { return 0 } else { - return (page - 1) * bars - } - } - - /// End index for provided array. Use with `start()` function. - /// - Parameter page: Current page. - /// - Returns: Provides the end index for certain page. - func end(_ page: Int) -> Int { - return min(page * bars, history.count) - } - - /// Calculates what the height of a bar should be based on it's value, view height and the current page. - /// - Parameters: - /// - value: Value of the bar (how many points) - /// - height: Height of view. Use geometry reader to feed max height possible - /// - page: Current page. Used to find max value for specific page. - /// - Returns: Returns an approriate height for the bar fill. - func barHeight(_ value: CGFloat, height: CGFloat, page: Int) -> CGFloat { - return (value/maxFor(page)) * height - } - - /// The bar view, set any bar styling here. - /// - Parameter height: The height of the filled bar - /// - Returns: Returns a bar view. - func bar(_ height: CGFloat) -> some View { - ZStack(alignment: .bottom) { - RoundedRectangle(cornerRadius: 10, style: .continuous) - .foregroundColor(Color(.secondarySystemFill)) - RoundedRectangle(cornerRadius: 10, style: .continuous) - .foregroundColor(.accentColor) - .frame(height: height) - .animation(.spring()) - } - .frame(maxWidth: 45) - .mask(RoundedRectangle(cornerRadius: 10, style: .continuous)) - } -} - -struct PointsGraph_Previews: PreviewProvider { - static var previews: some View { - PointsGraph() - .frame(width: nil, height: 500, alignment: .center) - } -} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionsView.swift b/Project SF/Views/Tabs/Competitions/CompetitionsView.swift index a5e196e..4b97537 100644 --- a/Project SF/Views/Tabs/Competitions/CompetitionsView.swift +++ b/Project SF/Views/Tabs/Competitions/CompetitionsView.swift @@ -12,33 +12,6 @@ struct Competition: Identifiable { var name: String var startDate: Date var endDate: Date - let creatingUser: CompetingPerson - var people: [CompetingPerson] = [] - - var place: Int - - init(name: String, startDate: Date, endDate: Date, creatingUser: CompetingPerson, people: [CompetingPerson] = []) { - self.name = name - self.startDate = startDate - self.endDate = endDate - self.creatingUser = creatingUser - self.people = people - - self.people.append(self.creatingUser) - self.people.sort { - $0.points > $1.points - } - - let index = self.people.firstIndex(of: creatingUser) ?? 0 - self.place = index + 1 - } -} - -struct CompetingPerson: Identifiable, Equatable { - var id = UUID() - var name: String - var points: Int = 0 - var history: [Int] = [] } struct CompetitionsView: View { @@ -49,53 +22,15 @@ struct CompetitionsView: View { // Temporary. Get these from CK when thats working. var competitions: [Competition] = [ - Competition( - name: "Competition1", - startDate: Date() - 100000, - endDate: Date() + 100000, - creatingUser: CompetingPerson(name: "Me", points: 300), - people: [ - CompetingPerson(name: "Person1", points: 100), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 6000) - ] - ), - Competition( - name: "Competition2", - startDate: Date(), - endDate: Date() + 1000000, - creatingUser: CompetingPerson(name: "Me", points: 5500), - people: [ - CompetingPerson(name: "Person1", points: 5000), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 500) - ] - ) + Competition(name: "Competition1", startDate: Date() - 100000, endDate: Date() + 100000), + Competition(name: "Competition2", startDate: Date() - 100000, endDate: Date() + 30000000), + Competition(name: "Competition3", startDate: Date() - 100000, endDate: Date() + 9900000) ] var recentCompetitions: [Competition] = [ - Competition( - name: "Competition1", - startDate: Date() - 100000, - endDate: Date() - 1000, - creatingUser: CompetingPerson(name: "Me", points: 50), - people: [ - CompetingPerson(name: "Person1", points: 100), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 6000) - ] - ), - Competition( - name: "Competition2", - startDate: Date() - 100000, - endDate: Date() - 10000, - creatingUser: CompetingPerson(name: "Me", points: 300), - people: [ - CompetingPerson(name: "Person1", points: 5000), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 500) - ] - ) + Competition(name: "Competition1", startDate: Date() - 100000, endDate: Date() - 1000), + Competition(name: "Competition2", startDate: Date() - 1000000, endDate: Date() - 10000), + Competition(name: "Competition3", startDate: Date() - 100000, endDate: Date() - 12345) ] var body: some View { @@ -105,15 +40,21 @@ struct CompetitionsView: View { ActivityOverview(activity: $healthKit.latestActivityData) } - Section(header: Text("Currently competing")) { + Section(header: Text("Currently Competing")) { ForEach(competitions.indices) { index in - CompetitionCell(competitions[index]) + CompetitionCell( + competitionName: competitions[index].name, + startDate: competitions[index].startDate, + endDate: competitions[index].endDate + ) } } Section(header: Text("Recent competitions")) { ForEach(recentCompetitions.indices) { index in - CompetitionCell(competitions[index]) + CompetitionCell(competitionName: recentCompetitions[index].name, + startDate: recentCompetitions[index].startDate, + endDate: recentCompetitions[index].endDate) } } } @@ -140,6 +81,5 @@ struct CompetitionsView: View { struct CompetitionsView_Previews: PreviewProvider { static var previews: some View { CompetitionsView() - .environmentObject(HealthKitController()) } } diff --git a/Project SF/Views/Tabs/Competitions/CreateCompetition.swift b/Project SF/Views/Tabs/Competitions/CreateCompetition.swift index 24f007c..a3fb06a 100644 --- a/Project SF/Views/Tabs/Competitions/CreateCompetition.swift +++ b/Project SF/Views/Tabs/Competitions/CreateCompetition.swift @@ -10,7 +10,6 @@ import SwiftUI struct CreateCompetition: View { @Environment(\.presentationMode) var presentationMode - @Environment(\.locale) var locale @State var competitionName = "" @State var competitionEndDate = Date() + 60 * 60 * 24 @State var pickedDate = 0 @@ -85,21 +84,15 @@ struct CreateCompetition: View { HStack { Image(systemName: "chevron.down") .foreground(Color(.tertiaryLabel)) - Stepper("Goal") { - stepsGoalInt = min(50000, max(1000, stepsGoalInt + 1000)) - stepsGoal = String(stepsGoalInt) - } onDecrement: { - stepsGoalInt = min(50000, max(1000, stepsGoalInt - 1000)) - stepsGoal = String(stepsGoalInt) - } - .opacity(Int(stepsGoal) == nil ? 1 : 1) // Fixes stepper issue. + Stepper("Goal", value: $stepsGoalInt, in: 1000...50000, step: 1000) } HStack { Spacer() - TextField("Amount", text: $stepsGoal, onEditingChanged: { _ in - stepsGoalInt = min(50000, max(1000, Int(stepsGoal) ?? 10000)) + TextField("Amount", text: $stepsGoal) { _ in } onCommit: { + stepsGoalInt = Int(stepsGoal) ?? 10000 + stepsGoalInt = stepsGoalInt == 0 ? 1 : stepsGoalInt stepsGoal = String(stepsGoalInt) - }) + } .keyboardType(.numberPad) .multilineTextAlignment(.trailing) Text("steps") @@ -117,33 +110,18 @@ struct CreateCompetition: View { HStack { Image(systemName: "chevron.down") .foreground(Color(.tertiaryLabel)) - Stepper("Goal") { - distanceGoalInt = min( - locale.usesMetricSystem ? 1000 : 600, - max(1, distanceGoalInt + 1) - ) - distanceGoal = String(distanceGoalInt) - } onDecrement: { - distanceGoalInt = min( - locale.usesMetricSystem ? 1000 : 600, - max(1, distanceGoalInt - 1) - ) - distanceGoal = String(distanceGoalInt) - } - .opacity(Int(distanceGoal) == nil ? 1 : 1) // Fixes stepper issue. + Stepper("Goal", value: $distanceGoalInt, in: 1...100, step: 1) } HStack { Spacer() - TextField("Amount", text: $distanceGoal, onEditingChanged: { _ in - distanceGoalInt = min( - locale.usesMetricSystem ? 1000 : 600, - max(1, Int(distanceGoal) ?? 10) - ) + TextField("Amount", text: $distanceGoal) { _ in } onCommit: { + distanceGoalInt = Int(distanceGoal) ?? 10 + distanceGoalInt = distanceGoalInt == 0 ? 1 : distanceGoalInt distanceGoal = String(distanceGoalInt) - }) + } .keyboardType(.numberPad) .multilineTextAlignment(.trailing) - Text(locale.usesMetricSystem ? "km" : "miles") + Text("km") .foregroundColor(.secondary) } } @@ -201,7 +179,7 @@ struct CreateCompetition: View { guard !competitionName.isEmpty else { return } presentationMode.wrappedValue.dismiss() } - .disabled(competitionName.isEmpty) + .disabled(competitionName.isEmpty && Int(stepsGoal) == nil) } .padding(.horizontal) .navigationBarTitle("Create Competition") diff --git a/Project SF/Views/Tabs/Competitions/PlaceBadgeView.swift b/Project SF/Views/Tabs/Competitions/PlaceBadgeView.swift deleted file mode 100644 index fcd27be..0000000 --- a/Project SF/Views/Tabs/Competitions/PlaceBadgeView.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// PlaceBadgeView.swift -// Project SF -// -// Created by Christian Privitelli on 13/7/20. -// - -import SwiftUI - -struct PlaceBadgeView: View { - let place: Int - let flippable: Bool - - @Binding var activityRings: ActivityRings - - let font: Font - let innerPadding: CGFloat - let outerPadding: CGFloat - - @State var flipped = false - - init(place: Int, flippable: Bool, - activityRings: Binding, font: Font = .title, - innerPadding: CGFloat = 24, outerPadding: CGFloat = 4) { - self.place = place - self.flippable = flippable - self._activityRings = activityRings - self.font = font - self.innerPadding = innerPadding - self.outerPadding = outerPadding - } - - var body: some View { - Text("\(place)\(place == 1 ? "st" : place == 2 ? "nd" : place == 3 ? "rd" : "th")") - .font(font) - .fontWeight(.black) - .padding(innerPadding) - .opacity(flipped ? 0 : 1) - .background( - Circle() - .foreground( - Group { - if !flipped { - place == 1 ? Color.yellow : - place == 2 ? Color(.lightGray) : - place == 3 ? Color.init(red: 0.6, green: 0.4, blue: 0.3) : - Color.accentColor - } else { - GeometryReader { geometry in - ZStack { - Color(.systemBackground) - ActivityRingsView( - values: $activityRings, - ringSize: RingSize( - size: geometry.size.height-20, - width: (geometry.size.height-20)*0.13, - padding: 2 - ) - ) - .rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0)) - } - } - } - } - ) - .padding(-4) - ) - .rotation3DEffect( - flipped ? .degrees(180) : .degrees(0), - axis: (x: 0, y: 1, z: 0), - anchor: .center - ) - .modifier(OptionalTapGesture(condition: flippable, action: { - withAnimation(.spring()) { - flipped.toggle() - } - })) - .shadow(color: Color.black.opacity(0.1), radius: 10) - } -} - -struct OptionalTapGesture: ViewModifier { - var condition: Bool - var action: () -> Void - - @ViewBuilder - func body(content: Content) -> some View { - if condition { - content.onTapGesture(perform: action) - } else { - content - } - } -} diff --git a/Project SF/Views/Tabs/Friends/FriendDetailView.swift b/Project SF/Views/Tabs/Friends/FriendDetailView.swift index 63e3b8b..360f4c1 100644 --- a/Project SF/Views/Tabs/Friends/FriendDetailView.swift +++ b/Project SF/Views/Tabs/Friends/FriendDetailView.swift @@ -14,28 +14,9 @@ struct FriendDetailView: View { let friend: Friend var competitions: [Competition] = [ - Competition( - name: "Competition1", - startDate: Date() - 100000, - endDate: Date() + 100000, - creatingUser: CompetingPerson(name: "Me", points: 300), - people: [ - CompetingPerson(name: "Person1", points: 100), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 6000) - ] - ), - Competition( - name: "Competition2", - startDate: Date(), - endDate: Date() + 1000000, - creatingUser: CompetingPerson(name: "Me", points: 5500), - people: [ - CompetingPerson(name: "Person1", points: 5000), - CompetingPerson(name: "Person2", points: 200), - CompetingPerson(name: "Person3", points: 500) - ] - ) + Competition(name: "Competition1", startDate: Date() - 100000, endDate: Date() + 100000), + Competition(name: "Competition2", startDate: Date() - 100000, endDate: Date() + 30000000), + Competition(name: "Competition3", startDate: Date() - 100000, endDate: Date() + 9900000) ] var body: some View { @@ -102,7 +83,11 @@ struct FriendDetailView: View { Section(header: Text("Current Competitions")) { ForEach(competitions.indices) { index in - CompetitionCell(competitions[index]) + CompetitionCell( + competitionName: competitions[index].name, + startDate: competitions[index].startDate, + endDate: competitions[index].endDate + ) } } From ea8ae387007438c0178fdb8b5b0fd6d3f4f288c6 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Fri, 17 Jul 2020 19:12:09 +1000 Subject: [PATCH 11/17] Revert "Revert "Merge branch 'development' into profile-image-generator"" This reverts commit d1abc8991cc033d40d2c5d1b962918d66df65193. --- Project SF.xcodeproj/project.pbxproj | 32 +++ .../shadow.colorset/Contents.json | 38 ++++ .../Int+ConvertFromRangeToRange copy.swift | 21 ++ .../Tabs/Competitions/CompetitionCell.swift | 88 +++++--- .../CompetitionDetail/CompetitionDetail.swift | 73 +++++++ .../CompetitionDetail/CompetitorCell.swift | 70 +++++++ .../CompetitionDetail/CompetitorDetail.swift | 69 +++++++ .../CompetitionDetail/PointsGraph.swift | 195 ++++++++++++++++++ .../Tabs/Competitions/CompetitionsView.swift | 90 ++++++-- .../Tabs/Competitions/CreateCompetition.swift | 46 +++-- .../Tabs/Competitions/PlaceBadgeView.swift | 94 +++++++++ .../Views/Tabs/Friends/FriendDetailView.swift | 31 ++- 12 files changed, 788 insertions(+), 59 deletions(-) create mode 100644 Project SF/Assets.xcassets/shadow.colorset/Contents.json create mode 100644 Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift create mode 100644 Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitionDetail.swift create mode 100644 Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorCell.swift create mode 100644 Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorDetail.swift create mode 100644 Project SF/Views/Tabs/Competitions/CompetitionDetail/PointsGraph.swift create mode 100644 Project SF/Views/Tabs/Competitions/PlaceBadgeView.swift diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index 54cb98d..c02c7a5 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -7,8 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 3019CD9924BC9792002564AD /* PlaceBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */; }; + 30251C1924BFC1A50058D6D2 /* CompetitorDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */; }; + 30251C1B24BFC1B10058D6D2 /* CompetitorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */; }; + 30251C1E24BFE1D50058D6D2 /* PointsGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */; }; + 30278E9324BC553A00E87E80 /* CompetitionDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */; }; 302CF88A24BA199E00FF79D7 /* RingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302CF88924BA199E00FF79D7 /* RingType.swift */; }; 302CF88C24BA1A4000FF79D7 /* ActivityResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302CF88B24BA1A4000FF79D7 /* ActivityResult.swift */; }; + 3050123624C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */; }; 3067712F24BDE0080085F152 /* FriendsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3067712E24BDE0080085F152 /* FriendsCell.swift */; }; 3067713124BDE3950085F152 /* FriendDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3067713024BDE3950085F152 /* FriendDetailView.swift */; }; 307648C924BC7DC5005D8531 /* GrantDataAccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 307648C824BC7DC4005D8531 /* GrantDataAccessView.swift */; }; @@ -82,8 +88,14 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceBadgeView.swift; sourceTree = ""; }; + 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitorDetail.swift; sourceTree = ""; }; + 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitorCell.swift; sourceTree = ""; }; + 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointsGraph.swift; sourceTree = ""; }; + 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitionDetail.swift; sourceTree = ""; }; 302CF88924BA199E00FF79D7 /* RingType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RingType.swift; sourceTree = ""; }; 302CF88B24BA1A4000FF79D7 /* ActivityResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityResult.swift; sourceTree = ""; }; + 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+ConvertFromRangeToRange copy.swift"; sourceTree = ""; }; 3067712E24BDE0080085F152 /* FriendsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendsCell.swift; sourceTree = ""; }; 3067713024BDE3950085F152 /* FriendDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendDetailView.swift; sourceTree = ""; }; 307648C824BC7DC4005D8531 /* GrantDataAccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrantDataAccessView.swift; sourceTree = ""; }; @@ -174,6 +186,17 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 30251C1C24BFC1B80058D6D2 /* CompetitionDetail */ = { + isa = PBXGroup; + children = ( + 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */, + 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */, + 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */, + 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */, + ); + path = CompetitionDetail; + sourceTree = ""; + }; 3029AF5024B8869E003F0324 /* Views */ = { isa = PBXGroup; children = ( @@ -195,6 +218,7 @@ isa = PBXGroup; children = ( 9C7B4D0724B89AF100FC4456 /* Double+ConvertFromRangeToRange.swift */, + 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */, 9C7B4D0524B89AC700FC4456 /* View+ForegroundModifier.swift */, 9CCFDD2324B9745E00162B0F /* UIApplication+IsTesting.swift */, 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */, @@ -308,9 +332,11 @@ 30D321A624BB65D5009CD9D0 /* Competitions */ = { isa = PBXGroup; children = ( + 30251C1C24BFC1B80058D6D2 /* CompetitionDetail */, 30D321A724BB65D5009CD9D0 /* CompetitionsView.swift */, 30D321A824BB65D5009CD9D0 /* CompetitionCell.swift */, 30D321A924BB65D5009CD9D0 /* CreateCompetition.swift */, + 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */, ); path = Competitions; sourceTree = ""; @@ -596,8 +622,10 @@ 30B2818E24C091A400647B83 /* Color+UIColor.swift in Sources */, 302CF88A24BA199E00FF79D7 /* RingType.swift in Sources */, 9C7B4D0624B89AC700FC4456 /* View+ForegroundModifier.swift in Sources */, + 30251C1B24BFC1B10058D6D2 /* CompetitorCell.swift in Sources */, 30CCF30724BED9EA00103C1E /* ActivityOverview.swift in Sources */, 30D321BA24BB65D5009CD9D0 /* NotificationSettings.swift in Sources */, + 30251C1E24BFE1D50058D6D2 /* PointsGraph.swift in Sources */, 30BFC8E724B8041C00DAC6D9 /* ActivityRingsView.swift in Sources */, 3090918224B9480F000E3B11 /* RoundedButton.swift in Sources */, 9CCFDD2424B9745E00162B0F /* UIApplication+IsTesting.swift in Sources */, @@ -607,6 +635,8 @@ 3067713124BDE3950085F152 /* FriendDetailView.swift in Sources */, 9C6C74AC24BADD1800C657B0 /* RoundedNavigationButton.swift in Sources */, 30D321BB24BB65D5009CD9D0 /* PermissionSettings.swift in Sources */, + 3050123624C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift in Sources */, + 30278E9324BC553A00E87E80 /* CompetitionDetail.swift in Sources */, 30D321B824BB65D5009CD9D0 /* SettingsView.swift in Sources */, 307648CB24BC81C8005D8531 /* iCloudErrorView.swift in Sources */, ); @@ -761,6 +791,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "Project SF/Project SF.entitlements"; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"Project SF/Preview Content\""; DEVELOPMENT_TEAM = W9K99K5JN4; @@ -773,6 +804,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.RomanEsin.ProjectSF; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/Project SF/Assets.xcassets/shadow.colorset/Contents.json b/Project SF/Assets.xcassets/shadow.colorset/Contents.json new file mode 100644 index 0000000..dd489ec --- /dev/null +++ b/Project SF/Assets.xcassets/shadow.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.300", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.300", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift b/Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift new file mode 100644 index 0000000..020c8f0 --- /dev/null +++ b/Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift @@ -0,0 +1,21 @@ +// +// Int+ConvertFromRangeToRange.swift +// Project SF +// +// Created by Christian Privitelli on 16/7/20. +// + +import Foundation + +extension Int { + + public func convert(fromRange: (Int, Int), toRange: (Int, Int)) -> Int { + var value = self + value -= fromRange.0 + value /= Int(fromRange.1 - fromRange.0) + value *= toRange.1 - toRange.0 + value += toRange.0 + return value + } + +} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionCell.swift b/Project SF/Views/Tabs/Competitions/CompetitionCell.swift index 435fb5f..0713c3c 100644 --- a/Project SF/Views/Tabs/Competitions/CompetitionCell.swift +++ b/Project SF/Views/Tabs/Competitions/CompetitionCell.swift @@ -8,50 +8,90 @@ import SwiftUI struct CompetitionCell: View { - let competitionName: String - let startDate: Date - let endDate: Date + + let activity = ActivityRings( + moveCurrent: 350, + moveGoal: 300, + exerciseCurrent: 4, + exerciseGoal: 30, + standCurrent: 1, + standGoal: 12 + ) + let competition: Competition var body: some View { NavigationLink( - destination: Text("Destination"), + destination: CompetitionDetail(competition: competition), label: { HStack { - VStack { - Text("1st") - .font(.largeTitle) - .bold() - Text("5 points") - .foregroundColor(.secondary) - .font(.subheadline) + VStack(spacing: 8) { + PlaceBadgeView( + place: competition.place, + flippable: false, + activityRings: .constant(activity), + font: .system(size: 23), + innerPadding: 10, + outerPadding: 4 + ) } - .padding(.horizontal, 8) - + .frame(width: 65) + .padding(.trailing, 8) + VStack(alignment: .leading) { - Spacer() - Text("\(endDate, style: .relative) \(endDate < Date() ? "ago" : "")") + Text("\(competition.creatingUser.points) points, \(competition.endDate, style: .relative) \(competition.endDate < Date() ? "ago" : "left")") .foregroundColor(.secondary) .font(.subheadline) + .lineLimit(1) + .minimumScaleFactor(0.6) Spacer() - Text(competitionName) - .font(.headline) - Spacer() + Text(competition.name) + .font(.title2) + .fontWeight(.bold) } - Spacer() + .padding(.vertical, 8) } - .frame(maxWidth: .infinity, alignment: .leading) }) .padding(.vertical, 8) } + + init(_ competition: Competition) { + self.competition = competition + } } struct CompetitionCell_Previews: PreviewProvider { + static var previews: some View { - VStack(spacing: 32) { - CompetitionCell(competitionName: "test", startDate: Date(), endDate: Date() + 1000) - .frame(width: 500, height: 40) + var competitions: [Competition] = [ + Competition( + name: "Competition1", + startDate: Date() - 100000, + endDate: Date() + 100000, + creatingUser: CompetingPerson(name: "Me", points: 300), + people: [ + CompetingPerson(name: "Person1", points: 100), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 6000) + ] + ), + Competition( + name: "Competition2", + startDate: Date(), + endDate: Date() + 1000000, + creatingUser: CompetingPerson(name: "Me", points: 5500), + people: [ + CompetingPerson(name: "Person1", points: 5000), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 500) + ] + ) + ] + + return VStack(spacing: 32) { + CompetitionCell(competitions[0]) + .frame(height: 100) - CompetitionCell(competitionName: "test", startDate: Date(), endDate: Date() - 1000) + CompetitionCell(competitions[1]) .frame(width: 500, height: 40) } } diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitionDetail.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitionDetail.swift new file mode 100644 index 0000000..34234ab --- /dev/null +++ b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitionDetail.swift @@ -0,0 +1,73 @@ +// +// CompetitionDetail.swift +// Project SF +// +// Created by Christian Privitelli on 13/7/20. +// + +import SwiftUI + +struct CompetitionDetail: View { + + @EnvironmentObject var healthKit: HealthKitController + @Environment(\.colorScheme) var colorScheme + let competition: Competition + + var body: some View { + List { + HStack { + PlaceBadgeView( + place: competition.place, + flippable: true, + activityRings: $healthKit.latestActivityData + ) + VStack(alignment: .leading) { + Text("\(competition.creatingUser.points) points") + .font(.title) + .fontWeight(.medium) + HStack(spacing: 0) { + Text(competition.endDate, style: .relative) + Text(" to go.") + } + .font(.title3) + .foregroundColor(Color(.tertiaryLabel)) + } + } + .padding(.vertical) + .padding(.horizontal) + + Section(header: Text("Leaderboard")) { + ForEach(competition.people.sorted { $0.points > $1.points }) { person in + CompetitorCell(competition: competition, person: person) + } + } + Section(header: Text("Your Points History")) { + PointsGraph() + .frame(height: 220) + } + } + .listStyle(InsetGroupedListStyle()) + .navigationTitle(competition.name) + } +} + +struct CompetitionDetail_Previews: PreviewProvider { + static var previews: some View { + NavigationView { + CompetitionDetail( + competition: Competition( + name: "CompetitionName", + startDate: Date() - 100000, + endDate: Date() + 100000, + creatingUser: CompetingPerson(name: "Me", points: 150), + people: [ + CompetingPerson(name: "Person1", points: 100), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 0) + ] + ) + ) + .environmentObject(HealthKitController()) + } + } +} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorCell.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorCell.swift new file mode 100644 index 0000000..493a571 --- /dev/null +++ b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorCell.swift @@ -0,0 +1,70 @@ +// +// CompetitorCell.swift +// Project SF +// +// Created by Christian Privitelli on 16/7/20. +// + +import SwiftUI + +struct CompetitorCell: View { + + let competition: Competition + let person: CompetingPerson + + @ViewBuilder + var body: some View { + if competition.creatingUser == person { + cellBody() + } else { + NavigationLink(destination: CompetitorDetail(competition: competition, person: person)) { + cellBody() + } + } + } + + func cellBody() -> some View { + HStack { + Text("\(competition.people.sorted { $0.points > $1.points }.firstIndex(of: person)! + 1)") + .font(.caption) + .foregroundColor(Color(.tertiaryLabel)) + Image(systemName: "circle.fill") + .resizable() + .frame(width: 40, height: 40) + .foregroundColor(.init( + red: Double.random(in: 0...1), + green: Double.random(in: 0...1), + blue: Double.random(in: 0...1) + ) + ) + .padding(.leading, 4) + VStack(alignment: .leading) { + Text(person.name) + .fontWeight(.bold) + Text("\(person.points) points") + .font(.caption) + .foregroundColor(Color(.secondaryLabel)) + } + } + } +} + +struct CompetitorCell_Previews: PreviewProvider { + static var previews: some View { + CompetitorCell( + competition: Competition( + name: "CompetitionName", + startDate: Date() - 100000, + endDate: Date() + 100000, + creatingUser: CompetingPerson(name: "Me", points: 150), + people: [ + CompetingPerson(name: "Person1", points: 100), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 0), + CompetingPerson(name: "Me", points: 150) + ] + ), + person: CompetingPerson(name: "Me", points: 150) + ) + } +} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorDetail.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorDetail.swift new file mode 100644 index 0000000..cd6832b --- /dev/null +++ b/Project SF/Views/Tabs/Competitions/CompetitionDetail/CompetitorDetail.swift @@ -0,0 +1,69 @@ +// +// CompetitorDetail.swift +// Project SF +// +// Created by Christian Privitelli on 16/7/20. +// + +import SwiftUI + +struct CompetitorDetail: View { + + @EnvironmentObject var healthKit: HealthKitController + let competition: Competition + let person: CompetingPerson + + var body: some View { + List { + Section { + HStack { + PlaceBadgeView( + place: competition.people.sorted { $0.points > $1.points }.firstIndex(of: person)! + 1, + flippable: true, + activityRings: $healthKit.latestActivityData // TODO: Add real user activity here + ) + VStack(alignment: .leading) { + Text("\(person.points) points") + .font(.title) + .fontWeight(.medium) + } + } + .padding(.vertical) + .padding(.horizontal) + } + + Section(header: Text("\(person.name)s Point History")) { + PointsGraph() + .frame(height: 230) + } + + Section { + Button("Send friend request.") { + // TODO: Send friend request here. + } + } + } + .navigationTitle(person.name) + .listStyle(InsetGroupedListStyle()) + } +} + +struct CompetitorDetail_Previews: PreviewProvider { + static var previews: some View { + CompetitorDetail( + competition: Competition( + name: "CompetitionName", + startDate: Date() - 100000, + endDate: Date() + 100000, + creatingUser: CompetingPerson(name: "Me", points: 150), + people: [ + CompetingPerson(name: "Person1", points: 100), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 0), + CompetingPerson(name: "Me", points: 150) + ] + ), + person: CompetingPerson(name: "Me", points: 150) + ) + } +} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetail/PointsGraph.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetail/PointsGraph.swift new file mode 100644 index 0000000..f69f824 --- /dev/null +++ b/Project SF/Views/Tabs/Competitions/CompetitionDetail/PointsGraph.swift @@ -0,0 +1,195 @@ +// +// PointsGraph.swift +// Project SF +// +// Created by Christian Privitelli on 16/7/20. +// + +import Foundation +import SwiftUI + +struct PointsGraph: View { + + @State var currentPage: Int = 0 + @State var translation: CGFloat = 0 + var startDate: Date + let history: [CGFloat] + let bars = 7 + + init(history: [CGFloat] = [10, 40, 500, 250, 5, 65, 92, 90, 95, 76, 29]) { + self.history = history + self.startDate = Date() + self.startDate = Calendar.current.date(byAdding: .day, value: -history.count, to: Date())! + } + + var body: some View { + VStack { + GeometryReader { masterGeo in + HStack(spacing: 0) { + ForEach(1 ..< pages() + 1) { page in + VStack { + Text(dateRange()) + .font(.caption) + .foregroundColor(Color(.tertiaryLabel)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal) + GeometryReader { innerGeo in + HStack { + ForEach(history.indices[start(page) ..< end(page)], + id: \.self) { index in + bar(currentPage == page ? barHeight(history[index], + height: innerGeo.size.height, + page: page) : 0) + if index == end(page) - 1 { + VStack { + Text("\(Int(maxFor(page)))") + Spacer() + Text("0") + } + .font(.caption) + .foregroundColor(Color(.tertiaryLabel)) + } + } + } + .padding(.horizontal) + .frame(width: masterGeo.size.width) + } + } + } + } + .offset(x: calcOffset(width: masterGeo.size.width)) + .frame(width: masterGeo.size.width*CGFloat(pages())) + .gesture( + DragGesture() + .onChanged { value in + translation = value.translation.width + } + .onEnded { _ in + withAnimation(.spring()) { + if translation < -100 { + currentPage = min(pages(), currentPage+1) + } else if translation > 100 { + currentPage = max(1, currentPage-1) + } + translation = 0 + } + } + ) + } + .onAppear { + currentPage = pages() + } + HStack { + ForEach(1 ..< pages() + 1) { dot in + Circle() + .foregroundColor(dot == currentPage ? Color(.systemGray) : Color(.tertiaryLabel)) + .frame(width: 8, height: 8) + } + } + .padding(.top, 6) + } + } + + /// Calculates date range for current page. + /// - Returns: A closed range of dates. + func dateRange() -> ClosedRange { + let startPageValue = (currentPage - 1) * bars + var lastPageValue = 0 + + if currentPage == pages() { // Then it's the last page + let lastPageAmount = (history.count - (pages() - 1) * bars) - 1 + lastPageValue = startPageValue+lastPageAmount + } else { + lastPageValue = bars * currentPage - 1 + } + + let start = Calendar.current.date(byAdding: .day, value: startPageValue, to: startDate)! + let endDate = Calendar.current.date(byAdding: .day, value: lastPageValue, to: startDate)! + return start...endDate + } + + /// Calculates the offset and page of the view + /// - Parameter width: Parent view width. + /// - Returns: An offset as a CGFloat. + func calcOffset(width: CGFloat) -> CGFloat { + if currentPage == 1 { + return 0 + translation + } else { + let negativeWidth = -width + let calculated = negativeWidth * CGFloat(currentPage - 1) + return calculated + translation + } + } + + /// Maps pages to be opposite so latest data shows first. + /// - Parameter page: The current non converted page. + /// - Returns: Int of converted range page. + func converted(_ page: Int) -> Int { + let range = (1, pages()) + let reversedRange = (pages(), 1) + let converted = page.convert(fromRange: range, toRange: reversedRange) + return Int(converted) + } + + /// Function to calculate how many pages there should be based on provided array. + /// - Returns: Int for number of pages. + func pages() -> Int { + return Int(ceil(Double(history.count) / Double(bars))) + } + + /// Gets the max value on a certain page. + /// - Parameter page: Page to find max value. + /// - Returns: Max value on certain page as CGFloat. + func maxFor(_ page: Int) -> CGFloat { + return history[start(page) ..< end(page)].max() ?? 0 + } + + /// Start index for provided array. Use with `end()` function. + /// - Parameter page: Current page. + /// - Returns: Provides the start index for certain page. + func start(_ page: Int) -> Int { + if page == 1 { return 0 } else { + return (page - 1) * bars + } + } + + /// End index for provided array. Use with `start()` function. + /// - Parameter page: Current page. + /// - Returns: Provides the end index for certain page. + func end(_ page: Int) -> Int { + return min(page * bars, history.count) + } + + /// Calculates what the height of a bar should be based on it's value, view height and the current page. + /// - Parameters: + /// - value: Value of the bar (how many points) + /// - height: Height of view. Use geometry reader to feed max height possible + /// - page: Current page. Used to find max value for specific page. + /// - Returns: Returns an approriate height for the bar fill. + func barHeight(_ value: CGFloat, height: CGFloat, page: Int) -> CGFloat { + return (value/maxFor(page)) * height + } + + /// The bar view, set any bar styling here. + /// - Parameter height: The height of the filled bar + /// - Returns: Returns a bar view. + func bar(_ height: CGFloat) -> some View { + ZStack(alignment: .bottom) { + RoundedRectangle(cornerRadius: 10, style: .continuous) + .foregroundColor(Color(.secondarySystemFill)) + RoundedRectangle(cornerRadius: 10, style: .continuous) + .foregroundColor(.accentColor) + .frame(height: height) + .animation(.spring()) + } + .frame(maxWidth: 45) + .mask(RoundedRectangle(cornerRadius: 10, style: .continuous)) + } +} + +struct PointsGraph_Previews: PreviewProvider { + static var previews: some View { + PointsGraph() + .frame(width: nil, height: 500, alignment: .center) + } +} diff --git a/Project SF/Views/Tabs/Competitions/CompetitionsView.swift b/Project SF/Views/Tabs/Competitions/CompetitionsView.swift index 4b97537..a5e196e 100644 --- a/Project SF/Views/Tabs/Competitions/CompetitionsView.swift +++ b/Project SF/Views/Tabs/Competitions/CompetitionsView.swift @@ -12,6 +12,33 @@ struct Competition: Identifiable { var name: String var startDate: Date var endDate: Date + let creatingUser: CompetingPerson + var people: [CompetingPerson] = [] + + var place: Int + + init(name: String, startDate: Date, endDate: Date, creatingUser: CompetingPerson, people: [CompetingPerson] = []) { + self.name = name + self.startDate = startDate + self.endDate = endDate + self.creatingUser = creatingUser + self.people = people + + self.people.append(self.creatingUser) + self.people.sort { + $0.points > $1.points + } + + let index = self.people.firstIndex(of: creatingUser) ?? 0 + self.place = index + 1 + } +} + +struct CompetingPerson: Identifiable, Equatable { + var id = UUID() + var name: String + var points: Int = 0 + var history: [Int] = [] } struct CompetitionsView: View { @@ -22,15 +49,53 @@ struct CompetitionsView: View { // Temporary. Get these from CK when thats working. var competitions: [Competition] = [ - Competition(name: "Competition1", startDate: Date() - 100000, endDate: Date() + 100000), - Competition(name: "Competition2", startDate: Date() - 100000, endDate: Date() + 30000000), - Competition(name: "Competition3", startDate: Date() - 100000, endDate: Date() + 9900000) + Competition( + name: "Competition1", + startDate: Date() - 100000, + endDate: Date() + 100000, + creatingUser: CompetingPerson(name: "Me", points: 300), + people: [ + CompetingPerson(name: "Person1", points: 100), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 6000) + ] + ), + Competition( + name: "Competition2", + startDate: Date(), + endDate: Date() + 1000000, + creatingUser: CompetingPerson(name: "Me", points: 5500), + people: [ + CompetingPerson(name: "Person1", points: 5000), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 500) + ] + ) ] var recentCompetitions: [Competition] = [ - Competition(name: "Competition1", startDate: Date() - 100000, endDate: Date() - 1000), - Competition(name: "Competition2", startDate: Date() - 1000000, endDate: Date() - 10000), - Competition(name: "Competition3", startDate: Date() - 100000, endDate: Date() - 12345) + Competition( + name: "Competition1", + startDate: Date() - 100000, + endDate: Date() - 1000, + creatingUser: CompetingPerson(name: "Me", points: 50), + people: [ + CompetingPerson(name: "Person1", points: 100), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 6000) + ] + ), + Competition( + name: "Competition2", + startDate: Date() - 100000, + endDate: Date() - 10000, + creatingUser: CompetingPerson(name: "Me", points: 300), + people: [ + CompetingPerson(name: "Person1", points: 5000), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 500) + ] + ) ] var body: some View { @@ -40,21 +105,15 @@ struct CompetitionsView: View { ActivityOverview(activity: $healthKit.latestActivityData) } - Section(header: Text("Currently Competing")) { + Section(header: Text("Currently competing")) { ForEach(competitions.indices) { index in - CompetitionCell( - competitionName: competitions[index].name, - startDate: competitions[index].startDate, - endDate: competitions[index].endDate - ) + CompetitionCell(competitions[index]) } } Section(header: Text("Recent competitions")) { ForEach(recentCompetitions.indices) { index in - CompetitionCell(competitionName: recentCompetitions[index].name, - startDate: recentCompetitions[index].startDate, - endDate: recentCompetitions[index].endDate) + CompetitionCell(competitions[index]) } } } @@ -81,5 +140,6 @@ struct CompetitionsView: View { struct CompetitionsView_Previews: PreviewProvider { static var previews: some View { CompetitionsView() + .environmentObject(HealthKitController()) } } diff --git a/Project SF/Views/Tabs/Competitions/CreateCompetition.swift b/Project SF/Views/Tabs/Competitions/CreateCompetition.swift index a3fb06a..24f007c 100644 --- a/Project SF/Views/Tabs/Competitions/CreateCompetition.swift +++ b/Project SF/Views/Tabs/Competitions/CreateCompetition.swift @@ -10,6 +10,7 @@ import SwiftUI struct CreateCompetition: View { @Environment(\.presentationMode) var presentationMode + @Environment(\.locale) var locale @State var competitionName = "" @State var competitionEndDate = Date() + 60 * 60 * 24 @State var pickedDate = 0 @@ -84,15 +85,21 @@ struct CreateCompetition: View { HStack { Image(systemName: "chevron.down") .foreground(Color(.tertiaryLabel)) - Stepper("Goal", value: $stepsGoalInt, in: 1000...50000, step: 1000) + Stepper("Goal") { + stepsGoalInt = min(50000, max(1000, stepsGoalInt + 1000)) + stepsGoal = String(stepsGoalInt) + } onDecrement: { + stepsGoalInt = min(50000, max(1000, stepsGoalInt - 1000)) + stepsGoal = String(stepsGoalInt) + } + .opacity(Int(stepsGoal) == nil ? 1 : 1) // Fixes stepper issue. } HStack { Spacer() - TextField("Amount", text: $stepsGoal) { _ in } onCommit: { - stepsGoalInt = Int(stepsGoal) ?? 10000 - stepsGoalInt = stepsGoalInt == 0 ? 1 : stepsGoalInt + TextField("Amount", text: $stepsGoal, onEditingChanged: { _ in + stepsGoalInt = min(50000, max(1000, Int(stepsGoal) ?? 10000)) stepsGoal = String(stepsGoalInt) - } + }) .keyboardType(.numberPad) .multilineTextAlignment(.trailing) Text("steps") @@ -110,18 +117,33 @@ struct CreateCompetition: View { HStack { Image(systemName: "chevron.down") .foreground(Color(.tertiaryLabel)) - Stepper("Goal", value: $distanceGoalInt, in: 1...100, step: 1) + Stepper("Goal") { + distanceGoalInt = min( + locale.usesMetricSystem ? 1000 : 600, + max(1, distanceGoalInt + 1) + ) + distanceGoal = String(distanceGoalInt) + } onDecrement: { + distanceGoalInt = min( + locale.usesMetricSystem ? 1000 : 600, + max(1, distanceGoalInt - 1) + ) + distanceGoal = String(distanceGoalInt) + } + .opacity(Int(distanceGoal) == nil ? 1 : 1) // Fixes stepper issue. } HStack { Spacer() - TextField("Amount", text: $distanceGoal) { _ in } onCommit: { - distanceGoalInt = Int(distanceGoal) ?? 10 - distanceGoalInt = distanceGoalInt == 0 ? 1 : distanceGoalInt + TextField("Amount", text: $distanceGoal, onEditingChanged: { _ in + distanceGoalInt = min( + locale.usesMetricSystem ? 1000 : 600, + max(1, Int(distanceGoal) ?? 10) + ) distanceGoal = String(distanceGoalInt) - } + }) .keyboardType(.numberPad) .multilineTextAlignment(.trailing) - Text("km") + Text(locale.usesMetricSystem ? "km" : "miles") .foregroundColor(.secondary) } } @@ -179,7 +201,7 @@ struct CreateCompetition: View { guard !competitionName.isEmpty else { return } presentationMode.wrappedValue.dismiss() } - .disabled(competitionName.isEmpty && Int(stepsGoal) == nil) + .disabled(competitionName.isEmpty) } .padding(.horizontal) .navigationBarTitle("Create Competition") diff --git a/Project SF/Views/Tabs/Competitions/PlaceBadgeView.swift b/Project SF/Views/Tabs/Competitions/PlaceBadgeView.swift new file mode 100644 index 0000000..fcd27be --- /dev/null +++ b/Project SF/Views/Tabs/Competitions/PlaceBadgeView.swift @@ -0,0 +1,94 @@ +// +// PlaceBadgeView.swift +// Project SF +// +// Created by Christian Privitelli on 13/7/20. +// + +import SwiftUI + +struct PlaceBadgeView: View { + let place: Int + let flippable: Bool + + @Binding var activityRings: ActivityRings + + let font: Font + let innerPadding: CGFloat + let outerPadding: CGFloat + + @State var flipped = false + + init(place: Int, flippable: Bool, + activityRings: Binding, font: Font = .title, + innerPadding: CGFloat = 24, outerPadding: CGFloat = 4) { + self.place = place + self.flippable = flippable + self._activityRings = activityRings + self.font = font + self.innerPadding = innerPadding + self.outerPadding = outerPadding + } + + var body: some View { + Text("\(place)\(place == 1 ? "st" : place == 2 ? "nd" : place == 3 ? "rd" : "th")") + .font(font) + .fontWeight(.black) + .padding(innerPadding) + .opacity(flipped ? 0 : 1) + .background( + Circle() + .foreground( + Group { + if !flipped { + place == 1 ? Color.yellow : + place == 2 ? Color(.lightGray) : + place == 3 ? Color.init(red: 0.6, green: 0.4, blue: 0.3) : + Color.accentColor + } else { + GeometryReader { geometry in + ZStack { + Color(.systemBackground) + ActivityRingsView( + values: $activityRings, + ringSize: RingSize( + size: geometry.size.height-20, + width: (geometry.size.height-20)*0.13, + padding: 2 + ) + ) + .rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0)) + } + } + } + } + ) + .padding(-4) + ) + .rotation3DEffect( + flipped ? .degrees(180) : .degrees(0), + axis: (x: 0, y: 1, z: 0), + anchor: .center + ) + .modifier(OptionalTapGesture(condition: flippable, action: { + withAnimation(.spring()) { + flipped.toggle() + } + })) + .shadow(color: Color.black.opacity(0.1), radius: 10) + } +} + +struct OptionalTapGesture: ViewModifier { + var condition: Bool + var action: () -> Void + + @ViewBuilder + func body(content: Content) -> some View { + if condition { + content.onTapGesture(perform: action) + } else { + content + } + } +} diff --git a/Project SF/Views/Tabs/Friends/FriendDetailView.swift b/Project SF/Views/Tabs/Friends/FriendDetailView.swift index 360f4c1..63e3b8b 100644 --- a/Project SF/Views/Tabs/Friends/FriendDetailView.swift +++ b/Project SF/Views/Tabs/Friends/FriendDetailView.swift @@ -14,9 +14,28 @@ struct FriendDetailView: View { let friend: Friend var competitions: [Competition] = [ - Competition(name: "Competition1", startDate: Date() - 100000, endDate: Date() + 100000), - Competition(name: "Competition2", startDate: Date() - 100000, endDate: Date() + 30000000), - Competition(name: "Competition3", startDate: Date() - 100000, endDate: Date() + 9900000) + Competition( + name: "Competition1", + startDate: Date() - 100000, + endDate: Date() + 100000, + creatingUser: CompetingPerson(name: "Me", points: 300), + people: [ + CompetingPerson(name: "Person1", points: 100), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 6000) + ] + ), + Competition( + name: "Competition2", + startDate: Date(), + endDate: Date() + 1000000, + creatingUser: CompetingPerson(name: "Me", points: 5500), + people: [ + CompetingPerson(name: "Person1", points: 5000), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 500) + ] + ) ] var body: some View { @@ -83,11 +102,7 @@ struct FriendDetailView: View { Section(header: Text("Current Competitions")) { ForEach(competitions.indices) { index in - CompetitionCell( - competitionName: competitions[index].name, - startDate: competitions[index].startDate, - endDate: competitions[index].endDate - ) + CompetitionCell(competitions[index]) } } From bb99e46bb630a2b346013e4074528d421fe8cad9 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Fri, 17 Jul 2020 19:20:46 +1000 Subject: [PATCH 12/17] Fix the merge --- Project SF.xcodeproj/project.pbxproj | 90 ++++++++++--------- ...wift => Int+ConvertFromRangeToRange.swift} | 0 .../Competitions/CompetitionDetailView.swift | 12 ++- 3 files changed, 59 insertions(+), 43 deletions(-) rename Project SF/Utilities/Extensions/{Int+ConvertFromRangeToRange copy.swift => Int+ConvertFromRangeToRange.swift} (100%) diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index c02c7a5..bfedcc9 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -7,14 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 3019CD9924BC9792002564AD /* PlaceBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */; }; - 30251C1924BFC1A50058D6D2 /* CompetitorDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */; }; - 30251C1B24BFC1B10058D6D2 /* CompetitorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */; }; - 30251C1E24BFE1D50058D6D2 /* PointsGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */; }; - 30278E9324BC553A00E87E80 /* CompetitionDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */; }; 302CF88A24BA199E00FF79D7 /* RingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302CF88924BA199E00FF79D7 /* RingType.swift */; }; 302CF88C24BA1A4000FF79D7 /* ActivityResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302CF88B24BA1A4000FF79D7 /* ActivityResult.swift */; }; - 3050123624C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */; }; + 3050123624C0887D00E39019 /* Int+ConvertFromRangeToRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange.swift */; }; 3067712F24BDE0080085F152 /* FriendsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3067712E24BDE0080085F152 /* FriendsCell.swift */; }; 3067713124BDE3950085F152 /* FriendDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3067713024BDE3950085F152 /* FriendDetailView.swift */; }; 307648C924BC7DC5005D8531 /* GrantDataAccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 307648C824BC7DC4005D8531 /* GrantDataAccessView.swift */; }; @@ -28,6 +23,15 @@ 30B2818A24C001A000647B83 /* Pixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818924C001A000647B83 /* Pixel.swift */; }; 30B2818C24C07F4700647B83 /* ProfileImageCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */; }; 30B2818E24C091A400647B83 /* Color+UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818D24C091A400647B83 /* Color+UIColor.swift */; }; + 30B2819924C1A41300647B83 /* PointsGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819024C1A41200647B83 /* PointsGraph.swift */; }; + 30B2819A24C1A41300647B83 /* CompetitorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819124C1A41200647B83 /* CompetitorCell.swift */; }; + 30B2819B24C1A41300647B83 /* CompetitionDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819224C1A41200647B83 /* CompetitionDetail.swift */; }; + 30B2819C24C1A41300647B83 /* CompetitorDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819324C1A41200647B83 /* CompetitorDetail.swift */; }; + 30B2819D24C1A41300647B83 /* CompetitionDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819424C1A41200647B83 /* CompetitionDetailView.swift */; }; + 30B2819E24C1A41300647B83 /* CompetitionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819524C1A41200647B83 /* CompetitionsView.swift */; }; + 30B2819F24C1A41300647B83 /* CreateCompetition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819624C1A41200647B83 /* CreateCompetition.swift */; }; + 30B281A024C1A41300647B83 /* CompetitionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819724C1A41300647B83 /* CompetitionCell.swift */; }; + 30B281A124C1A41300647B83 /* PlaceBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819824C1A41300647B83 /* PlaceBadgeView.swift */; }; 30BFC8CE24B75E6C00DAC6D9 /* ProjectSFApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */; }; 30BFC8D024B75E6C00DAC6D9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */; }; 30BFC8D224B75E6F00DAC6D9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30BFC8D124B75E6F00DAC6D9 /* Assets.xcassets */; }; @@ -39,9 +43,6 @@ 30CCF30724BED9EA00103C1E /* ActivityOverview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CCF30624BED9EA00103C1E /* ActivityOverview.swift */; }; 30D3217824BB430E009CD9D0 /* RoundedTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D3217724BB430E009CD9D0 /* RoundedTextField.swift */; }; 30D3217D24BB4EDE009CD9D0 /* ActivityRings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D3217C24BB4EDE009CD9D0 /* ActivityRings.swift */; }; - 30D321B524BB65D5009CD9D0 /* CompetitionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321A724BB65D5009CD9D0 /* CompetitionsView.swift */; }; - 30D321B624BB65D5009CD9D0 /* CompetitionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321A824BB65D5009CD9D0 /* CompetitionCell.swift */; }; - 30D321B724BB65D5009CD9D0 /* CreateCompetition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321A924BB65D5009CD9D0 /* CreateCompetition.swift */; }; 30D321B824BB65D5009CD9D0 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321AC24BB65D5009CD9D0 /* SettingsView.swift */; }; 30D321B924BB65D5009CD9D0 /* ImageSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321AD24BB65D5009CD9D0 /* ImageSelectionView.swift */; }; 30D321BA24BB65D5009CD9D0 /* NotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321AE24BB65D5009CD9D0 /* NotificationSettings.swift */; }; @@ -88,14 +89,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceBadgeView.swift; sourceTree = ""; }; - 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitorDetail.swift; sourceTree = ""; }; - 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitorCell.swift; sourceTree = ""; }; - 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointsGraph.swift; sourceTree = ""; }; - 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompetitionDetail.swift; sourceTree = ""; }; 302CF88924BA199E00FF79D7 /* RingType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RingType.swift; sourceTree = ""; }; 302CF88B24BA1A4000FF79D7 /* ActivityResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityResult.swift; sourceTree = ""; }; - 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+ConvertFromRangeToRange copy.swift"; sourceTree = ""; }; + 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+ConvertFromRangeToRange.swift"; sourceTree = ""; }; 3067712E24BDE0080085F152 /* FriendsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendsCell.swift; sourceTree = ""; }; 3067713024BDE3950085F152 /* FriendDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FriendDetailView.swift; sourceTree = ""; }; 307648C824BC7DC4005D8531 /* GrantDataAccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrantDataAccessView.swift; sourceTree = ""; }; @@ -109,6 +105,15 @@ 30B2818924C001A000647B83 /* Pixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pixel.swift; sourceTree = ""; }; 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImageCreator.swift; sourceTree = ""; }; 30B2818D24C091A400647B83 /* Color+UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+UIColor.swift"; sourceTree = ""; }; + 30B2819024C1A41200647B83 /* PointsGraph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointsGraph.swift; sourceTree = ""; }; + 30B2819124C1A41200647B83 /* CompetitorCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitorCell.swift; sourceTree = ""; }; + 30B2819224C1A41200647B83 /* CompetitionDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitionDetail.swift; sourceTree = ""; }; + 30B2819324C1A41200647B83 /* CompetitorDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitorDetail.swift; sourceTree = ""; }; + 30B2819424C1A41200647B83 /* CompetitionDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitionDetailView.swift; sourceTree = ""; }; + 30B2819524C1A41200647B83 /* CompetitionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitionsView.swift; sourceTree = ""; }; + 30B2819624C1A41200647B83 /* CreateCompetition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateCompetition.swift; sourceTree = ""; }; + 30B2819724C1A41300647B83 /* CompetitionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitionCell.swift; sourceTree = ""; }; + 30B2819824C1A41300647B83 /* PlaceBadgeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaceBadgeView.swift; sourceTree = ""; }; 30BFC8CA24B75E6C00DAC6D9 /* Project SF.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Project SF.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 30BFC8CD24B75E6C00DAC6D9 /* ProjectSFApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectSFApp.swift; sourceTree = ""; }; 30BFC8CF24B75E6C00DAC6D9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -123,9 +128,6 @@ 30CCF30624BED9EA00103C1E /* ActivityOverview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityOverview.swift; sourceTree = ""; }; 30D3217724BB430E009CD9D0 /* RoundedTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedTextField.swift; sourceTree = ""; }; 30D3217C24BB4EDE009CD9D0 /* ActivityRings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityRings.swift; sourceTree = ""; }; - 30D321A724BB65D5009CD9D0 /* CompetitionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitionsView.swift; sourceTree = ""; }; - 30D321A824BB65D5009CD9D0 /* CompetitionCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitionCell.swift; sourceTree = ""; }; - 30D321A924BB65D5009CD9D0 /* CreateCompetition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateCompetition.swift; sourceTree = ""; }; 30D321AC24BB65D5009CD9D0 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 30D321AD24BB65D5009CD9D0 /* ImageSelectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageSelectionView.swift; sourceTree = ""; }; 30D321AE24BB65D5009CD9D0 /* NotificationSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationSettings.swift; sourceTree = ""; }; @@ -186,17 +188,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 30251C1C24BFC1B80058D6D2 /* CompetitionDetail */ = { - isa = PBXGroup; - children = ( - 30278E9224BC553A00E87E80 /* CompetitionDetail.swift */, - 30251C1824BFC1A50058D6D2 /* CompetitorDetail.swift */, - 30251C1A24BFC1B10058D6D2 /* CompetitorCell.swift */, - 30251C1D24BFE1D50058D6D2 /* PointsGraph.swift */, - ); - path = CompetitionDetail; - sourceTree = ""; - }; 3029AF5024B8869E003F0324 /* Views */ = { isa = PBXGroup; children = ( @@ -218,7 +209,7 @@ isa = PBXGroup; children = ( 9C7B4D0724B89AF100FC4456 /* Double+ConvertFromRangeToRange.swift */, - 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift */, + 3050123524C0887D00E39019 /* Int+ConvertFromRangeToRange.swift */, 9C7B4D0524B89AC700FC4456 /* View+ForegroundModifier.swift */, 9CCFDD2324B9745E00162B0F /* UIApplication+IsTesting.swift */, 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */, @@ -249,6 +240,17 @@ path = Onboarding; sourceTree = ""; }; + 30B2818F24C1A41200647B83 /* CompetitionDetail */ = { + isa = PBXGroup; + children = ( + 30B2819024C1A41200647B83 /* PointsGraph.swift */, + 30B2819124C1A41200647B83 /* CompetitorCell.swift */, + 30B2819224C1A41200647B83 /* CompetitionDetail.swift */, + 30B2819324C1A41200647B83 /* CompetitorDetail.swift */, + ); + path = CompetitionDetail; + sourceTree = ""; + }; 30BFC8C124B75E6C00DAC6D9 = { isa = PBXGroup; children = ( @@ -332,11 +334,12 @@ 30D321A624BB65D5009CD9D0 /* Competitions */ = { isa = PBXGroup; children = ( - 30251C1C24BFC1B80058D6D2 /* CompetitionDetail */, - 30D321A724BB65D5009CD9D0 /* CompetitionsView.swift */, - 30D321A824BB65D5009CD9D0 /* CompetitionCell.swift */, - 30D321A924BB65D5009CD9D0 /* CreateCompetition.swift */, - 3019CD9824BC9792002564AD /* PlaceBadgeView.swift */, + 30B2819724C1A41300647B83 /* CompetitionCell.swift */, + 30B2818F24C1A41200647B83 /* CompetitionDetail */, + 30B2819424C1A41200647B83 /* CompetitionDetailView.swift */, + 30B2819524C1A41200647B83 /* CompetitionsView.swift */, + 30B2819624C1A41200647B83 /* CreateCompetition.swift */, + 30B2819824C1A41300647B83 /* PlaceBadgeView.swift */, ); path = Competitions; sourceTree = ""; @@ -587,7 +590,6 @@ buildActionMask = 2147483647; files = ( 308FAD9E24B9A0DC00126F3F /* NavigationBarLabel.swift in Sources */, - 30D321B524BB65D5009CD9D0 /* CompetitionsView.swift in Sources */, 9C7B4D0824B89AF100FC4456 /* Double+ConvertFromRangeToRange.swift in Sources */, 30BFC8D024B75E6C00DAC6D9 /* ContentView.swift in Sources */, 30FCC84C24BB2EC800862C01 /* ResignKeyboardOnDragGesture.swift in Sources */, @@ -597,14 +599,16 @@ 9CCFDD2624B9753F00162B0F /* Main.swift in Sources */, 30D321C124BC1886009CD9D0 /* NavScrollView.swift in Sources */, 30B2818824BFF2F600647B83 /* UIImage+PixelData.swift in Sources */, + 30B281A124C1A41300647B83 /* PlaceBadgeView.swift in Sources */, 30BFC8E924B804CC00DAC6D9 /* ActivityRingView.swift in Sources */, 3067712F24BDE0080085F152 /* FriendsCell.swift in Sources */, + 30B281A024C1A41300647B83 /* CompetitionCell.swift in Sources */, 30D321BD24BB65D5009CD9D0 /* ProfileSettings.swift in Sources */, 30BFC8DF24B7637F00DAC6D9 /* VisualEffectView.swift in Sources */, 9CCFDD1D24B971D600162B0F /* User.swift in Sources */, - 30D321B624BB65D5009CD9D0 /* CompetitionCell.swift in Sources */, 30D321BC24BB65D5009CD9D0 /* PrivacyAbout.swift in Sources */, - 30D321B724BB65D5009CD9D0 /* CreateCompetition.swift in Sources */, + 30B2819C24C1A41300647B83 /* CompetitorDetail.swift in Sources */, + 30B2819B24C1A41300647B83 /* CompetitionDetail.swift in Sources */, 308FAD8D24B97E8A00126F3F /* PrivacyView.swift in Sources */, 30D321BE24BB65D5009CD9D0 /* ProfileView.swift in Sources */, 308FAD8B24B97E7E00126F3F /* OnboardingInfoCell.swift in Sources */, @@ -616,27 +620,29 @@ 30B2818A24C001A000647B83 /* Pixel.swift in Sources */, 9C8FAD4624B9E39E00571947 /* Record.swift in Sources */, 30D321B924BB65D5009CD9D0 /* ImageSelectionView.swift in Sources */, + 30B2819F24C1A41300647B83 /* CreateCompetition.swift in Sources */, + 30B2819924C1A41300647B83 /* PointsGraph.swift in Sources */, 30D3217D24BB4EDE009CD9D0 /* ActivityRings.swift in Sources */, 307648C924BC7DC5005D8531 /* GrantDataAccessView.swift in Sources */, 30D321BF24BB65D5009CD9D0 /* FriendsView.swift in Sources */, 30B2818E24C091A400647B83 /* Color+UIColor.swift in Sources */, 302CF88A24BA199E00FF79D7 /* RingType.swift in Sources */, 9C7B4D0624B89AC700FC4456 /* View+ForegroundModifier.swift in Sources */, - 30251C1B24BFC1B10058D6D2 /* CompetitorCell.swift in Sources */, + 30B2819A24C1A41300647B83 /* CompetitorCell.swift in Sources */, 30CCF30724BED9EA00103C1E /* ActivityOverview.swift in Sources */, 30D321BA24BB65D5009CD9D0 /* NotificationSettings.swift in Sources */, - 30251C1E24BFE1D50058D6D2 /* PointsGraph.swift in Sources */, 30BFC8E724B8041C00DAC6D9 /* ActivityRingsView.swift in Sources */, 3090918224B9480F000E3B11 /* RoundedButton.swift in Sources */, 9CCFDD2424B9745E00162B0F /* UIApplication+IsTesting.swift in Sources */, 30B2818C24C07F4700647B83 /* ProfileImageCreator.swift in Sources */, + 30B2819D24C1A41300647B83 /* CompetitionDetailView.swift in Sources */, 9CCFDD2824B9758A00162B0F /* TestApp.swift in Sources */, + 30B2819E24C1A41300647B83 /* CompetitionsView.swift in Sources */, 302CF88C24BA1A4000FF79D7 /* ActivityResult.swift in Sources */, 3067713124BDE3950085F152 /* FriendDetailView.swift in Sources */, 9C6C74AC24BADD1800C657B0 /* RoundedNavigationButton.swift in Sources */, 30D321BB24BB65D5009CD9D0 /* PermissionSettings.swift in Sources */, - 3050123624C0887D00E39019 /* Int+ConvertFromRangeToRange copy.swift in Sources */, - 30278E9324BC553A00E87E80 /* CompetitionDetail.swift in Sources */, + 3050123624C0887D00E39019 /* Int+ConvertFromRangeToRange.swift in Sources */, 30D321B824BB65D5009CD9D0 /* SettingsView.swift in Sources */, 307648CB24BC81C8005D8531 /* iCloudErrorView.swift in Sources */, ); diff --git a/Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift b/Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange.swift similarity index 100% rename from Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange copy.swift rename to Project SF/Utilities/Extensions/Int+ConvertFromRangeToRange.swift diff --git a/Project SF/Views/Tabs/Competitions/CompetitionDetailView.swift b/Project SF/Views/Tabs/Competitions/CompetitionDetailView.swift index 7aa02ae..b8c21b6 100644 --- a/Project SF/Views/Tabs/Competitions/CompetitionDetailView.swift +++ b/Project SF/Views/Tabs/Competitions/CompetitionDetailView.swift @@ -18,6 +18,16 @@ struct CompetitionDetailView: View { struct CompetitionDetailView_Previews: PreviewProvider { static var previews: some View { - CompetitionDetailView(competition: Competition(name: "Competition Name", startDate: Date(), endDate: Date() + 123)) + CompetitionDetailView(competition: Competition( + name: "Competition2", + startDate: Date(), + endDate: Date() + 1000000, + creatingUser: CompetingPerson(name: "Me", points: 5500), + people: [ + CompetingPerson(name: "Person1", points: 5000), + CompetingPerson(name: "Person2", points: 200), + CompetingPerson(name: "Person3", points: 500) + ] + )) } } From 3ecdeee7e87836c17dc44d539068f5f72268b30d Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Fri, 17 Jul 2020 20:52:05 +1000 Subject: [PATCH 13/17] Added dynamic color change. --- Project SF/Models/Pixel.swift | 8 ++++---- .../Views/Tabs/Profile/Settings/ProfileImageCreator.swift | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Project SF/Models/Pixel.swift b/Project SF/Models/Pixel.swift index 4d9aaa4..1dad581 100644 --- a/Project SF/Models/Pixel.swift +++ b/Project SF/Models/Pixel.swift @@ -38,10 +38,10 @@ class PixelImage { /// - Returns: Random symmetrixal `PixelImage` with given size. static func randomSymmetrical(color: Color, width: Int, height: Int) -> PixelImage { var pixels: [Pixel] = [] - let clear = Pixel(a: 255, - r: 240, - g: 240, - b: 240) + let clear = Pixel(a: 0, + r: 0, + g: 0, + b: 0) let uiColor = color.uiColor() var a: CGFloat = 0 diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift index ea031fe..85b7b56 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift @@ -19,20 +19,20 @@ struct ProfileImageCreator: View { if let image = image { Image(uiImage: image) .interpolation(.none) - .renderingMode(.original) + .renderingMode(.template) .resizable() .aspectRatio(contentMode: .fill) .frame(width: 200, height: 200) .clipShape(Circle()) .animation(.spring()) + .foregroundColor(color) - // TODO: Dynamically change color of the image ColorPicker("Select foreground color", selection: $color, supportsOpacity: false) .padding(.top) } RoundedButton("Generate image") { withAnimation { - image = UIImage(pixelImage: .randomSymmetrical(color: color, width: 7, height: 7)) + image = UIImage(pixelImage: .randomSymmetrical(color: .white, width: 7, height: 7)) } } .padding(.top) From ab5db6796cf0a7fc0e17ad0039edfaa965182e20 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Sat, 18 Jul 2020 12:14:33 +1000 Subject: [PATCH 14/17] Updated image presentation and color changing. --- .../Tabs/Competitions/CompetitionCell.swift | 2 +- .../Settings/ProfileImageCreator.swift | 18 ++++++++--- .../Profile/Settings/ProfileSettings.swift | 32 ++++++++++++++----- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/Project SF/Views/Tabs/Competitions/CompetitionCell.swift b/Project SF/Views/Tabs/Competitions/CompetitionCell.swift index 0713c3c..4786f54 100644 --- a/Project SF/Views/Tabs/Competitions/CompetitionCell.swift +++ b/Project SF/Views/Tabs/Competitions/CompetitionCell.swift @@ -62,7 +62,7 @@ struct CompetitionCell: View { struct CompetitionCell_Previews: PreviewProvider { static var previews: some View { - var competitions: [Competition] = [ + let competitions: [Competition] = [ Competition( name: "Competition1", startDate: Date() - 100000, diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift index 85b7b56..8be31b8 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift @@ -11,7 +11,7 @@ struct ProfileImageCreator: View { @Environment(\.presentationMode) var presentationMode @Binding var image: UIImage? - @State var color = Color.accentColor + @Binding var color: Color var body: some View { NavigationView { @@ -24,7 +24,6 @@ struct ProfileImageCreator: View { .aspectRatio(contentMode: .fill) .frame(width: 200, height: 200) .clipShape(Circle()) - .animation(.spring()) .foregroundColor(color) ColorPicker("Select foreground color", selection: $color, supportsOpacity: false) @@ -40,15 +39,26 @@ struct ProfileImageCreator: View { .padding(.horizontal) .navigationTitle("Profile Image Creator") } + .onDisappear { + print(321) + } } - init(_ image: Binding) { + init(_ image: Binding, color: Binding) { _image = image + _color = color + if image.wrappedValue != nil, color.wrappedValue == .clear { + DispatchQueue.main.async { + image.wrappedValue = nil + color.wrappedValue = .black + } + } } } struct ProfileImageCreator_Previews: PreviewProvider { static var previews: some View { - ProfileImageCreator(.constant(UIImage(pixelImage: .randomSymmetrical(color: .red, width: 10, height: 10)))) + ProfileImageCreator(.constant(UIImage(pixelImage: .randomSymmetrical(color: .red, width: 10, height: 10))), + color: .constant(Color.red)) } } diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift index 1ba96f3..83f628a 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileSettings.swift @@ -16,6 +16,7 @@ struct ProfileSettings: View { @StateObject var keyboard = KeyboardManager() @State var profilePicture: UIImage? + @State var color = Color.clear @State var showSheet = false @State var selectedSheet = 0 @@ -35,13 +36,23 @@ struct ProfileSettings: View { .aspectRatio(contentMode: .fit) .frame(width: 100, height: 100) } else { - Image(uiImage: profilePicture!) - .interpolation(.none) - .renderingMode(.original) - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 100, height: 100) - .clipShape(Circle()) + if color != .clear { + Image(uiImage: profilePicture!) + .interpolation(.none) + .renderingMode(.template) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 100, height: 100) + .clipShape(Circle()) + .foregroundColor(color) + } else { + Image(uiImage: profilePicture!) + .renderingMode(.original) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 100, height: 100) + .clipShape(Circle()) + } } } @@ -102,9 +113,14 @@ struct ProfileSettings: View { showSheet = true }), .cancel()]) } + .onChange(of: showSheet, perform: { showSheet in + if !showSheet && selectedSheet == 1 { + color = .clear + } + }) .sheet(isPresented: $showSheet) { if selectedSheet == 0 { - ProfileImageCreator($profilePicture) + ProfileImageCreator($profilePicture, color: $color) } else { ImageSelectionView(image: $profilePicture) } From 06494d4ff276045fa4e9d1237f6174613078bd15 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Sat, 18 Jul 2020 12:45:37 +1000 Subject: [PATCH 15/17] Update Init and color changing. --- .../Settings/ProfileImageCreator.swift | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift index 8be31b8..e2accf8 100644 --- a/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift +++ b/Project SF/Views/Tabs/Profile/Settings/ProfileImageCreator.swift @@ -26,8 +26,10 @@ struct ProfileImageCreator: View { .clipShape(Circle()) .foregroundColor(color) - ColorPicker("Select foreground color", selection: $color, supportsOpacity: false) - .padding(.top) + GroupBox { + ColorPicker("Select foreground color", selection: $color, supportsOpacity: false) + } + .padding(.top) } RoundedButton("Generate image") { withAnimation { @@ -40,19 +42,25 @@ struct ProfileImageCreator: View { .navigationTitle("Profile Image Creator") } .onDisappear { - print(321) + if image == nil { + color = .clear + } } } init(_ image: Binding, color: Binding) { - _image = image - _color = color - if image.wrappedValue != nil, color.wrappedValue == .clear { - DispatchQueue.main.async { - image.wrappedValue = nil + DispatchQueue.main.async { + var shouldRemoveImage = false + if color.wrappedValue == .clear { color.wrappedValue = .black + shouldRemoveImage = true + } + if image.wrappedValue != nil && shouldRemoveImage { + image.wrappedValue = nil } } + _image = image + _color = color } } From e0b91335653dcf7e61b82b76d45817b662940e2f Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Sat, 18 Jul 2020 13:01:35 +1000 Subject: [PATCH 16/17] Remove UIColor extension. --- Project SF.xcodeproj/project.pbxproj | 4 -- Project SF/Models/Pixel.swift | 2 +- .../Utilities/Extensions/Color+UIColor.swift | 39 ------------------- 3 files changed, 1 insertion(+), 44 deletions(-) delete mode 100644 Project SF/Utilities/Extensions/Color+UIColor.swift diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index bfedcc9..228bc0f 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -22,7 +22,6 @@ 30B2818824BFF2F600647B83 /* UIImage+PixelData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */; }; 30B2818A24C001A000647B83 /* Pixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818924C001A000647B83 /* Pixel.swift */; }; 30B2818C24C07F4700647B83 /* ProfileImageCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */; }; - 30B2818E24C091A400647B83 /* Color+UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2818D24C091A400647B83 /* Color+UIColor.swift */; }; 30B2819924C1A41300647B83 /* PointsGraph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819024C1A41200647B83 /* PointsGraph.swift */; }; 30B2819A24C1A41300647B83 /* CompetitorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819124C1A41200647B83 /* CompetitorCell.swift */; }; 30B2819B24C1A41300647B83 /* CompetitionDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B2819224C1A41200647B83 /* CompetitionDetail.swift */; }; @@ -104,7 +103,6 @@ 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+PixelData.swift"; sourceTree = ""; }; 30B2818924C001A000647B83 /* Pixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pixel.swift; sourceTree = ""; }; 30B2818B24C07F4700647B83 /* ProfileImageCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImageCreator.swift; sourceTree = ""; }; - 30B2818D24C091A400647B83 /* Color+UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+UIColor.swift"; sourceTree = ""; }; 30B2819024C1A41200647B83 /* PointsGraph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointsGraph.swift; sourceTree = ""; }; 30B2819124C1A41200647B83 /* CompetitorCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitorCell.swift; sourceTree = ""; }; 30B2819224C1A41200647B83 /* CompetitionDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompetitionDetail.swift; sourceTree = ""; }; @@ -213,7 +211,6 @@ 9C7B4D0524B89AC700FC4456 /* View+ForegroundModifier.swift */, 9CCFDD2324B9745E00162B0F /* UIApplication+IsTesting.swift */, 30B2818724BFF2F600647B83 /* UIImage+PixelData.swift */, - 30B2818D24C091A400647B83 /* Color+UIColor.swift */, ); path = Extensions; sourceTree = ""; @@ -625,7 +622,6 @@ 30D3217D24BB4EDE009CD9D0 /* ActivityRings.swift in Sources */, 307648C924BC7DC5005D8531 /* GrantDataAccessView.swift in Sources */, 30D321BF24BB65D5009CD9D0 /* FriendsView.swift in Sources */, - 30B2818E24C091A400647B83 /* Color+UIColor.swift in Sources */, 302CF88A24BA199E00FF79D7 /* RingType.swift in Sources */, 9C7B4D0624B89AC700FC4456 /* View+ForegroundModifier.swift in Sources */, 30B2819A24C1A41300647B83 /* CompetitorCell.swift in Sources */, diff --git a/Project SF/Models/Pixel.swift b/Project SF/Models/Pixel.swift index 1dad581..547529a 100644 --- a/Project SF/Models/Pixel.swift +++ b/Project SF/Models/Pixel.swift @@ -43,7 +43,7 @@ class PixelImage { g: 0, b: 0) - let uiColor = color.uiColor() + let uiColor = UIColor(color) var a: CGFloat = 0 var r: CGFloat = 0 var g: CGFloat = 0 diff --git a/Project SF/Utilities/Extensions/Color+UIColor.swift b/Project SF/Utilities/Extensions/Color+UIColor.swift deleted file mode 100644 index a1de7ef..0000000 --- a/Project SF/Utilities/Extensions/Color+UIColor.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// Color+UIColor.swift -// Project SF -// -// Created by Roman Esin on 16.07.2020. -// - -import SwiftUI - -// Reference: https://stackoverflow.com/a/58531033/10616784 -extension Color { - - func uiColor() -> UIColor { - - let components = self.components() - return UIColor(red: components.r, green: components.g, blue: components.b, alpha: components.a) - } - - // swiftlint:disable large_tuple - // swiftlint:disable identifier_name - private func components() -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) { - - let scanner = Scanner(string: self.description.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)) - var hexNumber: UInt64 = 0 - var r: CGFloat = 0.0 - var g: CGFloat = 0.0 - var b: CGFloat = 0.0 - var a: CGFloat = 0.0 - - let result = scanner.scanHexInt64(&hexNumber) - if result { - r = CGFloat((hexNumber & 0xff000000) >> 24) / 255 - g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255 - b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255 - a = CGFloat(hexNumber & 0x000000ff) / 255 - } - return (r, g, b, a) - } -} From 8b6341a6991452ca0dcd76a3b5df4de9b1776563 Mon Sep 17 00:00:00 2001 From: Roman Esin Date: Sat, 18 Jul 2020 18:35:39 +1000 Subject: [PATCH 17/17] Create ImageStore. --- Project SF.xcodeproj/project.pbxproj | 4 ++++ Project SF/Models/ImageStore.swift | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 Project SF/Models/ImageStore.swift diff --git a/Project SF.xcodeproj/project.pbxproj b/Project SF.xcodeproj/project.pbxproj index 228bc0f..9c4f438 100644 --- a/Project SF.xcodeproj/project.pbxproj +++ b/Project SF.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 30D321BE24BB65D5009CD9D0 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321B224BB65D5009CD9D0 /* ProfileView.swift */; }; 30D321BF24BB65D5009CD9D0 /* FriendsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321B424BB65D5009CD9D0 /* FriendsView.swift */; }; 30D321C124BC1886009CD9D0 /* NavScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D321C024BC1886009CD9D0 /* NavScrollView.swift */; }; + 30EAB9FB24C2A234005326C8 /* ImageStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30EAB9FA24C2A234005326C8 /* ImageStore.swift */; }; 30F1EA3B24B951CA00FF89FC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30F1EA3A24B951CA00FF89FC /* OnboardingView.swift */; }; 30FCC84C24BB2EC800862C01 /* ResignKeyboardOnDragGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FCC84B24BB2EC800862C01 /* ResignKeyboardOnDragGesture.swift */; }; 30FCC84E24BB318000862C01 /* KeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FCC84D24BB318000862C01 /* KeyboardManager.swift */; }; @@ -135,6 +136,7 @@ 30D321B224BB65D5009CD9D0 /* ProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; 30D321B424BB65D5009CD9D0 /* FriendsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FriendsView.swift; sourceTree = ""; }; 30D321C024BC1886009CD9D0 /* NavScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavScrollView.swift; sourceTree = ""; }; + 30EAB9FA24C2A234005326C8 /* ImageStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageStore.swift; sourceTree = ""; }; 30F1EA3A24B951CA00FF89FC /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; 30FABF2B24BB5423004B8B62 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = ""; }; 30FCC84B24BB2EC800862C01 /* ResignKeyboardOnDragGesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignKeyboardOnDragGesture.swift; sourceTree = ""; }; @@ -416,6 +418,7 @@ 302CF88B24BA1A4000FF79D7 /* ActivityResult.swift */, 30D3217C24BB4EDE009CD9D0 /* ActivityRings.swift */, 30B2818924C001A000647B83 /* Pixel.swift */, + 30EAB9FA24C2A234005326C8 /* ImageStore.swift */, ); path = Models; sourceTree = ""; @@ -605,6 +608,7 @@ 9CCFDD1D24B971D600162B0F /* User.swift in Sources */, 30D321BC24BB65D5009CD9D0 /* PrivacyAbout.swift in Sources */, 30B2819C24C1A41300647B83 /* CompetitorDetail.swift in Sources */, + 30EAB9FB24C2A234005326C8 /* ImageStore.swift in Sources */, 30B2819B24C1A41300647B83 /* CompetitionDetail.swift in Sources */, 308FAD8D24B97E8A00126F3F /* PrivacyView.swift in Sources */, 30D321BE24BB65D5009CD9D0 /* ProfileView.swift in Sources */, diff --git a/Project SF/Models/ImageStore.swift b/Project SF/Models/ImageStore.swift new file mode 100644 index 0000000..6612b5a --- /dev/null +++ b/Project SF/Models/ImageStore.swift @@ -0,0 +1,28 @@ +// +// ImageStore.swift +// Project SF +// +// Created by Roman Esin on 18.07.2020. +// + +import UIKit + +// TODO: Finish this thing... +@propertyWrapper +struct ImageStore { + var key: String + var storage = UserDefaults.standard + + var wrappedValue: UIImage? { + get { + guard let data = storage.value(forKey: key) as? Data else { return nil } + return UIImage(data: data) + } + set { storage.setValue(newValue?.pngData(), forKey: key) } + } + + init(_ key: String, wrappedValue: UIImage) { + self.key = key + self.wrappedValue = wrappedValue + } +}