From c4704b6e7d1b4e251f85fc09e03ca658fa4d30c5 Mon Sep 17 00:00:00 2001 From: andy0570 Date: Thu, 25 Aug 2022 14:48:54 +0800 Subject: [PATCH 01/13] Added `applyGradient(colors:locations:direction:)` extension to apply gradient color. --- .../SwifterSwift/UIKit/UIViewExtensions.swift | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index ae4052025..d8f117a2e 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -49,6 +49,14 @@ public extension UIView { /// SwifterSwift: easeInOut animation. case easeInOut } + + /// SwifterSwift: Apply gradient directions + enum GradientDirection { + case topToBottom + case bottomToTop + case leftToRight + case rightToLeft + } } // MARK: - Properties @@ -472,6 +480,45 @@ public extension UIView { CATransaction.commit() } + /// SwifterSwift: Apply Gradient Colors. + /// + /// view.applyGradient( + /// colors: [UIColor.red.cgColor, UIColor.blue.cgColor], + /// locations: [0.0, 1.0], + /// direction: .topToBottom + /// ) + /// + /// - Parameters: + /// - colors: An array of colors defining the color of each gradient stop. + /// - locations: An array of NSNumber defining the location of each + /// gradient stop as a value in the range [0,1]. The values must be + /// monotonically increasing. + /// - direction: Enumeration type describing the direction of the gradient. + func applyGradient(colors: [Any]?, locations: [NSNumber]? = [0.0, 1.0], direction: GradientDirection = .topToBottom) { + // + let gradientLayer = CAGradientLayer() + gradientLayer.frame = self.bounds + gradientLayer.colors = colors + gradientLayer.locations = locations + + switch direction { + case .topToBottom: + gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0) + gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0) + case .bottomToTop: + gradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0) + gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0) + case .leftToRight: + gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5) + gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5) + case .rightToLeft: + gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.5) + gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.5) + } + + layer.addSublayer(gradientLayer) + } + /// SwifterSwift: Add Visual Format constraints. /// /// - Parameters: From 0614d99059ed97508535c57a000392eb30876704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8B=AC=E6=9C=A8=E8=88=9F=E7=9A=84=E6=9C=A8?= Date: Fri, 18 Nov 2022 11:44:19 +0800 Subject: [PATCH 02/13] Update Sources/SwifterSwift/UIKit/UIViewExtensions.swift Co-authored-by: Guy Kogus --- Sources/SwifterSwift/UIKit/UIViewExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index d8f117a2e..040329f07 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -494,7 +494,7 @@ public extension UIView { /// gradient stop as a value in the range [0,1]. The values must be /// monotonically increasing. /// - direction: Enumeration type describing the direction of the gradient. - func applyGradient(colors: [Any]?, locations: [NSNumber]? = [0.0, 1.0], direction: GradientDirection = .topToBottom) { + func applyGradient(colors: [Color]?, locations: [CGFloat]? = [0.0, 1.0], direction: GradientDirection = .topToBottom) { // let gradientLayer = CAGradientLayer() gradientLayer.frame = self.bounds From 5235a190dd0f914c58e2fec0c40edf341bf81ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8B=AC=E6=9C=A8=E8=88=9F=E7=9A=84=E6=9C=A8?= Date: Fri, 18 Nov 2022 11:45:47 +0800 Subject: [PATCH 03/13] Update Sources/SwifterSwift/UIKit/UIViewExtensions.swift Co-authored-by: Guy Kogus --- Sources/SwifterSwift/UIKit/UIViewExtensions.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index 040329f07..086c4d4b1 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -498,8 +498,8 @@ public extension UIView { // let gradientLayer = CAGradientLayer() gradientLayer.frame = self.bounds - gradientLayer.colors = colors - gradientLayer.locations = locations + gradientLayer.colors = colors?.map { $0.cgColor } + gradientLayer.locations = locations?.map { NSNumber(value: $0) } switch direction { case .topToBottom: From 4356d1a35b42d47ebf719133a9a87ab493934634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8B=AC=E6=9C=A8=E8=88=9F=E7=9A=84=E6=9C=A8?= Date: Fri, 18 Nov 2022 11:46:00 +0800 Subject: [PATCH 04/13] Update Sources/SwifterSwift/UIKit/UIViewExtensions.swift Co-authored-by: Guy Kogus --- Sources/SwifterSwift/UIKit/UIViewExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index 086c4d4b1..80cc0efc6 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -497,7 +497,7 @@ public extension UIView { func applyGradient(colors: [Color]?, locations: [CGFloat]? = [0.0, 1.0], direction: GradientDirection = .topToBottom) { // let gradientLayer = CAGradientLayer() - gradientLayer.frame = self.bounds + gradientLayer.frame = bounds gradientLayer.colors = colors?.map { $0.cgColor } gradientLayer.locations = locations?.map { NSNumber(value: $0) } From 862a926fe32950dd120a4aa7c52b239027fdb9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8B=AC=E6=9C=A8=E8=88=9F=E7=9A=84=E6=9C=A8?= Date: Fri, 18 Nov 2022 11:46:21 +0800 Subject: [PATCH 05/13] Update Sources/SwifterSwift/UIKit/UIViewExtensions.swift Co-authored-by: Guy Kogus --- Sources/SwifterSwift/UIKit/UIViewExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index 80cc0efc6..78980302f 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -483,7 +483,7 @@ public extension UIView { /// SwifterSwift: Apply Gradient Colors. /// /// view.applyGradient( - /// colors: [UIColor.red.cgColor, UIColor.blue.cgColor], + /// colors: [.red, .blue], /// locations: [0.0, 1.0], /// direction: .topToBottom /// ) From e053eabe5574fd399918fb8bb16e3126425acf9d Mon Sep 17 00:00:00 2001 From: Qilin Hu Date: Fri, 18 Nov 2022 14:00:13 +0800 Subject: [PATCH 06/13] Added a CHANGELOG entry. --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87d094a2d..ddae70493 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added `widthConstraint`, `heightConstraint`, `leadingConstraint`, `trailingConstraint`, `topConstraint`, and `bottomConstraint` for finding specific constraints. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [gurgeous](https://github.com/gurgeous) - Added `UIView.subviews(ofType:)` extension which returns all the subviews of a given type recursively in the view hierarchy rooted on the view it its called. [#993](https://github.com/SwifterSwift/SwifterSwift/pull/993) by [ashercoelho](https://github.com/ashercoelho) - Added `UIStackView.swap(_ view1:, _ view2:)` extension which exchanges two views that are arranged in the stack. [#989](https://github.com/SwifterSwift/SwifterSwift/pull/989) by [salahamassi](https://github.com/salahamassi) + - Added `applyGradient(colors:locations:direction:)` extension to apply gradient color. [#1039](https://github.com/SwifterSwift/SwifterSwift/pull/1039) by [Andy0570](https://github.com/Andy0570) - **UIImage** - Added `averageColor`, which calculates the average UIColor for an entire image. [#884](https://github.com/SwifterSwift/SwifterSwift/pull/884) by [gurgeous](https://github.com/gurgeous) - Added `withAlwaysOriginalTintColor(_:)` returns a new version of the image with a tint color that uses the .alwaysOriginal rendering mode. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [jayxiang][https://github.com/jayxiang] @@ -455,7 +456,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added scalar multiplication of CGFloat and CGVector via standard multiplication operator (\*). [#527](https://github.com/SwifterSwift/SwifterSwift/pull/527) by [moyerr](https://github.com/moyerr) - Added negation of vectors via prefix (-) operator. [#527](https://github.com/SwifterSwift/SwifterSwift/pull/527) by [moyerr](https://github.com/moyerr) - Added `init(angle:magnitude:)` to create vectors based on their angle and magnitude. [#527](https://github.com/SwifterSwift/SwifterSwift/pull/527) by [moyerr](https://github.com/moyerr) --**UIRefreshControl**: +- **UIRefreshControl**: - `beginRefresh(in tableView:, animated:, sendAction:)` UIRefreshControl extension to begin refresh programatically. [#525](https://github.com/SwifterSwift/SwifterSwift/pull/525) by [ratulSharker](https://github.com/ratulSharker) - **Dictionary**: - Added `removeValueForRandomKey()` to remove a value for a random key from a dictionary. [#497](https://github.com/SwifterSwift/SwifterSwift/pull/497) by [MaxHaertwig](https://github.com/maxhaertwig). From e6bd26168682b1c414d500ba955c6cc4c5a2bb0c Mon Sep 17 00:00:00 2001 From: Phill Zarfos Date: Fri, 23 Dec 2022 17:00:12 -0500 Subject: [PATCH 07/13] added tests for UIView applyGradient() --- Tests/UIKitTests/UIViewExtensionsTests.swift | 110 +++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/Tests/UIKitTests/UIViewExtensionsTests.swift b/Tests/UIKitTests/UIViewExtensionsTests.swift index be45b1df7..734a1c9ac 100644 --- a/Tests/UIKitTests/UIViewExtensionsTests.swift +++ b/Tests/UIKitTests/UIViewExtensionsTests.swift @@ -378,6 +378,116 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssert(view.gestureRecognizers!.isEmpty) } + func testApplyGradient() { + // topToBottom + let view0 = UIView() + XCTAssertNil(view0.layer.sublayers) + view0.applyGradient( + colors: [.red, .orange, .green, .blue], + locations: [0.0, 0.333, 0.667, 1.0], + direction: .topToBottom + ) + XCTAssertNotNil(view0.layer.sublayers) + if let sublayers = view0.layer.sublayers as? [CAGradientLayer] { + XCTAssertEqual(sublayers.count, 1) + XCTAssertTrue(sublayers[0].startPoint.x.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].startPoint.y.isEqual(to: 0.0)) + XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 1.0)) + XCTAssertEqual(sublayers[0].colors?.count, 4) + XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) + XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) + XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) + XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + XCTAssertEqual(sublayers[0].locations?.count, 4) + XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) + XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) + XCTAssertNotNil(sublayers[0].locations?[2].isEqual(to: 0.667)) + XCTAssertNotNil(sublayers[0].locations?[3].isEqual(to: 1.0)) + } + + // bottomToTop + let view1 = UIView() + XCTAssertNil(view1.layer.sublayers) + view1.applyGradient( + colors: [.red, .orange, .green, .blue], + locations: [0.0, 0.333, 0.667, 1.0], + direction: .bottomToTop + ) + XCTAssertNotNil(view1.layer.sublayers) + if let sublayers = view1.layer.sublayers as? [CAGradientLayer] { + XCTAssertEqual(sublayers.count, 1) + XCTAssertTrue(sublayers[0].startPoint.x.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].startPoint.y.isEqual(to: 1.0)) + XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.0)) + XCTAssertEqual(sublayers[0].colors?.count, 4) + XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) + XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) + XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) + XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + XCTAssertEqual(sublayers[0].locations?.count, 4) + XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) + XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) + XCTAssertNotNil(sublayers[0].locations?[2].isEqual(to: 0.667)) + XCTAssertNotNil(sublayers[0].locations?[3].isEqual(to: 1.0)) + } + + // leftToRight + let view2 = UIView() + XCTAssertNil(view2.layer.sublayers) + view2.applyGradient( + colors: [.red, .orange, .green, .blue], + locations: [0.0, 0.333, 0.667, 1.0], + direction: .leftToRight + ) + XCTAssertNotNil(view2.layer.sublayers) + if let sublayers = view2.layer.sublayers as? [CAGradientLayer] { + XCTAssertEqual(sublayers.count, 1) + XCTAssertTrue(sublayers[0].startPoint.x.isEqual(to: 0.0)) + XCTAssertTrue(sublayers[0].startPoint.y.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 1.0)) + XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.5)) + XCTAssertEqual(sublayers[0].colors?.count, 4) + XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) + XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) + XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) + XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + XCTAssertEqual(sublayers[0].locations?.count, 4) + XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) + XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) + XCTAssertNotNil(sublayers[0].locations?[2].isEqual(to: 0.667)) + XCTAssertNotNil(sublayers[0].locations?[3].isEqual(to: 1.0)) + } + + // rightToLeft + let view3 = UIView() + XCTAssertNil(view3.layer.sublayers) + view3.applyGradient( + colors: [.red, .orange, .green, .blue], + locations: [0.0, 0.333, 0.667, 1.0], + direction: .rightToLeft + ) + XCTAssertNotNil(view3.layer.sublayers) + if let sublayers = view3.layer.sublayers as? [CAGradientLayer] { + XCTAssertEqual(sublayers.count, 1) + XCTAssertTrue(sublayers[0].startPoint.x.isEqual(to: 1.0)) + XCTAssertTrue(sublayers[0].startPoint.y.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.0)) + XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.5)) + XCTAssertEqual(sublayers[0].colors?.count, 4) + XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) + XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) + XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) + XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + XCTAssertEqual(sublayers[0].locations?.count, 4) + XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) + XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) + XCTAssertNotNil(sublayers[0].locations?[2].isEqual(to: 0.667)) + XCTAssertNotNil(sublayers[0].locations?[3].isEqual(to: 1.0)) + } + } + func testAnchor() { let view = UIView() let subview = UIView() From 43784d996079efd84057139654bbc990fe40a75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8B=AC=E6=9C=A8=E8=88=9F=E7=9A=84=E6=9C=A8?= Date: Mon, 6 Mar 2023 10:36:41 +0800 Subject: [PATCH 08/13] Update Sources/SwifterSwift/UIKit/UIViewExtensions.swift Co-authored-by: Guy Kogus --- Sources/SwifterSwift/UIKit/UIViewExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index 78980302f..bef456d2d 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -490,7 +490,7 @@ public extension UIView { /// /// - Parameters: /// - colors: An array of colors defining the color of each gradient stop. - /// - locations: An array of NSNumber defining the location of each + /// - locations: An array of `CGFloat` defining the location of each /// gradient stop as a value in the range [0,1]. The values must be /// monotonically increasing. /// - direction: Enumeration type describing the direction of the gradient. From 8db37238a93e67223daf7874ed8328161f08a825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8B=AC=E6=9C=A8=E8=88=9F=E7=9A=84=E6=9C=A8?= Date: Mon, 6 Mar 2023 10:38:21 +0800 Subject: [PATCH 09/13] Update Sources/SwifterSwift/UIKit/UIViewExtensions.swift Co-authored-by: Guy Kogus --- Sources/SwifterSwift/UIKit/UIViewExtensions.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index bef456d2d..4d23679b2 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -494,7 +494,7 @@ public extension UIView { /// gradient stop as a value in the range [0,1]. The values must be /// monotonically increasing. /// - direction: Enumeration type describing the direction of the gradient. - func applyGradient(colors: [Color]?, locations: [CGFloat]? = [0.0, 1.0], direction: GradientDirection = .topToBottom) { + func applyGradient(colors: [Color], locations: [CGFloat] = [0.0, 1.0], direction: GradientDirection = .topToBottom) { // let gradientLayer = CAGradientLayer() gradientLayer.frame = bounds From d85b67b76c599a42b2b5fbef7cd2e5f8fb564ccc Mon Sep 17 00:00:00 2001 From: Qilin Hu Date: Mon, 6 Mar 2023 11:46:26 +0800 Subject: [PATCH 10/13] update 'addGradient(colors:locations:direction:)' --- .../SwifterSwift/UIKit/UIViewExtensions.swift | 45 +++++++++---------- Tests/UIKitTests/UIViewExtensionsTests.swift | 20 ++++++--- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index 4d23679b2..754ebafcc 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -56,6 +56,19 @@ public extension UIView { case bottomToTop case leftToRight case rightToLeft + + var gradientEndpoint: (CGPoint, CGPoint) { + switch self { + case .topToBottom: + return (CGPoint(x: 0.5, y: 0.0), CGPoint(x: 0.5, y: 1.0)) + case .bottomToTop: + return (CGPoint(x: 0.5, y: 1.0), CGPoint(x: 0.5, y: 0.0)) + case .leftToRight: + return (CGPoint(x: 0.0, y: 0.5), CGPoint(x: 1.0, y: 0.5)) + case .rightToLeft: + return (CGPoint(x: 1.0, y: 0.5), CGPoint(x: 0.0, y: 0.5)) + } + } } } @@ -480,42 +493,28 @@ public extension UIView { CATransaction.commit() } - /// SwifterSwift: Apply Gradient Colors. + /// SwifterSwift: Add Gradient Colors. /// - /// view.applyGradient( + /// view.addGradient( /// colors: [.red, .blue], /// locations: [0.0, 1.0], /// direction: .topToBottom /// ) /// /// - Parameters: - /// - colors: An array of colors defining the color of each gradient stop. + /// - colors: An array of `SFColor` defining the color of each gradient stop. /// - locations: An array of `CGFloat` defining the location of each - /// gradient stop as a value in the range [0,1]. The values must be + /// gradient stop as a value in the range [0, 1]. The values must be /// monotonically increasing. /// - direction: Enumeration type describing the direction of the gradient. - func applyGradient(colors: [Color], locations: [CGFloat] = [0.0, 1.0], direction: GradientDirection = .topToBottom) { + func addGradient(colors: [SFColor], locations: [CGFloat] = [0.0, 1.0], direction: GradientDirection = .topToBottom) { // let gradientLayer = CAGradientLayer() gradientLayer.frame = bounds - gradientLayer.colors = colors?.map { $0.cgColor } - gradientLayer.locations = locations?.map { NSNumber(value: $0) } - - switch direction { - case .topToBottom: - gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0) - gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0) - case .bottomToTop: - gradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0) - gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0) - case .leftToRight: - gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5) - gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5) - case .rightToLeft: - gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.5) - gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.5) - } - + gradientLayer.colors = colors.map { $0.cgColor } + gradientLayer.locations = locations.map { NSNumber(value: $0) } + gradientLayer.startPoint = direction.gradientEndpoint.0 + gradientLayer.endPoint = direction.gradientEndpoint.1 layer.addSublayer(gradientLayer) } diff --git a/Tests/UIKitTests/UIViewExtensionsTests.swift b/Tests/UIKitTests/UIViewExtensionsTests.swift index 734a1c9ac..4b0d55281 100644 --- a/Tests/UIKitTests/UIViewExtensionsTests.swift +++ b/Tests/UIKitTests/UIViewExtensionsTests.swift @@ -378,12 +378,12 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssert(view.gestureRecognizers!.isEmpty) } - func testApplyGradient() { + func testAddGradient() { // topToBottom let view0 = UIView() XCTAssertNil(view0.layer.sublayers) - view0.applyGradient( - colors: [.red, .orange, .green, .blue], + view0.addGradient( + colors:[.red, .orange, .green, .blue], locations: [0.0, 0.333, 0.667, 1.0], direction: .topToBottom ) @@ -395,10 +395,12 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.5)) XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 1.0)) XCTAssertEqual(sublayers[0].colors?.count, 4) + // swiftlint:disable force_cast XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + // swiftlint:enable force_cast XCTAssertEqual(sublayers[0].locations?.count, 4) XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) @@ -409,7 +411,7 @@ final class UIViewExtensionsTests: XCTestCase { // bottomToTop let view1 = UIView() XCTAssertNil(view1.layer.sublayers) - view1.applyGradient( + view1.addGradient( colors: [.red, .orange, .green, .blue], locations: [0.0, 0.333, 0.667, 1.0], direction: .bottomToTop @@ -422,10 +424,12 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.5)) XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.0)) XCTAssertEqual(sublayers[0].colors?.count, 4) + // swiftlint:disable force_cast XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + // swiftlint:enable force_cast XCTAssertEqual(sublayers[0].locations?.count, 4) XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) @@ -436,7 +440,7 @@ final class UIViewExtensionsTests: XCTestCase { // leftToRight let view2 = UIView() XCTAssertNil(view2.layer.sublayers) - view2.applyGradient( + view2.addGradient( colors: [.red, .orange, .green, .blue], locations: [0.0, 0.333, 0.667, 1.0], direction: .leftToRight @@ -449,10 +453,12 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 1.0)) XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.5)) XCTAssertEqual(sublayers[0].colors?.count, 4) + // swiftlint:disable force_cast XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + // swiftlint:enable force_cast XCTAssertEqual(sublayers[0].locations?.count, 4) XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) @@ -463,7 +469,7 @@ final class UIViewExtensionsTests: XCTestCase { // rightToLeft let view3 = UIView() XCTAssertNil(view3.layer.sublayers) - view3.applyGradient( + view3.addGradient( colors: [.red, .orange, .green, .blue], locations: [0.0, 0.333, 0.667, 1.0], direction: .rightToLeft @@ -476,10 +482,12 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.0)) XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.5)) XCTAssertEqual(sublayers[0].colors?.count, 4) + // swiftlint:disable force_cast XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + // swiftlint:enable force_cast XCTAssertEqual(sublayers[0].locations?.count, 4) XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) From a6038ee649ef06f0e34b5daf0e5b51ba6f86fcc0 Mon Sep 17 00:00:00 2001 From: Qilin Hu Date: Mon, 6 Mar 2023 11:50:16 +0800 Subject: [PATCH 11/13] rename 'applyGradient()' to 'addGradient()' --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a361ca4b..3165c0339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,7 +89,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added `widthConstraint`, `heightConstraint`, `leadingConstraint`, `trailingConstraint`, `topConstraint`, and `bottomConstraint` for finding specific constraints. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [gurgeous](https://github.com/gurgeous) - Added `UIView.subviews(ofType:)` extension which returns all the subviews of a given type recursively in the view hierarchy rooted on the view it its called. [#993](https://github.com/SwifterSwift/SwifterSwift/pull/993) by [ashercoelho](https://github.com/ashercoelho) - Added `UIStackView.swap(_ view1:, _ view2:)` extension which exchanges two views that are arranged in the stack. [#989](https://github.com/SwifterSwift/SwifterSwift/pull/989) by [salahamassi](https://github.com/salahamassi) - - Added `applyGradient(colors:locations:direction:)` extension to apply gradient color. [#1039](https://github.com/SwifterSwift/SwifterSwift/pull/1039) by [Andy0570](https://github.com/Andy0570) + - Added `addGradient(colors:locations:direction:)` extension to apply gradient color. [#1039](https://github.com/SwifterSwift/SwifterSwift/pull/1039) by [Andy0570](https://github.com/Andy0570) - **UIImage** - Added `averageColor`, which calculates the average UIColor for an entire image. [#884](https://github.com/SwifterSwift/SwifterSwift/pull/884) by [gurgeous](https://github.com/gurgeous) - Added `withAlwaysOriginalTintColor(_:)` returns a new version of the image with a tint color that uses the .alwaysOriginal rendering mode. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [jayxiang][https://github.com/jayxiang] From 2a6e7ed623c55de9d6021f1e8a90ea531021b695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8B=AC=E6=9C=A8=E8=88=9F=E7=9A=84=E6=9C=A8?= Date: Mon, 20 Mar 2023 15:54:35 +0800 Subject: [PATCH 12/13] Update CHANGELOG.md Co-authored-by: Guy Kogus --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8c747fe3..33f6fa016 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -470,7 +470,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added negation of vectors via prefix (-) operator. [#527](https://github.com/SwifterSwift/SwifterSwift/pull/527) by [moyerr](https://github.com/moyerr) - Added `init(angle:magnitude:)` to create vectors based on their angle and magnitude. [#527](https://github.com/SwifterSwift/SwifterSwift/pull/527) by [moyerr](https://github.com/moyerr) - **UIRefreshControl**: - - `beginRefresh(in tableView:, animated:, sendAction:)` UIRefreshControl extension to begin refresh programatically. [#525](https://github.com/SwifterSwift/SwifterSwift/pull/525) by [ratulSharker](https://github.com/ratulSharker) + - `beginRefresh(in tableView:, animated:, sendAction:)` UIRefreshControl extension to begin refresh programmatically. [#525](https://github.com/SwifterSwift/SwifterSwift/pull/525) by [ratulSharker](https://github.com/ratulSharker) - **Dictionary**: - Added `removeValueForRandomKey()` to remove a value for a random key from a dictionary. [#497](https://github.com/SwifterSwift/SwifterSwift/pull/497) by [MaxHaertwig](https://github.com/maxhaertwig). - Added `mapKeysAndValues(_:)` to map a `Dictionary` into a `Dictionary` with different (or same) `Key` and `Value` types. [#546](https://github.com/SwifterSwift/SwifterSwift/pull/546) by [guykogus](https://github.com/guykogus) From 26c1673b9396a7422585eea272e310ed73bce8ef Mon Sep 17 00:00:00 2001 From: Qilin Hu Date: Wed, 22 Mar 2023 16:25:45 +0800 Subject: [PATCH 13/13] Refactor 'GradientDirection' type from enum to struct. --- .../SwifterSwift/UIKit/UIViewExtensions.swift | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index 754ebafcc..14499a87c 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -50,25 +50,19 @@ public extension UIView { case easeInOut } - /// SwifterSwift: Apply gradient directions - enum GradientDirection { - case topToBottom - case bottomToTop - case leftToRight - case rightToLeft - - var gradientEndpoint: (CGPoint, CGPoint) { - switch self { - case .topToBottom: - return (CGPoint(x: 0.5, y: 0.0), CGPoint(x: 0.5, y: 1.0)) - case .bottomToTop: - return (CGPoint(x: 0.5, y: 1.0), CGPoint(x: 0.5, y: 0.0)) - case .leftToRight: - return (CGPoint(x: 0.0, y: 0.5), CGPoint(x: 1.0, y: 0.5)) - case .rightToLeft: - return (CGPoint(x: 1.0, y: 0.5), CGPoint(x: 0.0, y: 0.5)) - } - } + /// SwifterSwift: Add gradient directions + struct GradientDirection { + static let topToBottom = GradientDirection(startPoint: CGPoint(x: 0.5, y: 0.0), + endPoint: CGPoint(x: 0.5, y: 1.0)) + static let bottomToTop = GradientDirection(startPoint: CGPoint(x: 0.5, y: 1.0), + endPoint: CGPoint(x: 0.5, y: 0.0)) + static let leftToRight = GradientDirection(startPoint: CGPoint(x: 0.0, y: 0.5), + endPoint: CGPoint(x: 1.0, y: 0.5)) + static let rightToLeft = GradientDirection(startPoint: CGPoint(x: 1.0, y: 0.5), + endPoint: CGPoint(x: 0.0, y: 0.5)) + + let startPoint: CGPoint + let endPoint: CGPoint } } @@ -506,15 +500,15 @@ public extension UIView { /// - locations: An array of `CGFloat` defining the location of each /// gradient stop as a value in the range [0, 1]. The values must be /// monotonically increasing. - /// - direction: Enumeration type describing the direction of the gradient. - func addGradient(colors: [SFColor], locations: [CGFloat] = [0.0, 1.0], direction: GradientDirection = .topToBottom) { + /// - direction: Struct type describing the direction of the gradient. + func addGradient(colors: [SFColor], locations: [CGFloat] = [0.0, 1.0], direction: GradientDirection) { // let gradientLayer = CAGradientLayer() gradientLayer.frame = bounds gradientLayer.colors = colors.map { $0.cgColor } gradientLayer.locations = locations.map { NSNumber(value: $0) } - gradientLayer.startPoint = direction.gradientEndpoint.0 - gradientLayer.endPoint = direction.gradientEndpoint.1 + gradientLayer.startPoint = direction.startPoint + gradientLayer.endPoint = direction.endPoint layer.addSublayer(gradientLayer) }