Skip to content

Instantly share code, notes, and snippets.

@marksands
Created December 12, 2019 14:36
Show Gist options
  • Save marksands/d3730a655d80735354184abad1ddfdb9 to your computer and use it in GitHub Desktop.
Save marksands/d3730a655d80735354184abad1ddfdb9 to your computer and use it in GitHub Desktop.
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)
}
}
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)
}
}
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))
}
}
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