Skip to content

Commit

Permalink
Implemented UIViewAutoresizing
Browse files Browse the repository at this point in the history
  • Loading branch information
colemancda committed Nov 18, 2017
1 parent c8a7f67 commit 7fca764
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Sources/Cacao/UITableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,15 @@ internal extension UITableViewCell {
final class SeparatorView: UIView {

var style: UITableViewCellSeparatorStyle = .none {

didSet {
setNeedsDisplay()
isHidden = style == .none
}
}

var color: UIColor? {

didSet { setNeedsDisplay() }
}

Expand Down
42 changes: 40 additions & 2 deletions Sources/Cacao/UIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ open class UIView: UIResponder {
self.frame = frame
}

// MARK: - CustomStringConvertible

open override var description: String {

return String(format: "<%@: %p; frame = %@; hidden = %@; layer = %@>", className, self, "\(self.frame)", (self.isHidden ? "YES" : "NO"), "\(self.texture ?? "nil" as Any)")
}

// MARK: - Properties

// MARK: - Configuring a View’s Visual Appearance
Expand Down Expand Up @@ -205,8 +212,10 @@ open class UIView: UIResponder {
open var frame: CGRect {
get { return _frame }
set {
let oldValue = _bounds
_frame = newValue
_bounds.size = newValue.size
boundsDidChange(from: oldValue, to: newValue)
}
}
private var _frame = CGRect()
Expand All @@ -229,12 +238,28 @@ open class UIView: UIResponder {
public final var bounds: CGRect {
get { return _bounds }
set {
let oldValue = _bounds
_bounds = newValue
_frame.size = newValue.size
boundsDidChange(from: oldValue, to: newValue)
}
}
private var _bounds = CGRect()

private func boundsDidChange(from oldBounds: CGRect, to newBounds: CGRect) {

// bounds changed
guard oldBounds != newBounds else { return }

setNeedsLayout()

// Autoresize subviews
if autoresizesSubviews, oldBounds.size != newBounds.size {

subviews.forEach { $0.frame.resize($0.autoresizingMask, containerSize: (oldBounds.size, newBounds.size)) }
}
}

/// The center of the frame.
///
/// The center is specified within the coordinate system of its superview and is measured in points.
Expand Down Expand Up @@ -472,7 +497,7 @@ open class UIView: UIResponder {
/// That method performs any needed calculations and returns them to this method, which then makes the change.
public final func sizeToFit() {

// TODO
// TODO: sizeToFit
}

/// The natural size for the receiving view, considering only properties of the view itself.
Expand All @@ -491,6 +516,17 @@ open class UIView: UIResponder {
return CGSize(width: UIViewNoIntrinsicMetric, height: UIViewNoIntrinsicMetric)
}

/// A Boolean value that determines whether the receiver automatically resizes its subviews when its bounds change.
///
/// When set to `true`, the receiver adjusts the size of its subviews when its bounds change.
/// The default value is `true`.
public var autoresizesSubviews: Bool = true

/// An integer bit mask that determines how the receiver resizes itself when its superview’s bounds change.
///
/// When a view’s bounds change, that view automatically resizes its subviews according to each subview’s autoresizing mask.
public var autoresizingMask: UIViewAutoresizing = []

// MARK: - Identifying the View at Runtime

/// An integer that you can use to identify view objects in your application.
Expand Down Expand Up @@ -690,7 +726,9 @@ open class UIView: UIResponder {

/// Lays out subviews.
///
/// The default implementation of this method does nothing.
/// If contraints are availible, the default implementation uses any constraints you have set
/// to determine the size and position of any subviews.
/// Otherwise, the default implementation of this method does nothing.
///
/// Subclasses can override this method as needed to perform more precise layout of their subviews.
/// You should override this method only if the autoresizing and constraint-based behaviors of
Expand Down
101 changes: 101 additions & 0 deletions Sources/Cacao/UIViewAutoresizing.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//
// UIViewAutoresizing.swift
// Cacao
//
// Created by Alsey Coleman Miller on 11/18/17.
//

import Foundation

/// Options for automatic view resizing.
public struct UIViewAutoresizing : OptionSet {

public let rawValue: Int

public init(rawValue: Int) {
self.rawValue = rawValue
}

/// Options for automatic view resizing.
public static let none = UIViewAutoresizing(rawValue: 0)
public static let flexibleLeftMargin = UIViewAutoresizing(rawValue: 1 << 0)
public static let flexibleWidth = UIViewAutoresizing(rawValue: 1 << 1)
public static let flexibleRightMargin = UIViewAutoresizing(rawValue: 1 << 2)
public static let flexibleTopMargin = UIViewAutoresizing(rawValue: 1 << 3)
public static let flexibleHeight = UIViewAutoresizing(rawValue: 1 << 4)
public static let flexibleBottomMargin = UIViewAutoresizing(rawValue: 1 << 5)
}

// MARK: - Internal Extension

internal extension CGRect {

mutating func resize(_ autoresizingMask: UIViewAutoresizing, containerSize: (old: CGSize, new: CGSize)) {

self = resized(autoresizingMask, containerSize: containerSize)
}

func resized(_ autoresizingMask: UIViewAutoresizing, containerSize: (old: CGSize, new: CGSize)) -> CGRect {

guard autoresizingMask.isEmpty == false else { return self }

let oldSize = containerSize.old

let newSize = containerSize.new

var frame = self

let delta = CGSize(width: newSize.width - oldSize.width, height: newSize.height - oldSize.height)

if autoresizingMask.contains([.flexibleTopMargin, .flexibleHeight, .flexibleBottomMargin]) {
frame.origin.y = floor(frame.origin.y + (frame.origin.y / oldSize.height * delta.height))
frame.size.height = floor(frame.size.height + (frame.size.height / oldSize.height * delta.height))
}
else if autoresizingMask.contains([.flexibleTopMargin, .flexibleHeight]) {
let t: CGFloat = frame.origin.y + frame.size.height
frame.origin.y = floor(frame.origin.y + (frame.origin.y / t * delta.height))
frame.size.height = floor(frame.size.height + (frame.size.height / t * delta.height))
}
else if autoresizingMask.contains([.flexibleBottomMargin, .flexibleHeight]) {
frame.size.height = floor(frame.size.height + (frame.size.height / (oldSize.height - frame.origin.y) * delta.height))
}
else if autoresizingMask.contains([.flexibleBottomMargin, .flexibleTopMargin]) {
frame.origin.y = floor(frame.origin.y + (delta.height / 2.0))
}
else if autoresizingMask.contains(.flexibleHeight) {
frame.size.height = floor(frame.size.height + delta.height)
}
else if autoresizingMask.contains(.flexibleTopMargin) {
frame.origin.y = floor(frame.origin.y + delta.height)
}
else if autoresizingMask.contains(.flexibleBottomMargin) {
frame.origin.y = floor(frame.origin.y)
}
if autoresizingMask.contains([.flexibleLeftMargin, .flexibleWidth, .flexibleRightMargin]) {
frame.origin.x = floor(frame.origin.x + (frame.origin.x / oldSize.width * delta.width))
frame.size.width = floor(frame.size.width + (frame.size.width / oldSize.width * delta.width))
}
else if autoresizingMask.contains([.flexibleLeftMargin, .flexibleWidth]) {
let t = frame.origin.x + frame.size.width
frame.origin.x = floor(frame.origin.x + (frame.origin.x / t * delta.width))
frame.size.width = floor(frame.size.width + (frame.size.width / t * delta.width))
}
else if autoresizingMask.contains([.flexibleRightMargin, .flexibleWidth]) {
frame.size.width = floor(frame.size.width + (frame.size.width / (oldSize.width - frame.origin.x) * delta.width))
}
else if autoresizingMask.contains([.flexibleRightMargin, .flexibleLeftMargin]) {
frame.origin.x = floor(frame.origin.x + (delta.width / 2.0))
}
else if autoresizingMask.contains(.flexibleWidth) {
frame.size.width = floor(frame.size.width + delta.width)
}
else if autoresizingMask.contains(.flexibleLeftMargin) {
frame.origin.x = floor(frame.origin.x + delta.width)
}
else if autoresizingMask.contains(.flexibleRightMargin) {
frame.origin.x = floor(frame.origin.x)
}

return frame
}
}
15 changes: 12 additions & 3 deletions Sources/Cacao/UIViewContentMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ public enum UIViewContentMode: Int {
case bottomRight
}

// Cacao extension
public extension UIViewContentMode {
// MARK: - Internal Cacao Extension

internal extension UIViewContentMode {

public func rect(for bounds: CGRect, size: CGSize) -> CGRect {
func rect(for bounds: CGRect, size: CGSize) -> CGRect {

switch self {

Expand Down Expand Up @@ -176,3 +177,11 @@ public extension UIViewContentMode {
}
}
}

// MARK: - Public API extension

@_silgen_name("CacaoRectForContentMode")
public func CacaoRectForContentMode(_ contentMode: UIViewContentMode, bounds: CGRect, size: CGSize) -> CGRect {

return contentMode.rect(for: bounds, size: size)
}
2 changes: 1 addition & 1 deletion Sources/CacaoDemo/SwiftLogoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public final class SwiftLogoView: UIView {

public override func draw(_ rect: CGRect) {

let frame = contentMode.rect(for: bounds, size: intrinsicContentSize)
let frame = CacaoRectForContentMode(contentMode, bounds: bounds, size: intrinsicContentSize)

if includesText {

Expand Down

0 comments on commit 7fca764

Please sign in to comment.