From d4d7728c1995e11a1df205b7ae2ffc8b33a8e3aa Mon Sep 17 00:00:00 2001 From: Felix Mau Date: Thu, 11 Jun 2020 18:02:13 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20::=20Add=20`NotchGradientLoadingBar?= =?UTF-8?q?`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SafeAreaExampleViewController.swift | 2 +- Example/Pods/Pods.xcodeproj/project.pbxproj | 35 ++--- .../GradientLoadingBarController.swift | 36 ++--- .../Classes/Misc/Constants.swift | 4 +- .../Views/GradientActivityIndicatorView.swift | 8 +- NotchGradientLoadingBarController.swift | 125 ++++++++++++++++++ 6 files changed, 170 insertions(+), 40 deletions(-) create mode 100644 NotchGradientLoadingBarController.swift diff --git a/Example/GradientLoadingBar/SafeAreaExampleViewController.swift b/Example/GradientLoadingBar/SafeAreaExampleViewController.swift index 0396540..e63e507 100644 --- a/Example/GradientLoadingBar/SafeAreaExampleViewController.swift +++ b/Example/GradientLoadingBar/SafeAreaExampleViewController.swift @@ -12,7 +12,7 @@ import GradientLoadingBar class SafeAreaExampleViewController: UIViewController { // MARK: - Private properties - private let safeAreaGradientLoadingBar = GradientLoadingBar(isRelativeToSafeArea: false) + private let safeAreaGradientLoadingBar = NotchGradientLoadingBar(height: 6) // MARK: - Public methods diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index dfdf9a0..ed8d3d7 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ dependencies = ( ); name = SwiftFormat; + productName = SwiftFormat; }; 52B60EC2A583F24ACBB69C113F5488B9 /* SwiftLint */ = { isa = PBXAggregateTarget; @@ -24,6 +25,7 @@ dependencies = ( ); name = SwiftLint; + productName = SwiftLint; }; /* End PBXAggregateTarget section */ @@ -80,6 +82,7 @@ CE8387F85FA7B1C55704E5BDD58C9F17 /* GradientLoadingBarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D40940A239793530B056F5C6D42CBC /* GradientLoadingBarViewModel.swift */; }; D56563764E24224FB95BC77EC18F7D9E /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FE4FE8F00C724C9593C6DEFB4CE2286 /* String.swift */; }; D6A43851802E3E81CF23D65CA9A81E4E /* SnapshotTesting-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = D1B84171C66B510644D7EFFDC5B3E475 /* SnapshotTesting-dummy.m */; }; + DC190F5724925D170069E2B0 /* NotchGradientLoadingBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC190F5624925D170069E2B0 /* NotchGradientLoadingBarController.swift */; }; DC82754FF0AA19CEEF707C55CBB24008 /* GradientActivityIndicatorViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 288446056D0E6A131D9245C47AC68D44 /* GradientActivityIndicatorViewModel.swift */; }; E8945D07A466F2FA6A965D3AA13F41EF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E59CE2F9522AE21F97AA29799051F00E /* Foundation.framework */; }; E964CE43D2624DD003460825556D1101 /* LightweightObservable-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3CE49E78400E3A6E986194452C2A1611 /* LightweightObservable-dummy.m */; }; @@ -154,9 +157,9 @@ 0965FCF94C6751E5F873995C0CC05032 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 108AACC96F7DBC089B0840E09F19C698 /* Pods-GradientLoadingBar_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GradientLoadingBar_Example.release.xcconfig"; sourceTree = ""; }; 132C18988D60EF85736C043D43C19E57 /* Pods-GradientLoadingBar_Tests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-GradientLoadingBar_Tests-umbrella.h"; sourceTree = ""; }; - 13520AB8B3790CE3CEDCBBEEE1EF237A /* Pods_GradientLoadingBar_SnapshotTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_GradientLoadingBar_SnapshotTests.framework; path = "Pods-GradientLoadingBar_SnapshotTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 13520AB8B3790CE3CEDCBBEEE1EF237A /* Pods_GradientLoadingBar_SnapshotTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GradientLoadingBar_SnapshotTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1679A4CF92D4ADFB15F8200201EC3AE5 /* Pods-GradientLoadingBar_SnapshotTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-GradientLoadingBar_SnapshotTests-frameworks.sh"; sourceTree = ""; }; - 1711FC18263D47D46851F0433A93F110 /* Pods_GradientLoadingBar_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_GradientLoadingBar_Tests.framework; path = "Pods-GradientLoadingBar_Tests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1711FC18263D47D46851F0433A93F110 /* Pods_GradientLoadingBar_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GradientLoadingBar_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 172DF8516EF6D5757C3FBEF1C0CB52DC /* Pods-GradientLoadingBar_SnapshotTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-GradientLoadingBar_SnapshotTests-dummy.m"; sourceTree = ""; }; 1A9107CAA27BA0679345F24910903133 /* Pods-GradientLoadingBar_Tests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-GradientLoadingBar_Tests-dummy.m"; sourceTree = ""; }; 1B664F0A7F5E59140F761C16069995E9 /* Pods-GradientLoadingBar_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GradientLoadingBar_Tests.release.xcconfig"; sourceTree = ""; }; @@ -170,13 +173,13 @@ 382426057DD9B8AC16E9C8CBC1B1DE3E /* GradientActivityIndicatorView+AnimateIsHidden.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = "GradientActivityIndicatorView+AnimateIsHidden.swift"; sourceTree = ""; }; 3A900E1CFF1D76CF61DDF7358F5C8863 /* Pods-GradientLoadingBar_Example-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-GradientLoadingBar_Example-umbrella.h"; sourceTree = ""; }; 3B644D77B6DA2AE767C93222955C0195 /* SnapshotTesting.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SnapshotTesting.release.xcconfig; sourceTree = ""; }; - 3C6A379C135C84644F189B8056CCBA04 /* Pods_GradientLoadingBar_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_GradientLoadingBar_Example.framework; path = "Pods-GradientLoadingBar_Example.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3C6A379C135C84644F189B8056CCBA04 /* Pods_GradientLoadingBar_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GradientLoadingBar_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3CE49E78400E3A6E986194452C2A1611 /* LightweightObservable-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "LightweightObservable-dummy.m"; sourceTree = ""; }; 4371A74F9A6D74628527A30E755EFF25 /* Pods-GradientLoadingBar_SnapshotTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-GradientLoadingBar_SnapshotTests-umbrella.h"; sourceTree = ""; }; 4748F84A87672B2B8A2332EBFAB30E20 /* UIImage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = UIImage.swift; path = Sources/SnapshotTesting/Snapshotting/UIImage.swift; sourceTree = ""; }; 47AF808C977A1F54C41301EE10EFED69 /* SwiftFormat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftFormat.release.xcconfig; sourceTree = ""; }; 485FFC9380094A5DB42D7366AEBF4780 /* Diffing.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Diffing.swift; path = Sources/SnapshotTesting/Diffing.swift; sourceTree = ""; }; - 509A0CDAB5E258F92F9E7FA978AA781F /* readme.md */ = {isa = PBXFileReference; includeInIndex = 1; path = readme.md; sourceTree = ""; }; + 509A0CDAB5E258F92F9E7FA978AA781F /* readme.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; path = readme.md; sourceTree = ""; }; 51265E01B15BDF30B0C2CBDF6A151BC4 /* LightweightObservable.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = LightweightObservable.debug.xcconfig; sourceTree = ""; }; 5E0ED20F03F3062DB47BF2E306FF9049 /* LightweightObservable-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "LightweightObservable-Info.plist"; sourceTree = ""; }; 607A4044C0C5EAADEE148DC31AF9667B /* GradientLoadingBar-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GradientLoadingBar-prefix.pch"; sourceTree = ""; }; @@ -187,7 +190,7 @@ 655D196DB461A65E1D8D6C38F3ACF62D /* URLRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = URLRequest.swift; path = Sources/SnapshotTesting/Snapshotting/URLRequest.swift; sourceTree = ""; }; 66278BB4526795EDC4B59B5F6E94042F /* GradientLoadingBar-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "GradientLoadingBar-Info.plist"; sourceTree = ""; }; 66C7044ABF6219E06138EBA038827408 /* Description.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Description.swift; path = Sources/SnapshotTesting/Snapshotting/Description.swift; sourceTree = ""; }; - 67873E92210DD4E777C49C6E8AEDC108 /* GradientLoadingBar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = GradientLoadingBar.framework; path = GradientLoadingBar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 67873E92210DD4E777C49C6E8AEDC108 /* GradientLoadingBar.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GradientLoadingBar.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69838A921E83418B945E72EB342809F5 /* Pods-GradientLoadingBar_SnapshotTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GradientLoadingBar_SnapshotTests.debug.xcconfig"; sourceTree = ""; }; 6A0AC5F9D0359F62D9B6CFA7E28C270C /* Snapshotting.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Snapshotting.swift; path = Sources/SnapshotTesting/Snapshotting.swift; sourceTree = ""; }; 6A285D3D7A75E50B304EC4E2967172F4 /* GradientLoadingBarController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GradientLoadingBarController.swift; path = GradientLoadingBar/Classes/GradientLoadingBarController.swift; sourceTree = ""; }; @@ -203,12 +206,12 @@ 8907BBB8111B9A50687EB4B5E9E41A95 /* GradientLoadingBar-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "GradientLoadingBar-umbrella.h"; sourceTree = ""; }; 93233819B16746A4C1D1A32DFCEC1752 /* LightweightObservable-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "LightweightObservable-prefix.pch"; sourceTree = ""; }; 934AB585A2B4F9B908F37869A81F2EEC /* GradientLoadingBar.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = GradientLoadingBar.modulemap; sourceTree = ""; }; - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 9DD32929B122D2561D1E99468EECDBF7 /* Pods-GradientLoadingBar_SnapshotTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GradientLoadingBar_SnapshotTests.release.xcconfig"; sourceTree = ""; }; 9E46234BCB7A6CF5AE3ECC8C30C3786B /* AssertSnapshot.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AssertSnapshot.swift; path = Sources/SnapshotTesting/AssertSnapshot.swift; sourceTree = ""; }; 9EFE8B971DF98A48094E8121DA5F22BE /* LightweightObservable.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = LightweightObservable.release.xcconfig; sourceTree = ""; }; 9FAFAFBF669302034A4D6BEDD683F06D /* CALayer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CALayer.swift; path = Sources/SnapshotTesting/Snapshotting/CALayer.swift; sourceTree = ""; }; - A037DBBCCACBFACB5C39A80B1C273043 /* SnapshotTesting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SnapshotTesting.framework; path = SnapshotTesting.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A037DBBCCACBFACB5C39A80B1C273043 /* SnapshotTesting.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SnapshotTesting.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A1224C54074A146D75B2762DEE7B60CE /* SnapshotTestCase.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SnapshotTestCase.swift; path = Sources/SnapshotTesting/SnapshotTestCase.swift; sourceTree = ""; }; A2BEFE77478CF65195FEA6A6C36027C2 /* SwiftLint.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftLint.release.xcconfig; sourceTree = ""; }; A652F20CE7EE44E64565C2120B651665 /* Pods-GradientLoadingBar_SnapshotTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-GradientLoadingBar_SnapshotTests.modulemap"; sourceTree = ""; }; @@ -228,11 +231,12 @@ C478F41E1D29E73C340C46510C963758 /* SnapshotTesting.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SnapshotTesting.modulemap; sourceTree = ""; }; C4D40940A239793530B056F5C6D42CBC /* GradientLoadingBarViewModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GradientLoadingBarViewModel.swift; sourceTree = ""; }; CC084E9470B1610C5C104460C3E9672F /* GradientActivityIndicatorView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GradientActivityIndicatorView.swift; sourceTree = ""; }; - CC8524E3DB1DECB19CE8E799B2372568 /* GradientLoadingBar.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = GradientLoadingBar.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + CC8524E3DB1DECB19CE8E799B2372568 /* GradientLoadingBar.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; path = GradientLoadingBar.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; CEE3F100C4CDD54473EA2C6C753CE3F8 /* Observable+KeyPath.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Observable+KeyPath.swift"; path = "LightweightObservable/Classes/Extensions/Observable+KeyPath.swift"; sourceTree = ""; }; D1B84171C66B510644D7EFFDC5B3E475 /* SnapshotTesting-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SnapshotTesting-dummy.m"; sourceTree = ""; }; D20C54CBF964A3FE43CE7FE20719F6AD /* SwiftLint.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftLint.debug.xcconfig; sourceTree = ""; }; D5B2A1C4D6360C6D91F1483048511530 /* LightweightObservable-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "LightweightObservable-umbrella.h"; sourceTree = ""; }; + DC190F5624925D170069E2B0 /* NotchGradientLoadingBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotchGradientLoadingBarController.swift; sourceTree = ""; }; DC1FD5A53165BF4293473D170AD9995E /* View.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = View.swift; path = Sources/SnapshotTesting/Common/View.swift; sourceTree = ""; }; E06A1816AB01D090FDC9EEC02261A14E /* SnapshotTesting-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "SnapshotTesting-Info.plist"; sourceTree = ""; }; E0D90990792D653F523A884442305989 /* Pods-GradientLoadingBar_Example.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-GradientLoadingBar_Example.modulemap"; sourceTree = ""; }; @@ -243,13 +247,13 @@ E5F5BFF05569BBDD6EF0224F0E21090A /* CaseIterable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CaseIterable.swift; path = Sources/SnapshotTesting/Snapshotting/CaseIterable.swift; sourceTree = ""; }; E9C3128EB6965EA592CD63B38170D57B /* Async.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Async.swift; path = Sources/SnapshotTesting/Async.swift; sourceTree = ""; }; ED80020D57E60A2BDBE6BE1EB9333D0A /* SwiftFormat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftFormat.debug.xcconfig; sourceTree = ""; }; - F196AAA163469B17170E99B82C7C4C0A /* LightweightObservable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = LightweightObservable.framework; path = LightweightObservable.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F196AAA163469B17170E99B82C7C4C0A /* LightweightObservable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LightweightObservable.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F2B4FFBC82EA6CAE84B65AE1835AF25A /* SnapshotTesting.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SnapshotTesting.debug.xcconfig; sourceTree = ""; }; F2D95A77154276FCFD49025721CF9191 /* Observable+Equatable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Observable+Equatable.swift"; path = "LightweightObservable/Classes/Extensions/Observable+Equatable.swift"; sourceTree = ""; }; F7B1750DE470F045803DE2094D5F7264 /* Pods-GradientLoadingBar_SnapshotTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-GradientLoadingBar_SnapshotTests-Info.plist"; sourceTree = ""; }; F7FAEB3ACD1AD90617D11C22CECBCFF2 /* GradientLoadingBar.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = GradientLoadingBar.release.xcconfig; sourceTree = ""; }; F952B84395F604678559569DA34F74D2 /* Wait.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Wait.swift; path = Sources/SnapshotTesting/Extensions/Wait.swift; sourceTree = ""; }; - FB13F66A7D40AC5D9D870CB621D9D898 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE; sourceTree = ""; }; + FB13F66A7D40AC5D9D870CB621D9D898 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; FBAEAE91BB9B77006558E537894C33C5 /* Pods-GradientLoadingBar_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GradientLoadingBar_Tests.debug.xcconfig"; sourceTree = ""; }; FF135E1D23F08BE7F1D7FFC86260EF9B /* Pods-GradientLoadingBar_SnapshotTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-GradientLoadingBar_SnapshotTests-acknowledgements.markdown"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -371,7 +375,6 @@ CEE3F100C4CDD54473EA2C6C753CE3F8 /* Observable+KeyPath.swift */, 7A7C6C3CD90BB6F1A79FCFA9E9F6C9F5 /* Support Files */, ); - name = LightweightObservable; path = LightweightObservable; sourceTree = ""; }; @@ -380,7 +383,6 @@ children = ( E2CCD1A55C9A485E976F71F7C7CF3366 /* Support Files */, ); - name = SwiftLint; path = SwiftLint; sourceTree = ""; }; @@ -504,7 +506,6 @@ A6C45FD2B6DBF67E9CA08571D980F9C7 /* XCTAttachment.swift */, 434289011F6712E755970F1074781EA5 /* Support Files */, ); - name = SnapshotTesting; path = SnapshotTesting; sourceTree = ""; }; @@ -520,12 +521,13 @@ C30CBA6370532ED04361FF0E39E0D799 /* GradientLoadingBar */ = { isa = PBXGroup; children = ( - 6A285D3D7A75E50B304EC4E2967172F4 /* GradientLoadingBarController.swift */, B40EF345738F3DA667C21CF3F0DF5538 /* Misc */, A5CCEF189421647B1B00BB017CF80F7D /* Pod */, C9A709FCBCBFE549177C810EAFC278F3 /* Support Files */, B25194A474F7FB1B19DF392B63D633A0 /* ViewModel */, C8FA2FFE987E4CAE66EF6455DF3F969C /* Views */, + 6A285D3D7A75E50B304EC4E2967172F4 /* GradientLoadingBarController.swift */, + DC190F5624925D170069E2B0 /* NotchGradientLoadingBarController.swift */, ); name = GradientLoadingBar; path = ../..; @@ -583,7 +585,6 @@ children = ( 2CE89AADFFFE95E8003FB250177B1CD1 /* Support Files */, ); - name = SwiftFormat; path = SwiftFormat; sourceTree = ""; }; @@ -902,6 +903,7 @@ 72E7D09F895BFB276F79212661207A3E /* Constants.swift in Sources */, 1AB5DB926319ED83D108A7029C3CE863 /* GradientActivityIndicatorView+AnimateIsHidden.swift in Sources */, 845E53EF69DD891F41769CBFAD775C5C /* GradientActivityIndicatorView.swift in Sources */, + DC190F5724925D170069E2B0 /* NotchGradientLoadingBarController.swift in Sources */, DC82754FF0AA19CEEF707C55CBB24008 /* GradientActivityIndicatorViewModel.swift in Sources */, 9BF500319938CCD42F882041380B4A4E /* GradientLoadingBar-dummy.m in Sources */, EA1F3BF177FD4A38CBE9B67849F9DAC4 /* GradientLoadingBarController.swift in Sources */, @@ -1133,8 +1135,7 @@ MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; STRIP_INSTALLED_PRODUCT = NO; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_VERSION = 5.0; SYMROOT = "${SRCROOT}/../build"; }; diff --git a/GradientLoadingBar/Classes/GradientLoadingBarController.swift b/GradientLoadingBar/Classes/GradientLoadingBarController.swift index f27b696..e92ffc6 100644 --- a/GradientLoadingBar/Classes/GradientLoadingBarController.swift +++ b/GradientLoadingBar/Classes/GradientLoadingBarController.swift @@ -17,33 +17,35 @@ open class GradientLoadingBarController { // MARK: - Public properties /// The height of the gradient bar. + /// /// - Note: Has to be public to allow overwriting `setupConstraints()` public let height: CGFloat /// Flag whether the top layout constraint should respect `safeAreaLayoutGuide`. + /// /// - Note: Has to be public to allow overwriting `setupConstraints()` public let isRelativeToSafeArea: Bool /// View containing the gradient layer. - public let gradientView = GradientActivityIndicatorView() + public let gradientActivityIndicatorView = GradientActivityIndicatorView() /// Colors used for the gradient. public var gradientColors: [UIColor] { get { - gradientView.gradientColors + gradientActivityIndicatorView.gradientColors } set { - gradientView.gradientColors = newValue + gradientActivityIndicatorView.gradientColors = newValue } } /// Duration for the progress animation. public var progressAnimationDuration: TimeInterval { get { - gradientView.progressAnimationDuration + gradientActivityIndicatorView.progressAnimationDuration } set { - gradientView.progressAnimationDuration = newValue + gradientActivityIndicatorView.progressAnimationDuration = newValue } } @@ -73,7 +75,7 @@ open class GradientLoadingBarController { self.isRelativeToSafeArea = isRelativeToSafeArea // We don't want the view to be visible initially. - gradientView.fadeOut(duration: 0) + gradientActivityIndicatorView.fadeOut(duration: 0) bindViewModelToView() } @@ -90,24 +92,24 @@ open class GradientLoadingBarController { } NSLayoutConstraint.activate([ - gradientView.topAnchor.constraint(equalTo: superViewTopAnchor), - gradientView.heightAnchor.constraint(equalToConstant: height), + gradientActivityIndicatorView.topAnchor.constraint(equalTo: superViewTopAnchor), + gradientActivityIndicatorView.heightAnchor.constraint(equalToConstant: height), - gradientView.leadingAnchor.constraint(equalTo: superview.leadingAnchor), - gradientView.trailingAnchor.constraint(equalTo: superview.trailingAnchor) + gradientActivityIndicatorView.leadingAnchor.constraint(equalTo: superview.leadingAnchor), + gradientActivityIndicatorView.trailingAnchor.constraint(equalTo: superview.trailingAnchor) ]) } /// Fades in the gradient loading bar. public func fadeIn(duration: TimeInterval = TimeInterval.GradientLoadingBar.fadeInDuration, completion: ((Bool) -> Void)? = nil) { - gradientView.fadeIn(duration: duration, - completion: completion) + gradientActivityIndicatorView.fadeIn(duration: duration, + completion: completion) } /// Fades out the gradient loading bar. public func fadeOut(duration: TimeInterval = TimeInterval.GradientLoadingBar.fadeOutDuration, completion: ((Bool) -> Void)? = nil) { - gradientView.fadeOut(duration: duration, - completion: completion) + gradientActivityIndicatorView.fadeOut(duration: duration, + completion: completion) } // MARK: - Private methods @@ -120,11 +122,11 @@ open class GradientLoadingBarController { private func updateSuperview(_ superview: UIView?) { // If the view’s superview is not nil, the superview releases the view. - gradientView.removeFromSuperview() + gradientActivityIndicatorView.removeFromSuperview() if let superview = superview { - gradientView.translatesAutoresizingMaskIntoConstraints = false - superview.addSubview(gradientView) + gradientActivityIndicatorView.translatesAutoresizingMaskIntoConstraints = false + superview.addSubview(gradientActivityIndicatorView) setupConstraints(superview: superview) } diff --git a/GradientLoadingBar/Classes/Misc/Constants.swift b/GradientLoadingBar/Classes/Misc/Constants.swift index e7dcf2b..0bd141e 100644 --- a/GradientLoadingBar/Classes/Misc/Constants.swift +++ b/GradientLoadingBar/Classes/Misc/Constants.swift @@ -10,7 +10,7 @@ import UIKit extension UIColor { /// Default colors for components. - public struct GradientLoadingBar { + public enum GradientLoadingBar { /// The default color palette for the gradient colors. /// /// - SeeAlso: https://codepen.io/marcobiedermann/pen/LExXWW @@ -22,7 +22,7 @@ extension UIColor { extension TimeInterval { /// Numeric default values. - public struct GradientLoadingBar { + public enum GradientLoadingBar { /// The default duration for fading-in the loading bar, measured in seconds. public static let fadeInDuration = 0.33 diff --git a/GradientLoadingBar/Classes/Views/GradientActivityIndicatorView.swift b/GradientLoadingBar/Classes/Views/GradientActivityIndicatorView.swift index 6f6592b..d2c9c2e 100644 --- a/GradientLoadingBar/Classes/Views/GradientActivityIndicatorView.swift +++ b/GradientLoadingBar/Classes/Views/GradientActivityIndicatorView.swift @@ -14,7 +14,9 @@ open class GradientActivityIndicatorView: UIView { // MARK: - Config /// Animation-Key for the progress animation. - private static let progressAnimationKey = "GradientActivityIndicatorView--progressAnimation" + private enum Config { + static let progressAnimationKey = "GradientActivityIndicatorView--progressAnimation" + } // MARK: - Public properties @@ -120,9 +122,9 @@ open class GradientActivityIndicatorView: UIView { private func updateAnimation(isAnimating: Bool) { if isAnimating { - gradientLayer?.add(progressAnimation, forKey: GradientActivityIndicatorView.progressAnimationKey) + gradientLayer?.add(progressAnimation, forKey: Config.progressAnimationKey) } else { - gradientLayer?.removeAnimation(forKey: GradientActivityIndicatorView.progressAnimationKey) + gradientLayer?.removeAnimation(forKey: Config.progressAnimationKey) } } } diff --git a/NotchGradientLoadingBarController.swift b/NotchGradientLoadingBarController.swift new file mode 100644 index 0000000..18768b3 --- /dev/null +++ b/NotchGradientLoadingBarController.swift @@ -0,0 +1,125 @@ +// +// NotchGradientLoadingBarController.swift +// GradientLoadingBar +// +// Created by Felix Mau on 11.06.20. +// + +import UIKit + +/// +@available(iOS 11.0, *) +public typealias NotchGradientLoadingBar = NotchGradientLoadingBarController + +@available(iOS 11.0, *) +open class NotchGradientLoadingBarController: GradientLoadingBarController { + // MARK: - Config + + /// Values are based on + /// + private enum Config { + /// The width of the iPhone notch (a little bit larger than mentioned in the docs). + static let notchWidth: CGFloat = 211 + + /// The radius of the small circle on the outside of the notch. + static let smallCircleRadius: CGFloat = 6 + + /// The radius of the small circle on the inside of the notch. + static let largeCircleRadius: CGFloat = 20 + } + + // MARK: - Public methods + + override open func setupConstraints(superview: UIView) { + // + let height = 2 * Config.smallCircleRadius + Config.largeCircleRadius + self.height + + NSLayoutConstraint.activate([ + gradientActivityIndicatorView.topAnchor.constraint(equalTo: superview.topAnchor), + gradientActivityIndicatorView.heightAnchor.constraint(equalToConstant: height), + + gradientActivityIndicatorView.leadingAnchor.constraint(equalTo: superview.leadingAnchor), + gradientActivityIndicatorView.trailingAnchor.constraint(equalTo: superview.trailingAnchor) + ]) + + applyNotchMask() + } + + // MARK: - Private methods + + private func applyNotchMask() { + // We draw the mask of the notch in the center of the screen. + // As we currently only support portrait mode, we can safely use `UIScreen.main.bounds` here. + let screenWidth = UIScreen.main.bounds.size.width + let leftNotchPoint = (screenWidth - Config.notchWidth) / 2 + let rightNotchPoint = (screenWidth + Config.notchWidth) / 2 + + let smallCircleDiameter: CGFloat = 2 * Config.smallCircleRadius + + let bezierPath = UIBezierPath() + bezierPath.move(to: CGPoint(x: 0, y: 0)) + + // Draw line to small-circle left to `leftNotchPoint`. + bezierPath.addLineTo(x: leftNotchPoint - Config.smallCircleRadius, + y: 0) + + bezierPath.addArc(withCenter: CGPoint(x: leftNotchPoint - Config.smallCircleRadius, y: Config.smallCircleRadius), + radius: Config.smallCircleRadius, + startAngle: -CGFloat.pi / 2, + endAngle: 0, + clockwise: true) + + // Draw the large-circle. + // Moving it up by two points looked way better. + let verticalOffsetForLargeCenterPoint: CGFloat = 2 + bezierPath.addArc(withCenter: CGPoint(x: leftNotchPoint + Config.largeCircleRadius, + y: smallCircleDiameter - verticalOffsetForLargeCenterPoint), + radius: Config.largeCircleRadius, + startAngle: -CGFloat.pi, + endAngle: CGFloat.pi / 2, + clockwise: false) + + // Draw line to large-circle underneath and left to `rightNotchPoint`. + bezierPath.addLineTo(x: rightNotchPoint - Config.largeCircleRadius, + y: smallCircleDiameter + Config.largeCircleRadius - verticalOffsetForLargeCenterPoint) + + // Draw the large-circle. + // Moving it up by two points looked way better. + bezierPath.addArc(withCenter: CGPoint(x: rightNotchPoint - Config.largeCircleRadius, + y: smallCircleDiameter - verticalOffsetForLargeCenterPoint), + radius: Config.largeCircleRadius, + startAngle: CGFloat.pi / 2, + endAngle: 0, + clockwise: false) + + bezierPath.addArc(withCenter: CGPoint(x: rightNotchPoint + Config.smallCircleRadius, y: Config.smallCircleRadius), + radius: Config.smallCircleRadius, + startAngle: CGFloat.pi, + endAngle: CGFloat.pi + CGFloat.pi / 2, + clockwise: true) + + // Draw line to the end of the screen. + bezierPath.addLineTo(x: screenWidth, y: 0) + bezierPath.close() + + let shapeLayer = CAShapeLayer() + shapeLayer.fillColor = UIColor.white.cgColor + shapeLayer.strokeColor = UIColor.white.cgColor + shapeLayer.path = bezierPath.cgPath + + // As the stroke lays outside of our bezier-path and only one-half of the stroke is visible (the other part is hidden behind the frame + // of the smartphone), we have to multiply it by two. + shapeLayer.lineWidth = 2 * height + + gradientActivityIndicatorView.layer.mask = shapeLayer + } +} + +// MARK: - Helpers + +private extension UIBezierPath { + // swiftlint:disable:next identifier_name + func addLineTo(x: CGFloat, y: CGFloat) { + addLine(to: CGPoint(x: x, y: y)) + } +}