Created
December 12, 2019 14:36
-
-
Save marksands/d3730a655d80735354184abad1ddfdb9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
extension CGRect { | |
public static var unbounded: CGRect { | |
return CGRect(origin: .zero, size: .unbounded) | |
} | |
public var center: CGPoint { | |
return CGPoint(x: midX, y: midY) | |
} | |
public init(x: CGFloat, y: CGFloat, size: CGSize) { | |
self.init(x: x, y: y, width: size.width, height: size.height) | |
} | |
public init(origin: CGPoint, width: CGFloat, height: CGFloat) { | |
self.init(x: origin.x, y: origin.y, width: width, height: height) | |
} | |
public func with(width value: CGFloat) -> CGRect { | |
return CGRect(x: minX, y: minY, width: value, height: height) | |
} | |
public func with(height value: CGFloat) -> CGRect { | |
return CGRect(x: minX, y: minY, width: width, height: value) | |
} | |
public func with(x value: CGFloat) -> CGRect { | |
return CGRect(x: value, y: minY, width: width, height: height) | |
} | |
public func with(y value: CGFloat) -> CGRect { | |
return CGRect(x: minX, y: value, width: width, height: height) | |
} | |
public func insetBy(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect { | |
return inset(by: UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)) | |
} | |
public func aligned(strategy: ScreenAligner.Strategy = .ceil) -> CGRect { | |
return ScreenAligner(screen: UIScreen.main).align(self, strategy: strategy) | |
} | |
public func dividedIntoRects(count: Int, from edge: CGRectEdge) -> [CGRect] { | |
var copy = self | |
let dimension = self.dimension(for: edge) / CGFloat(count) | |
return (1...count).map({ _ in | |
let (slice, remainder) = copy.divided(atDistance: dimension, from: edge) | |
copy = remainder | |
return slice | |
}) | |
} | |
public func dimension(for edge: CGRectEdge) -> CGFloat { | |
if edge == .minYEdge || edge == .maxYEdge { | |
return self.height | |
} else { | |
return self.width | |
} | |
} | |
public func expanded(_ amount: CGFloat, edge: CGRectEdge) -> CGRect { | |
switch edge { | |
case .minXEdge: | |
return CGRect(x: minX - amount, y: minY, width: width + amount, height: height) | |
case .maxXEdge: | |
return CGRect(x: minX, y: minY, width: width + amount, height: height) | |
case .minYEdge: | |
return CGRect(x: minX, y: minY - amount, width: width, height: height + amount) | |
case .maxYEdge: | |
return CGRect(x: minX, y: minY, width: width, height: height + amount) | |
} | |
} | |
public func divided(atDistance distance: CGFloat, from edge: CGRectEdge, padding: CGFloat) -> (slice: CGRect, remainder: CGRect) { | |
let (slice, remainder) = divided(atDistance: distance, from: edge) | |
return (slice, remainder.divided(atDistance: padding, from: edge).remainder) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
extension CGSize { | |
public static var unbounded: CGSize { | |
return CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) | |
} | |
public func insetBy(dx: CGFloat, dy: CGFloat) -> CGSize { | |
return CGSize(width: width - dx * 2, height: height - dy * 2) | |
} | |
public var rect: CGRect { | |
return CGRect(origin: .zero, size: self) | |
} | |
public func with(width: CGFloat) -> CGSize { | |
return CGSize(width: width, height: self.height) | |
} | |
public func with(height: CGFloat) -> CGSize { | |
return CGSize(width: self.width, height: height) | |
} | |
public func rect(with origin: CGPoint) -> CGRect { | |
return CGRect(origin: origin, size: self) | |
} | |
public func with(additionalHeight: CGFloat) -> CGSize { | |
return CGSize(width: width, height: height + additionalHeight) | |
} | |
public func centered(in rect: CGRect) -> CGRect { | |
let centeredPoint = CGPoint(x: rect.minX + (rect.width - width) / 2, y: rect.minY + (rect.height - height) / 2) | |
let point = CGPoint(x: centeredPoint.x, y: centeredPoint.y) | |
return CGRect(origin: point, size: self) | |
} | |
public func centered(in rect: CGRect, top: CGFloat, right: CGFloat) -> CGRect { | |
let originX = rect.maxX - width - right | |
let originY = rect.minY + top | |
var rect = centered(in: rect) | |
rect.origin.x = originX | |
rect.origin.y = originY | |
return rect | |
} | |
public func centered(in rect: CGRect, top: CGFloat, left: CGFloat) -> CGRect { | |
let originX = rect.minX + left | |
let originY = rect.minY + top | |
var rect = centered(in: rect) | |
rect.origin.x = originX | |
rect.origin.y = originY | |
return rect | |
} | |
public func centered(in rect: CGRect, bottom: CGFloat, left: CGFloat) -> CGRect { | |
let originX = rect.minX + left | |
let originY = rect.maxY - height - bottom | |
var rect = centered(in: rect) | |
rect.origin.x = originX | |
rect.origin.y = originY | |
return rect | |
} | |
public func centeredHorizontally(in rect: CGRect, top: CGFloat) -> CGRect { | |
let originY = rect.minY + top | |
var rect = centered(in: rect) | |
rect.origin.y = originY | |
return rect | |
} | |
public func centeredHorizontally(in rect: CGRect, bottom: CGFloat) -> CGRect { | |
let originY = rect.maxY - height - bottom | |
var rect = centered(in: rect) | |
rect.origin.y = originY | |
return rect | |
} | |
public func centeredVertically(in rect: CGRect, left: CGFloat) -> CGRect { | |
let originX = rect.minX + left | |
var rect = centered(in: rect) | |
rect.origin.x = originX | |
return rect | |
} | |
public func centeredVertically(in rect: CGRect, right: CGFloat) -> CGRect { | |
let originX = rect.maxX - width - right | |
var rect = centered(in: rect) | |
rect.origin.x = originX | |
return rect | |
} | |
public func rounded(screenAligner: ScreenAligner = ScreenAligner(screen: .main), strategy: ScreenAligner.Strategy = .ceil) -> CGSize { | |
return screenAligner.align(self, strategy: strategy) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
public protocol Roundable { | |
func floorToPixel(scale: CGFloat) -> Self | |
func roundToPixel(scale: CGFloat) -> Self | |
func ceilToPixel(scale: CGFloat) -> Self | |
} | |
extension CGFloat: Roundable { | |
public func floorToPixel(scale: CGFloat) -> CGFloat { | |
return floor(self * scale) / scale | |
} | |
public func roundToPixel(scale: CGFloat) -> CGFloat { | |
return CoreGraphics.round(self * scale) / scale | |
} | |
public func ceilToPixel(scale: CGFloat) -> CGFloat { | |
return ceil(self * scale) / scale | |
} | |
} | |
extension CGPoint: Roundable { | |
public func floorToPixel(scale: CGFloat) -> CGPoint { | |
return CGPoint(x: x.floorToPixel(scale: scale), y: y.floorToPixel(scale: scale)) | |
} | |
public func roundToPixel(scale: CGFloat) -> CGPoint { | |
return CGPoint(x: x.roundToPixel(scale: scale), y: y.roundToPixel(scale: scale)) | |
} | |
public func ceilToPixel(scale: CGFloat) -> CGPoint { | |
return CGPoint(x: x.ceilToPixel(scale: scale), y: y.ceilToPixel(scale: scale)) | |
} | |
} | |
extension CGSize: Roundable { | |
public func floorToPixel(scale: CGFloat) -> CGSize { | |
return CGSize(width: width.floorToPixel(scale: scale), height: height.floorToPixel(scale: scale)) | |
} | |
public func roundToPixel(scale: CGFloat) -> CGSize { | |
return CGSize(width: width.roundToPixel(scale: scale), height: height.roundToPixel(scale: scale)) | |
} | |
public func ceilToPixel(scale: CGFloat) -> CGSize { | |
return CGSize(width: width.ceilToPixel(scale: scale), height: height.ceilToPixel(scale: scale)) | |
} | |
} | |
extension CGRect: Roundable { | |
public func floorToPixel(scale: CGFloat) -> CGRect { | |
return CGRect(origin: origin.floorToPixel(scale: scale), size: size.floorToPixel(scale: scale)) | |
} | |
public func roundToPixel(scale: CGFloat) -> CGRect { | |
return CGRect(origin: origin.roundToPixel(scale: scale), size: size.roundToPixel(scale: scale)) | |
} | |
public func ceilToPixel(scale: CGFloat) -> CGRect { | |
return CGRect(origin: origin.ceilToPixel(scale: scale), size: size.ceilToPixel(scale: scale)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
public struct ScreenAligner { | |
public enum Strategy { | |
case floor | |
case ceil | |
case round | |
} | |
public let scale: CGFloat | |
public init(window: UIWindow) { | |
self.scale = window.screen.scale | |
} | |
public init(screen: UIScreen = UIScreen.main) { | |
self.scale = screen.scale | |
} | |
public init(view: UIView) { | |
self.scale = view.window?.screen.scale ?? UIScreen.main.scale | |
} | |
public func align<R: Roundable>(_ value: R, strategy: Strategy = .round) -> R { | |
switch strategy { | |
case .floor: return value.floorToPixel(scale: scale) | |
case .round: return value.roundToPixel(scale: scale) | |
case .ceil: return value.ceilToPixel(scale: scale) | |
} | |
} | |
public func align(_ value: Double, strategy: Strategy = .round) -> CGFloat { | |
return align(CGFloat(value), strategy: strategy) | |
} | |
public func align(_ value: Float, strategy: Strategy = .round) -> CGFloat { | |
return align(CGFloat(value), strategy: strategy) | |
} | |
public func align(_ value: Int, strategy: Strategy = .round) -> CGFloat { | |
return align(CGFloat(value), strategy: strategy) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment