Jxrgxn
2/5/2016 - 10:41 AM

CGRect Alignment Extension

CGRect Alignment Extension

//
//  CGGeometry+Additions.swift
//  TokenField
//
//  Created by Cody Robertson on 2/4/16.
//  Copyright © 2016 Notion AI, Inc. All rights reserved.
//

import Foundation
import UIKit

public extension CGRect {
    
    public enum Edge {
        case Bottom
        case Left
        case Right
        case Top
        
        func toCGRectEdge() -> CGRectEdge {
            switch self {
            case .Bottom: return .MaxYEdge
            case .Left: return .MinXEdge
            case .Right: return .MaxXEdge
            case .Top: return .MinYEdge
            }
        }
    }
    
    /**
     This method creates a new CGRect by strecting the specified `edge` to align with the `toEdge`.
     
     The result can end up with a negative width/height.
     
     - Note: This method DOES mutate the size of the rect. 
     - SeeAlso: `algin(edge:toEdge:ofRect:withOffset:)`
     */
    public func pin(edge: Edge, toEdge: Edge, ofRect rect: CGRect, withOffset offset: CGFloat = 0) -> CGRect {
        switch (edge, toEdge) {
        case (.Left, .Left):
            return CGRect(x: rect.minX - offset, y: minY, width: width + (minX - rect.minX) + offset, height: height)
        case (.Left, .Right):
            return CGRect(x: rect.maxX + offset, y: minY, width: width + (minX - rect.maxX) - offset, height: height)
        case (.Top, .Top):
            return CGRect(x: minX, y: rect.minY - offset, width: width, height: height + (minY - rect.minY) + offset)
        case (.Top, .Bottom):
            return CGRect(x: minX, y: rect.maxY + offset, width: width, height: height + (minY - rect.maxY) - offset)
        case (.Right, .Left):
            return CGRect(x: rect.minX, y: minY, width: width - (maxX - rect.minX) + offset, height: height)
        case (.Right, .Right):
            return CGRect(x: minX, y: minY, width: width - (maxX - rect.maxX) - offset, height: height)
        case (.Bottom, .Top):
            return CGRect(x: minX, y: minY, width: width, height: height - (maxY - rect.minY) + offset)
        case (.Bottom, .Bottom):
            return CGRect(x: minX, y: minY, width: width, height: height - (maxY - rect.maxY) - offset)
        default: 
            preconditionFailure("Cannot align to this combination of edges")
        }
    }
    
    /**
     This method creates a new CGRect by aligning the specified `edge` to the `toEdge`.
     
     - SeeAlso: `pin(edge:toEdge:ofRect:withOffset:)`
     */
    public func align(edge: Edge, toEdge: Edge, ofRect rect: CGRect, withOffset offset: CGFloat = 0) -> CGRect {
        return CGRect(origin: alignOrigin(edge, toEdge: toEdge, ofRect: rect, withOffset: offset), size: size)
    }
    
    private func alignOrigin(edge: Edge, toEdge: Edge, ofRect rect: CGRect, withOffset offset: CGFloat) -> CGPoint {
        switch (edge, toEdge) {
        case (.Left, .Left):
            return CGPoint(x: rect.minX + offset, y: minY)
        case (.Left, .Right):
            return CGPoint(x: rect.maxX + offset, y: minY)
        case (.Top, .Top):
            return CGPoint(x: minX, y: rect.minY + offset)
        case (.Top, .Bottom):
            return CGPoint(x: minX, y: rect.maxY + offset)
        case (.Right, .Left):
            return CGPoint(x: rect.minX - width - offset, y: minY)
        case (.Right, .Right):
            return CGPoint(x: rect.maxX - width - offset, y: minY)
        case (.Bottom, .Top):
            return CGPoint(x: minX, y: rect.minY - height - offset)
        case (.Bottom, .Bottom):
            return CGPoint(x: minX, y: rect.maxY - height - offset)
        default:
            preconditionFailure("Cannot align to this combination of edges")
        }
    }
    
}




extension CGRect {
    
    public enum Direction {
        case Up
        case Down
        case Left
        case Right
    }
    
    public func move(direction: Direction, amount: CGFloat) -> CGRect {
        switch direction {
        case .Up: return CGRectMake(minX, minY - amount, width, height)
        case .Down: return CGRectMake(minX, minY + amount, width, height)
        case .Left: return CGRectMake(minX - amount, minY, width, height)
        case .Right: return CGRectMake(minX + amount, minY, width, height)
        }
    }
    
    public func expand(direction: Direction, amount: CGFloat) -> CGRect {
        switch direction {
        case .Up: return CGRectMake(minX, minY - amount, width, height + amount)
        case .Down: return CGRectMake(minX, minY, width, height + amount)
        case .Left: return CGRectMake(minX - amount, minY, width + amount, height)
        case .Right: return CGRectMake(minX, minY, width + amount, height)
        }
    }
    
}




extension CGRect {
    
    public enum Dimension {
        case Height
        case Width
    }

    public func setDimension(dimension: Dimension, toSize size: CGFloat) -> CGRect {
        switch dimension {
        case .Height: return CGRectMake(minX, minY, width, size)
        case .Width: return CGRectMake(minX, minY, size, height)
        }
    }
    
}