Skip to content

Commit

Permalink
Add addGradient(colors:locations:direction:) extension (#1039)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Guy Kogus <guy.kogus@gmail.com>
Co-authored-by: Phill Zarfos <pzarfos@thepennyhoarder.com>
  • Loading branch information
3 people committed Jun 19, 2023
1 parent 6ed83b7 commit 8dc7376
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 1 deletion.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,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 `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]
Expand Down Expand Up @@ -472,7 +473,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 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).
Expand Down
40 changes: 40 additions & 0 deletions Sources/SwifterSwift/UIKit/UIViewExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ public extension UIView {
/// SwifterSwift: easeInOut animation.
case easeInOut
}

/// 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
}
}

// MARK: - Properties
Expand Down Expand Up @@ -472,6 +487,31 @@ public extension UIView {
CATransaction.commit()
}

/// SwifterSwift: Add Gradient Colors.
///
/// view.addGradient(
/// colors: [.red, .blue],
/// locations: [0.0, 1.0],
/// direction: .topToBottom
/// )
///
/// - Parameters:
/// - 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
/// monotonically increasing.
/// - direction: Struct type describing the direction of the gradient.
func addGradient(colors: [SFColor], locations: [CGFloat] = [0.0, 1.0], direction: GradientDirection) {
// <https://github.com/swiftdevcenter/GradientColorExample>
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = colors.map { $0.cgColor }
gradientLayer.locations = locations.map { NSNumber(value: $0) }
gradientLayer.startPoint = direction.startPoint
gradientLayer.endPoint = direction.endPoint
layer.addSublayer(gradientLayer)
}

/// SwifterSwift: Add Visual Format constraints.
///
/// - Parameters:
Expand Down
118 changes: 118 additions & 0 deletions Tests/UIKitTests/UIViewExtensionsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,124 @@ final class UIViewExtensionsTests: XCTestCase {
XCTAssert(view.gestureRecognizers!.isEmpty)
}

func testAddGradient() {
// topToBottom
let view0 = UIView()
XCTAssertNil(view0.layer.sublayers)
view0.addGradient(
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)
// 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))
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.addGradient(
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)
// 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))
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.addGradient(
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)
// 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))
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.addGradient(
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)
// 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))
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()
Expand Down

0 comments on commit 8dc7376

Please sign in to comment.