diff --git a/CGRectExtensions/CGRectExtensions.swift b/CGRectExtensions/CGRectExtensions.swift index bdf7995..21debb4 100644 --- a/CGRectExtensions/CGRectExtensions.swift +++ b/CGRectExtensions/CGRectExtensions.swift @@ -19,56 +19,77 @@ import Foundation +// MARK: CGRect Control Points + +public enum CGRectControlPoint { + case MinXMinY + case CenterXMinY + case MaxXMinY + case MaxXCenterY + case MaxXMaxY + case CenterXMaxY + case MinXMaxY + case MinXCenterY + case CenterXCenterY +} + // MARK: CGPoint extension CGPoint { - + public init(_ x: CGFloat, _ y: CGFloat) { self.x = x self.y = y } - + public func with(# x: CGFloat) -> CGPoint { return CGPoint(x: x, y: y) } + public func with(# y: CGFloat) -> CGPoint { return CGPoint(x: x, y: y) } + + public func offset(# dx: CGFloat, dy: CGFloat) -> CGPoint { + return CGPoint(x: x + dx, y: y + dy) + } + } // MARK: CGSize extension CGSize { - + public init(_ width: CGFloat, _ height: CGFloat) { self.width = width self.height = height } - + public func with(# width: CGFloat) -> CGSize { return CGSize(width: width, height: height) } public func with(# height: CGFloat) -> CGSize { return CGSize(width: width, height: height) } + } // MARK: CGRect extension CGRect { - + public init(_ origin: CGPoint, _ size: CGSize) { self.origin = origin self.size = size } - + public init(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) { self.origin = CGPoint(x: x, y: y) self.size = CGSize(width: width, height: height) } - - // MARK: access shortcuts - + + // MARK: Access shortcuts + public var x: CGFloat { get {return origin.x} set {origin.x = newValue} @@ -77,7 +98,7 @@ extension CGRect { get {return origin.y} set {origin.y = newValue} } - + public var centerX: CGFloat { get {return x + width * 0.5} set {x = newValue - width * 0.5} @@ -86,9 +107,9 @@ extension CGRect { get {return y + height * 0.5} set {y = newValue - height * 0.5} } - - // MARK: edges - + + // MARK: Edges + public var left: CGFloat { get {return origin.x} set {origin.x = newValue} @@ -97,7 +118,7 @@ extension CGRect { get {return x + width} set {x = newValue - width} } - + #if os(iOS) public var top: CGFloat { get {return y} @@ -109,58 +130,88 @@ extension CGRect { } #else public var top: CGFloat { - get {return y + height} - set {y = newValue - height} + get {return y + height} + set {y = newValue - height} } public var bottom: CGFloat { - get {return y} - set {y = newValue} + get {return y} + set {y = newValue} } #endif - - // MARK: points - + + // MARK: Points + public var topLeft: CGPoint { get {return CGPoint(x: left, y: top)} set {left = newValue.x; top = newValue.y} } + public var topCenter: CGPoint { get {return CGPoint(x: centerX, y: top)} set {centerX = newValue.x; top = newValue.y} } + public var topRight: CGPoint { get {return CGPoint(x: right, y: top)} set {right = newValue.x; top = newValue.y} } - + public var centerLeft: CGPoint { get {return CGPoint(x: left, y: centerY)} set {left = newValue.x; centerY = newValue.y} } + public var center: CGPoint { get {return CGPoint(x: centerX, y: centerY)} set {centerX = newValue.x; centerY = newValue.y} } + public var centerRight: CGPoint { get {return CGPoint(x: right, y: centerY)} set {right = newValue.x; centerY = newValue.y} } - + public var bottomLeft: CGPoint { get {return CGPoint(x: left, y: bottom)} set {left = newValue.x; bottom = newValue.y} } + public var bottomCenter: CGPoint { get {return CGPoint(x: centerX, y: bottom)} set {centerX = newValue.x; bottom = newValue.y} } + public var bottomRight: CGPoint { get {return CGPoint(x: right, y: bottom)} set {right = newValue.x; bottom = newValue.y} } - - // MARK: with - + + // Returns the CGPoint of the CGRect's + public func pointOfControlPoint(controlPoint: CGRectControlPoint) -> CGPoint { + switch controlPoint { + case .MinXMinY: + return CGPoint(minX, minY) + case .CenterXMinY: + return CGPoint(centerX, minY) + case .MaxXMinY: + return CGPoint(maxX, minY) + case .MaxXCenterY: + return CGPoint(maxX, centerY) + case .MaxXMaxY: + return CGPoint(maxX, maxY) + case .CenterXMaxY: + return CGPoint(centerX, maxY) + case .MinXMaxY: + return CGPoint(minX, maxY) + case .MinXCenterY: + return CGPoint(minX, centerY) + case .CenterXCenterY: + return CGPoint(centerX, centerY) + } + } + + // MARK: With + public func with(# origin: CGPoint) -> CGRect { return CGRect(origin: origin, size: size) } @@ -173,7 +224,7 @@ extension CGRect { public func with(# y: CGFloat) -> CGRect { return with(x: x, y: y) } - + public func with(# size: CGSize) -> CGRect { return CGRect(origin: origin, size: size) } @@ -186,16 +237,16 @@ extension CGRect { public func with(# height: CGFloat) -> CGRect { return with(width: width, height: height) } - + public func with(# x: CGFloat, width: CGFloat) -> CGRect { return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height)) } public func with(# y: CGFloat, height: CGFloat) -> CGRect { return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height)) } - - // MARK: offset - + + // MARK: Offset + public func rectByOffsetting(dx: CGFloat, _ dy: CGFloat) -> CGRect { return with(x: x + dx, y: y + dy) } @@ -208,7 +259,7 @@ extension CGRect { public func rectByOffsetting(by: CGSize) -> CGRect { return with(x: x + by.width, y: y + by.height) } - + public mutating func offset(dx: CGFloat, _ dy: CGFloat) { offset(dx: dx, dy: dy) } @@ -221,203 +272,242 @@ extension CGRect { public mutating func offset(by: CGSize) { offset(dx: by.width, dy: by.height) } - - // MARK: inset - + + // MARK: Inset + public func rectByInsetting(by: CGFloat) -> CGRect { return rectByInsetting(dx: by, dy: by) } - + public func rectByInsetting(# dx: CGFloat) -> CGRect { return with(x: x + dx, width: width - dx * 2) } public func rectByInsetting(# dy: CGFloat) -> CGRect { return with(y: y + dy, height: height - dy * 2) } - + public func rectByInsetting(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect { return CGRect(x: x + minX, y: y + minY, width: width - minX - maxX, height: height - minY - maxY) } - + public func rectByInsetting(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect { #if os(iOS) return CGRect(x: x + left, y: y + top, width: width - right - left, height: height - top - bottom) - #else + #else return CGRect(x: x + left, y: y + bottom, width: width - right - left, height: height - top - bottom) #endif } - + public func rectByInsetting(# topLeft: CGSize) -> CGRect { return rectByInsetting(top: topLeft.height, left: topLeft.width) } + public func rectByInsetting(# topRight: CGSize) -> CGRect { return rectByInsetting(top: topRight.height, right: topRight.width) } + public func rectByInsetting(# bottomLeft: CGSize) -> CGRect { return rectByInsetting(bottom: bottomLeft.height, left: bottomLeft.width) } + public func rectByInsetting(# bottomRight: CGSize) -> CGRect { return rectByInsetting(bottom: bottomRight.height, right: bottomRight.width) } - + + public func rectByInsetting(insets: UIEdgeInsets) -> CGRect { + return rectByInsetting(top: insets.top, left: insets.left, bottom: insets.bottom, right: insets.right) + } + public mutating func inset(by: CGFloat) { inset(dx: by, dy: by) } - + public mutating func inset(# dx: CGFloat) { inset(dx: dx, dy: 0) } public mutating func inset(# dy: CGFloat) { inset(dx: 0, dy: dy) } - + public mutating func inset(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) { self = rectByInsetting(minX: minX, minY: minY, maxX: maxX, maxY: maxY) } - + public mutating func inset(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) { self = rectByInsetting(top: top, left: left, bottom: bottom, right: right) } - + public mutating func inset(# topLeft: CGSize) { self = rectByInsetting(topLeft: topLeft) } + public mutating func inset(# topRight: CGSize) { self = rectByInsetting(topRight: topRight) } + public mutating func inset(# bottomLeft: CGSize) { self = rectByInsetting(bottomLeft: bottomLeft) } + public mutating func inset(# bottomRight: CGSize) { self = rectByInsetting(bottomRight: bottomRight) } - - // MARK: extending - + + // MARK: Extending + public func rectByExtending(#dx: CGFloat, dy: CGFloat = 0) -> CGRect { return rectByInsetting(dx: -dx, dy: -dy) } + public func rectByExtending(# dy: CGFloat) -> CGRect { return rectByInsetting(dy: -dy) } - + public func rectByExtending(by: CGFloat) -> CGRect { return rectByInsetting(dx: -by, dy: -by) } - + public func rectByExtending(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect { return rectByInsetting(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY) } + public func rectByExtending(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect { return rectByInsetting(top: -top, left: -left, bottom: -bottom, right: -right) } - + public func rectByExtending(# topLeft: CGSize) -> CGRect { return rectByExtending(top: topLeft.height, left: topLeft.width) } + public func rectByExtending(# topRight: CGSize) -> CGRect { return rectByInsetting(top: -topRight.height, right: -topRight.width) } + public func rectByExtending(# bottomLeft: CGSize) -> CGRect { return rectByInsetting(bottom: -bottomLeft.height, left: -bottomLeft.width) } + public func rectByExtending(# bottomRight: CGSize) -> CGRect { return rectByInsetting(bottom: -bottomRight.height, right: -bottomRight.width) } - + public mutating func extend(#dx: CGFloat, dy: CGFloat = 0) { self = rectByInsetting(dx: -dx, dy: -dy) } + public mutating func extend(# dy: CGFloat) { self = rectByInsetting(dy: -dy) } - + public mutating func extend(by: CGFloat) { self = rectByInsetting(dx: -by, dy: -by) } - + public mutating func extend(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) { self = rectByInsetting(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY) } + public mutating func extend(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) { self = rectByInsetting(top: -top, left: -left, bottom: -bottom, right: -right) } - + public mutating func extend(# topLeft: CGSize) { self = rectByExtending(top: topLeft.height, left: topLeft.width) } + public mutating func extend(# topRight: CGSize) { self = rectByInsetting(top: -topRight.height, right: -topRight.width) } + public mutating func extend(# bottomLeft: CGSize) { self = rectByInsetting(bottom: -bottomLeft.height, left: -bottomLeft.width) } + public mutating func extend(# bottomRight: CGSize) { self = rectByInsetting(bottom: -bottomRight.height, right: -bottomRight.width) } - - // MARK: sizes - + + // MARK: Alignment + public func rectByCentering(size: CGSize) -> CGRect { - let dx = width - size.width - let dy = height - size.height - return CGRect(x: x + dx * 0.5, y: y + dy * 0.5, width: size.width, height: size.height) + return rectByCentering(size, aroundControlPoint: .CenterXCenterY) } - + + // A new CGRect of that is centered and then aligned to public func rectByCentering(size: CGSize, alignTo edge: CGRectEdge) -> CGRect { - return CGRect(origin: alignedOrigin(size, edge: edge), size: size) - } - - private func alignedOrigin(size: CGSize, edge: CGRectEdge) -> CGPoint { - let dx = width - size.width - let dy = height - size.height switch edge { case .MinXEdge: - return CGPoint(x: x, y: y + dy * 0.5) + return rectByPinning(size, controlPoint: .MinXCenterY, toTargetControlPoint: .MinXCenterY) case .MinYEdge: - return CGPoint(x: x + dx * 0.5, y: y) + return rectByPinning(size, controlPoint: .CenterXMinY, toTargetControlPoint: .CenterXMinY) case .MaxXEdge: - return CGPoint(x: x + dx, y: y + dy * 0.5) + return rectByPinning(size, controlPoint: .MaxXCenterY, toTargetControlPoint: .MaxXCenterY) case .MaxYEdge: - return CGPoint(x: x + dx * 0.5, y: y + dy) + return rectByPinning(size, controlPoint: .CenterXMaxY, toTargetControlPoint: .CenterXMaxY) } } - - public func rectByAligning(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) -> CGRect { - return CGRect(origin: alignedOrigin(size, corner: e1, e2), size: size) - } - - private func alignedOrigin(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) -> CGPoint { - let dx = width - size.width - let dy = height - size.height - switch (e1, e2) { - case (.MinXEdge, .MinYEdge), (.MinYEdge, .MinXEdge): - return CGPoint(x: x, y: y) - case (.MaxXEdge, .MinYEdge), (.MinYEdge, .MaxXEdge): - return CGPoint(x: x + dx, y: y) - case (.MinXEdge, .MaxYEdge), (.MaxYEdge, .MinXEdge): - return CGPoint(x: x, y: y + dy) - case (.MaxXEdge, .MaxYEdge), (.MaxYEdge, .MaxXEdge): - return CGPoint(x: x + dx, y: y + dy) - default: - preconditionFailure("Cannot align to this combination of edges") + + // A new CGRect of whose center is around target's control point + public func rectByCentering(size: CGSize, aroundControlPoint cPoint: CGRectControlPoint) -> CGRect { + let dx = size.width / 2 + let dy = size.height / 2 + let controlCGPoint = pointOfControlPoint(cPoint) + return CGRect(origin: controlCGPoint.offset(dx: -dx, dy: -dy), size: size) + } + + // A new CGRect of whose is pinned to + public func rectByPinning(size: CGSize, controlPoints cPoint: CGRectControlPoint) -> CGRect { + return rectByPinning(size, controlPoint: cPoint, toTargetControlPoint: cPoint) + } + + // A new CGRect of whose is pinned to + public func rectByPinning(size: CGSize, controlPoint cPoint: CGRectControlPoint, toTargetControlPoint targetCPoint: CGRectControlPoint) -> CGRect { + let dx = size.width / 2 + let dy = size.height / 2 + let rect = rectByCentering(size, aroundControlPoint: targetCPoint) + switch cPoint { + case .MinXMinY: + return rect.rectByOffsetting(dx, dy) + case .CenterXMinY: + return rect.rectByOffsetting(0, dy) + case .MaxXMinY: + return rect.rectByOffsetting(-dx, dy) + case .MaxXCenterY: + return rect.rectByOffsetting(-dx, 0) + case .MaxXMaxY: + return rect.rectByOffsetting(-dx, -dy) + case .CenterXMaxY: + return rect.rectByOffsetting(0, -dy) + case .MinXMaxY: + return rect.rectByOffsetting(dx, -dy) + case .MinXCenterY: + return rect.rectByOffsetting(dx, 0) + case .CenterXCenterY: + return rect } } - + + // MARK: Sizing + public mutating func setSizeCentered(size: CGSize) { self = rectByCentering(size) } - + public mutating func setSizeCentered(size: CGSize, alignTo edge: CGRectEdge) { self = rectByCentering(size, alignTo: edge) } - - public mutating func setSizeAligned(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) { - self = rectByAligning(size, corner: e1, e2) + + public mutating func setSizeWhilePinning(size: CGSize, controlPoint cPoint: CGRectControlPoint, toTargetControlPoint targetCPoint: CGRectControlPoint) { + self = rectByPinning(size, controlPoint: cPoint, toTargetControlPoint: targetCPoint) } + + public mutating func setSizeWhilePinning(size: CGSize, controlPoints cPoint: CGRectControlPoint) { + self = rectByPinning(size, controlPoints: cPoint) + } + } -// MARK: transform +// MARK: Transform extension CGAffineTransform: Equatable { } @@ -432,18 +522,21 @@ extension CGAffineTransform: DebugPrintable { } } -// MARK: operators +// MARK: Operators public func +(p1: CGPoint, p2: CGPoint) -> CGPoint { return CGPoint(x: p1.x + p2.x, y: p1.y + p2.y) } + public func +=(inout p1: CGPoint, p2: CGPoint) { p1.x += p2.x p1.y += p2.y } + public func -(p1: CGPoint, p2: CGPoint) -> CGPoint { return CGPoint(x: p1.x - p2.x, y: p1.y - p2.y) } + public func -=(inout p1: CGPoint, p2: CGPoint) { p1.x -= p2.x p1.y -= p2.y @@ -452,13 +545,16 @@ public func -=(inout p1: CGPoint, p2: CGPoint) { public func +(point: CGPoint, size: CGSize) -> CGPoint { return CGPoint(x: point.x + size.width, y: point.y + size.height) } + public func +=(inout point: CGPoint, size: CGSize) { point.x += size.width point.y += size.height } + public func -(point: CGPoint, size: CGSize) -> CGPoint { return CGPoint(x: point.x - size.width, y: point.y - size.height) } + public func -=(inout point: CGPoint, size: CGSize) { point.x -= size.width point.y -= size.height @@ -467,41 +563,52 @@ public func -=(inout point: CGPoint, size: CGSize) { public func +(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint { return CGPoint(x: point.x + tuple.0, y: point.y + tuple.1) } + public func +=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) { point.x += tuple.0 point.y += tuple.1 } + public func -(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint { return CGPoint(x: point.x - tuple.0, y: point.y - tuple.1) } + public func -=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) { point.x -= tuple.0 point.y -= tuple.1 } + public func *(point: CGPoint, factor: CGFloat) -> CGPoint { return CGPoint(x: point.x * factor, y: point.y * factor) } + public func *=(inout point: CGPoint, factor: CGFloat) { point.x *= factor point.y *= factor } + public func *(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint { return CGPoint(x: point.x * tuple.0, y: point.y * tuple.1) } + public func *=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) { point.x *= tuple.0 point.y *= tuple.1 } + public func /(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint { return CGPoint(x: point.x / tuple.0, y: point.y / tuple.1) } + public func /=(inout point: CGPoint, tuple: (CGFloat, CGFloat)) { point.x /= tuple.0 point.y /= tuple.1 } + public func /(point: CGPoint, factor: CGFloat) -> CGPoint { return CGPoint(x: point.x / factor, y: point.y / factor) } + public func /=(inout point: CGPoint, factor: CGFloat) { point.x /= factor point.y /= factor @@ -510,13 +617,16 @@ public func /=(inout point: CGPoint, factor: CGFloat) { public func +(s1: CGSize, s2: CGSize) -> CGSize { return CGSize(width: s1.width + s2.width, height: s1.height + s2.height) } + public func +=(inout s1: CGSize, s2: CGSize) { s1.width += s2.width s1.height += s2.height } + public func -(s1: CGSize, s2: CGSize) -> CGSize { return CGSize(width: s1.width - s2.width, height: s1.height - s2.height) } + public func -=(inout s1: CGSize, s2: CGSize) { s1.width -= s2.width s1.height -= s2.height @@ -525,41 +635,52 @@ public func -=(inout s1: CGSize, s2: CGSize) { public func +(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize { return CGSize(width: size.width + tuple.0, height: size.height + tuple.1) } + public func +=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { size.width += tuple.0 size.height += tuple.1 } + public func -(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize { return CGSize(width: size.width - tuple.0, height: size.height - tuple.1) } + public func -=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { size.width -= tuple.0 size.height -= tuple.1 } + public func *(size: CGSize, factor: CGFloat) -> CGSize { return CGSize(width: size.width * factor, height: size.height * factor) } + public func *=(inout size: CGSize, factor: CGFloat) { size.width *= factor size.height *= factor } + public func *(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize { return CGSize(width: size.width * tuple.0, height: size.height * tuple.1) } + public func *=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { size.width *= tuple.0 size.height *= tuple.1 } + public func /(size: CGSize, factor: CGFloat) -> CGSize { return CGSize(width: size.width / factor, height: size.height / factor) } + public func /=(inout size: CGSize, factor: CGFloat) { size.width /= factor size.height /= factor } + public func /(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize { return CGSize(width: size.width / tuple.0, height: size.height / tuple.1) } + public func /=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { size.width /= tuple.0 size.height /= tuple.1 @@ -568,12 +689,15 @@ public func /=(inout size: CGSize, tuple: (CGFloat, CGFloat)) { public func +(rect: CGRect, point: CGPoint) -> CGRect { return CGRect(origin: rect.origin + point, size: rect.size) } + public func +=(inout rect: CGRect, point: CGPoint) { rect.origin += point } + public func -(rect: CGRect, point: CGPoint) -> CGRect { return CGRect(origin: rect.origin - point, size: rect.size) } + public func -=(inout rect: CGRect, point: CGPoint) { rect.origin -= point } @@ -581,12 +705,15 @@ public func -=(inout rect: CGRect, point: CGPoint) { public func +(rect: CGRect, size: CGSize) -> CGRect { return CGRect(origin: rect.origin, size: rect.size + size) } + public func +=(inout rect: CGRect, size: CGSize) { rect.size += size } + public func -(rect: CGRect, size: CGSize) -> CGRect { return CGRect(origin: rect.origin, size: rect.size - size) } + public func -=(inout rect: CGRect, size: CGSize) { rect.size -= size } @@ -594,18 +721,23 @@ public func -=(inout rect: CGRect, size: CGSize) { public func *(point: CGPoint, transform: CGAffineTransform) -> CGPoint { return CGPointApplyAffineTransform(point, transform) } + public func *=(inout point: CGPoint, transform: CGAffineTransform) { point = CGPointApplyAffineTransform(point, transform) } + public func *(size: CGSize, transform: CGAffineTransform) -> CGSize { return CGSizeApplyAffineTransform(size, transform) } + public func *=(inout size: CGSize, transform: CGAffineTransform) { size = CGSizeApplyAffineTransform(size, transform) } + public func *(rect: CGRect, transform: CGAffineTransform) -> CGRect { return CGRectApplyAffineTransform(rect, transform) } + public func *=(inout rect: CGRect, transform: CGAffineTransform) { rect = CGRectApplyAffineTransform(rect, transform) } @@ -613,6 +745,7 @@ public func *=(inout rect: CGRect, transform: CGAffineTransform) { public func *(t1: CGAffineTransform, t2: CGAffineTransform) -> CGAffineTransform { return CGAffineTransformConcat(t1, t2) } + public func *=(inout t1: CGAffineTransform, t2: CGAffineTransform) { t1 = CGAffineTransformConcat(t1, t2) } diff --git a/CGRectExtensionsTests/SizesTests.swift b/CGRectExtensionsTests/SizesTests.swift index fc74c09..d437a43 100644 --- a/CGRectExtensionsTests/SizesTests.swift +++ b/CGRectExtensionsTests/SizesTests.swift @@ -1,6 +1,55 @@ import CGRectExtensions import XCTest +class PinningTests: XCTestCase { + + let srcRect = CGRect(x: 0, y: 0, width: 100, height: 200) + + let controlPoints: [CGRectControlPoint] = [ + .MinXMinY, + .CenterXMinY, + .MaxXMinY, + .MaxXCenterY, + .MaxXMaxY, + .CenterXMaxY, + .MinXMaxY, + .MinXCenterY, + .CenterXCenterY, + ] + + func testPinningDistinctControlPoints() { + for srcCPoint in controlPoints { + for dstCPoint in controlPoints { + var dstRect = srcRect.rectByPinning(CGSize(width: 5, height: 8), controlPoint: dstCPoint, toTargetControlPoint: srcCPoint) + XCTAssertEqual( + dstRect.pointOfControlPoint(dstCPoint), + srcRect.pointOfControlPoint(srcCPoint) + ) + } + } + } + + func testControlPointCGPoint() { + let expectedPoints: [(CGRectControlPoint, CGPoint)] = [ + (.MinXMinY, CGPoint(0, 0)), + (.CenterXMinY, CGPoint(50, 0)), + (.MaxXMinY, CGPoint(100, 0)), + (.MaxXCenterY, CGPoint(100, 100)), + (.MaxXMaxY, CGPoint(100, 200)), + (.CenterXMaxY, CGPoint(50, 200)), + (.MinXMaxY, CGPoint(0, 200)), + (.MinXCenterY, CGPoint(0, 100)), + (.CenterXCenterY, CGPoint(50, 100)), + ] + for (cPoint, cgPoint) in expectedPoints { + XCTAssertEqual( + srcRect.pointOfControlPoint(cPoint), + cgPoint + ) + } + } +} + class SizesTests: XCTestCase { let rect = CGRect(x: 1, y: 2, width: 10, height: 20) @@ -37,40 +86,27 @@ class SizesTests: XCTestCase { func testCenteringAtMinXMinY() { XCTAssertEqual( - rect.rectByAligning(CGSize(width: 5, height: 8), corner: .MinXEdge, .MinYEdge), + rect.rectByPinning(CGSize(width: 5, height: 8), controlPoints: .MinXMinY), CGRect(x: 1, y: 2, width: 5, height: 8)) } func testCenteringAtMaxXMinY() { XCTAssertEqual( - rect.rectByAligning(CGSize(width: 5, height: 8), corner: .MaxXEdge, .MinYEdge), + rect.rectByPinning(CGSize(width: 5, height: 8), controlPoints: .MaxXMinY), CGRect(x: 6, y: 2, width: 5, height: 8)) } func testCenteringAtMinXMaxY() { XCTAssertEqual( - rect.rectByAligning(CGSize(width: 5, height: 8), corner: .MinXEdge, .MaxYEdge), + rect.rectByPinning(CGSize(width: 5, height: 8), controlPoints: .MinXMaxY), CGRect(x: 1, y: 14, width: 5, height: 8)) } func testCenteringAtMaxXMaxY() { XCTAssertEqual( - rect.rectByAligning(CGSize(width: 5, height: 8), corner: .MaxXEdge, .MaxYEdge), + rect.rectByPinning(CGSize(width: 5, height: 8), controlPoints: .MaxXMaxY), CGRect(x: 6, y: 14, width: 5, height: 8)) } - - func testCenteringPermutation() { - let combinations: [(CGRectEdge, CGRectEdge)] = [ - (.MinXEdge, .MinYEdge), (.MaxXEdge, .MinYEdge), - (.MinXEdge, .MaxYEdge), (.MaxXEdge, .MaxYEdge) - ] - for (edge1, edge2) in combinations { - let size = CGSize(width: 5, height: 8) - XCTAssertEqual( - rect.rectByAligning(size, corner: edge1, edge2), - rect.rectByAligning(size, corner: edge2, edge1)) - } - } } class MutatingSizesTests: XCTestCase { @@ -103,37 +139,23 @@ class MutatingSizesTests: XCTestCase { } func testCenteringAtMinXMinY() { - rect.setSizeAligned(CGSize(width: 5, height: 8), corner: .MinXEdge, .MinYEdge) + rect.setSizeWhilePinning(CGSize(width: 5, height: 8), controlPoints: .MinXMinY) XCTAssertEqual(rect, CGRect(x: 1, y: 2, width: 5, height: 8)) } func testCenteringAtMaxXMinY() { - rect.setSizeAligned(CGSize(width: 5, height: 8), corner: .MaxXEdge, .MinYEdge) + rect.setSizeWhilePinning(CGSize(width: 5, height: 8), controlPoints: .MaxXMinY) XCTAssertEqual(rect, CGRect(x: 6, y: 2, width: 5, height: 8)) } func testCenteringAtMinXMaxY() { - rect.setSizeAligned(CGSize(width: 5, height: 8), corner: .MinXEdge, .MaxYEdge) + rect.setSizeWhilePinning(CGSize(width: 5, height: 8), controlPoints: .MinXMaxY) XCTAssertEqual(rect, CGRect(x: 1, y: 14, width: 5, height: 8)) } func testCenteringAtMaxXMaxY() { - rect.setSizeAligned(CGSize(width: 5, height: 8), corner: .MaxXEdge, .MaxYEdge) + rect.setSizeWhilePinning(CGSize(width: 5, height: 8), controlPoints: .MaxXMaxY) XCTAssertEqual(rect, CGRect(x: 6, y: 14, width: 5, height: 8)) } - func testCenteringPermutation() { - let combinations: [(CGRectEdge, CGRectEdge)] = [ - (.MinXEdge, .MinYEdge), (.MaxXEdge, .MinYEdge), - (.MinXEdge, .MaxYEdge), (.MaxXEdge, .MaxYEdge) - ] - let size = CGSize(width: 5, height: 8) - for (edge1, edge2) in combinations { - var rect1 = rect - var rect2 = rect - rect1.setSizeAligned(size, corner: edge1, edge2) - rect2.setSizeAligned(size, corner: edge2, edge1) - XCTAssertEqual(rect1, rect2) - } - } } diff --git a/README.md b/README.md index 1500053..91a1c5e 100644 --- a/README.md +++ b/README.md @@ -3,78 +3,8 @@ Swift CGRect Extensions A collection of `CGRect`, `CGPoint` and `CGSize` convenience functions for Swift on OS X and iOS. -The goal is to provide clear functions for the most commonly used rect operations, even if the code savings are minimal. The reason is that function names communicate the programmer's intent more clearly than setting properties with ad-hoc calculations. +This is a fork of the main repo. I've introduced some convenient changes that make layout of CGRects simple and intuitive, see below. Until @nschum pulls my changes, I'll keep this as a separate project. +See main repo for more documentation. -Pull requests and suggestions are welcome. - -Features --------- - -- Operators for adding, scaling sizes, applying `CGAffineTransform` -- Shortcut properties for corner and center points -- Offsetting, insetting and extending methods with multiple ways of specifying arguments -- Aligning and centering sizes in rects -- Copying rects with some values changed without going through a variable -- Respects the different coordinate systems on iOS and OS X -- Unit tested - -Integration ------------ - -For now I suggest simply adding `CGRectExtensions.swift` to your project. (Once CocoaPods supports Swift, a spec will be available.) - -Examples --------- - -**These examples are also available as a Playground in the Xcode project.** - -```swift -import Foundation -import CGRectExtensions - -let rect = CGRect(1, 2, 100, 200) // shorter constructor - -let minY = rect.minY // shortcut properties - -let topCenter = rect.topCenter // OS-dependent coordinate system - -let oppositeOrigin = rect.origin + rect.size // adding - -let corneredRect = rect.with(x: 0, y: 0) // modified copy - -let nextPageRect = rect.rectByOffsetting(dx: 100) // offsetting - -let paddedRect = rect.rectByInsetting(top: 66, left: 10, right: 10) // insetting - -let quarterSize = rect.size * 0.5 // scaling sizes -let corner = rect.rectByAligning(quarterSize, corner: .MinXEdge, .MinYEdge) // aligning sizes - -let halfWidthSize = rect.size * (0.5, 1) // scaling sizes -let centeredRect = rect.rectByCentering(halfWidthSize) // centering sizes - -let scaledRect = rect * CGAffineTransformMakeScale(2.0, 3.0) -``` - -Mutating functions are also available. These are especially convenient since Swift allows modifying a `struct` property directly. - -```swift -import Cocoa -import CGRectExtensions - -let view = NSView() -view.frame.size = CGSize(width: 100, height: 200) - -view.frame.bottomLeft = CGPoint(x: 1, y: 2) - -view.frame.offset(25, 25) - -view.frame.inset(top: 66, left: 10, right: 10) - -view.frame.setSizeCentered(CGSize(50, 50)) - -view.frame.setSizeCentered(CGSize(50, 50), alignTo: .MinXEdge) - -view.frame.setSizeAligned(CGSize(50, 50), corner: .MinXEdge, .MinYEdge) - -view.frame *= CGAffineTransformMakeScale(2.0, 3.0) -``` +Introducing CGRectControlPoint that lets you refer to convenient point on your CGRect. This extension then lets you manipulate these control points and pin them together. This presents a quick and readable way to preform manual layout. +See code for more documentation.