deep-linking-sample/Apps/Wikipedia/WMF Framework/UIView+SemanticContent.swift
Javier Cicchelli 9bcdaa697b [Setup] Basic project structure (#1)
This PR contains all the work related to setting up this project as required to implement the [Assignment](https://repo.rock-n-code.com/rock-n-code/deep-linking-assignment/wiki/Assignment) on top, as intended.

To summarise this work:
- [x] created a new **Xcode** project;
- [x] cloned the `Wikipedia` app and inserted it into the **Xcode** project;
- [x] created the `Locations` app and also, its `Libraries` package;
- [x] created the `Shared` package to share dependencies between the apps;
- [x] added a `Makefile` file and implemented some **environment** and **help** commands.

Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Reviewed-on: rock-n-code/deep-linking-assignment#1
2023-04-08 18:37:13 +00:00

163 lines
8.6 KiB
Swift

@objc public enum HorizontalAlignment : Int {
case center
case left
case right
}
@objc public enum VerticalAlignment: Int {
case center
case top
case bottom
}
public let NoIntrinsicSize = CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric)
extension UIView {
@objc public func wmf_sizeThatFits(_ size: CGSize) -> CGSize {
return sizeThatFits(size)
}
@objc @discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumSize: CGSize = NoIntrinsicSize, minimumSize: CGSize = NoIntrinsicSize, horizontalAlignment: HorizontalAlignment = .center, verticalAlignment: VerticalAlignment = .center, apply: Bool = false) -> CGRect {
let viewSize: CGSize = wmf_sizeThatFits(maximumSize)
var x: CGFloat = point.x
var y: CGFloat = point.y
let viewWidth: CGFloat
let widthToFit: CGFloat
if minimumSize.width != UIView.noIntrinsicMetric && maximumSize.width != UIView.noIntrinsicMetric { // max and min defined
viewWidth = max(min(maximumSize.width, viewSize.width), minimumSize.width)
widthToFit = maximumSize.width
} else if minimumSize.width != UIView.noIntrinsicMetric && maximumSize.width == UIView.noIntrinsicMetric { // only min defined
viewWidth = max(minimumSize.width, viewSize.width)
widthToFit = viewWidth
} else if minimumSize.width == UIView.noIntrinsicMetric && maximumSize.width != UIView.noIntrinsicMetric { // only max defined
viewWidth = min(maximumSize.width, viewSize.width)
widthToFit = maximumSize.width
} else { // neither defined
viewWidth = viewSize.width
widthToFit = viewWidth
}
let viewHeight: CGFloat
let heightToFit: CGFloat
if minimumSize.height != UIView.noIntrinsicMetric && maximumSize.height != UIView.noIntrinsicMetric { // max and min defined
viewHeight = max(min(maximumSize.height, viewSize.height), minimumSize.height)
heightToFit = maximumSize.height
} else if minimumSize.height != UIView.noIntrinsicMetric && maximumSize.height == UIView.noIntrinsicMetric { // only min defined
viewHeight = max(minimumSize.height, viewSize.height)
heightToFit = viewHeight
} else if minimumSize.height == UIView.noIntrinsicMetric && maximumSize.height != UIView.noIntrinsicMetric { // only max defined
viewHeight = min(maximumSize.height, viewSize.height)
heightToFit = maximumSize.height
} else { // neither defined
viewHeight = viewSize.height
heightToFit = viewHeight
}
switch verticalAlignment {
case .center:
y += floor(0.5*heightToFit - 0.5*viewHeight)
case .bottom:
y += (heightToFit - viewHeight)
case .top:
break
}
switch horizontalAlignment {
case .center:
x += floor(0.5*widthToFit - 0.5*viewWidth)
case .right:
x += (widthToFit - viewWidth)
case .left:
break
}
let fitFrame = CGRect(x: round(x), y: round(y), width: ceil(viewWidth), height: ceil(viewHeight))
if apply {
frame = fitFrame
}
return fitFrame
}
@discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumSize: CGSize = NoIntrinsicSize, minimumSize: CGSize = NoIntrinsicSize, horizontalAlignment: HorizontalAlignment, apply: Bool) -> CGRect {
return wmf_preferredFrame(at: point, maximumSize: maximumSize, minimumSize: minimumSize, horizontalAlignment: horizontalAlignment, verticalAlignment: .top, apply: apply)
}
@discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumSize: CGSize, minimumSize: CGSize = NoIntrinsicSize, alignedBy semanticContentAttribute: UISemanticContentAttribute, apply: Bool) -> CGRect {
let horizontalAlignment: HorizontalAlignment = semanticContentAttribute == .forceRightToLeft ? .right : .left
return wmf_preferredFrame(at: point, maximumSize: maximumSize, minimumSize: minimumSize, horizontalAlignment: horizontalAlignment, apply: apply)
}
@discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumWidth: CGFloat, minimumWidth: CGFloat = UIView.noIntrinsicMetric, horizontalAlignment: HorizontalAlignment, apply: Bool) -> CGRect {
let minimumSize = CGSize(width: minimumWidth, height: UIView.noIntrinsicMetric)
let maximumSize = CGSize(width: maximumWidth, height: UIView.noIntrinsicMetric)
return wmf_preferredFrame(at: point, maximumSize: maximumSize, minimumSize: minimumSize, horizontalAlignment: horizontalAlignment, apply: apply)
}
@discardableResult public func wmf_preferredFrame(at point: CGPoint, maximumWidth: CGFloat, minimumWidth: CGFloat = UIView.noIntrinsicMetric, alignedBy semanticContentAttribute: UISemanticContentAttribute, apply: Bool) -> CGRect {
let horizontalAlignment: HorizontalAlignment = semanticContentAttribute == .forceRightToLeft ? .right : .left
return wmf_preferredFrame(at: point, maximumWidth: maximumWidth, minimumWidth: minimumWidth, horizontalAlignment: horizontalAlignment, apply: apply)
}
@discardableResult public func wmf_preferredHeight(at point: CGPoint, maximumWidth: CGFloat, minimumWidth: CGFloat = UIView.noIntrinsicMetric, alignedBy semanticContentAttribute: UISemanticContentAttribute, spacing: CGFloat, apply: Bool) -> CGFloat {
return wmf_preferredFrame(at: point, maximumWidth: maximumWidth, minimumWidth: minimumWidth, alignedBy: semanticContentAttribute, apply: apply).layoutHeight(with: spacing)
}
@discardableResult public func wmf_preferredHeight(at point: CGPoint, maximumWidth: CGFloat, minimumWidth: CGFloat = UIView.noIntrinsicMetric, horizontalAlignment: HorizontalAlignment, spacing: CGFloat, apply: Bool) -> CGFloat {
return wmf_preferredFrame(at: point, maximumWidth: maximumWidth, minimumWidth: minimumWidth, horizontalAlignment: horizontalAlignment, apply: apply).layoutHeight(with: spacing)
}
}
extension UIButton {
public override func wmf_sizeThatFits(_ maximumSize: CGSize) -> CGSize {
var buttonAdjustedSize = maximumSize
var heightAdjustment = contentEdgeInsets.top + contentEdgeInsets.bottom
var widthAdjustment = contentEdgeInsets.left + contentEdgeInsets.right
var imageHeight: CGFloat = 0
if let image = image(for: .normal) {
widthAdjustment += imageEdgeInsets.left + imageEdgeInsets.right + image.size.width
imageHeight = image.size.height + imageEdgeInsets.top + imageEdgeInsets.bottom + contentEdgeInsets.top + contentEdgeInsets.bottom
}
heightAdjustment += titleEdgeInsets.top + titleEdgeInsets.bottom
widthAdjustment += titleEdgeInsets.left + titleEdgeInsets.right
if buttonAdjustedSize.width != UIView.noIntrinsicMetric {
buttonAdjustedSize.width = buttonAdjustedSize.width - widthAdjustment
}
if buttonAdjustedSize.height != UIView.noIntrinsicMetric {
buttonAdjustedSize.height = buttonAdjustedSize.height - heightAdjustment
}
let buttonLabelSize: CGSize
if let titleLabel = titleLabel {
buttonLabelSize = titleLabel.sizeThatFits(buttonAdjustedSize)
} else {
buttonLabelSize = .zero
}
let maxHeight = max(imageHeight, buttonLabelSize.height + heightAdjustment)
return CGSize(width: buttonLabelSize.width + widthAdjustment, height: maxHeight)
}
}
extension AlignedImageButton {
@discardableResult override public func wmf_preferredFrame(at point: CGPoint, maximumSize: CGSize = NoIntrinsicSize, minimumSize: CGSize = NoIntrinsicSize, horizontalAlignment: HorizontalAlignment = .center, verticalAlignment: VerticalAlignment = .center, apply: Bool = false) -> CGRect {
let adjustedPoint = CGPoint(x: point.x - leftPadding, y: point.y - verticalPadding)
var adjustedSize = maximumSize
if adjustedSize.width != UIView.noIntrinsicMetric {
adjustedSize.width = adjustedSize.width + leftPadding + rightPadding
}
if adjustedSize.height != UIView.noIntrinsicMetric {
adjustedSize.height = adjustedSize.height + 2 * verticalPadding
}
return super.wmf_preferredFrame(at: adjustedPoint, maximumSize: adjustedSize, minimumSize: minimumSize, horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment, apply: apply)
}
}