-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from banjun/safearea
Safe Area Support for iPhone X
- Loading branch information
Showing
8 changed files
with
264 additions
and
14 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import Foundation | ||
|
||
extension VFL { | ||
/// decompose visual format into both side of edge connections and a middle remainder format string | ||
func edgeDecomposed(format: String) throws -> (first: (Connection, VFL.View)?, middle: String, last: (Connection, VFL.View)?) { | ||
var middle = format | ||
let vfl = try VFL(format: format) | ||
guard case .h = vfl.orientation else { return (nil, format, nil) } // only support horizontals | ||
|
||
let first = vfl.firstBound.map {($0, vfl.firstView)} | ||
let last = vfl.lastBound.map {($0, vfl.lastView)} | ||
|
||
// strip decomposed edge connections | ||
// we do not generate a format string from parsed VFL, for some reliability | ||
// instead, use a knowledge that first `[` and last `]` separate edge connections | ||
if first != nil { | ||
middle = String(middle.drop {$0 != "["}) | ||
} | ||
if last != nil { | ||
middle = String(middle.reversed().drop {$0 != "]"}.reversed()) | ||
} | ||
|
||
return (first, middle, last) | ||
} | ||
} | ||
|
||
extension VFL.SimplePredicate { | ||
func value(_ metrics: [String: CGFloat]) -> CGFloat? { | ||
switch self { | ||
case let .metricName(n): return metrics[n] | ||
case let .positiveNumber(v): return v | ||
} | ||
} | ||
} | ||
|
||
extension VFL.Constant { | ||
func value(_ metrics: [String: CGFloat]) -> CGFloat? { | ||
switch self { | ||
case let .metricName(n): return metrics[n] | ||
case let .number(v): return v | ||
} | ||
} | ||
} | ||
|
||
extension VFL.Priority { | ||
func value(_ metrics: [String: CGFloat]) -> CGFloat? { | ||
switch self { | ||
case let .metricName(n): return metrics[n] | ||
case let .number(v): return v | ||
} | ||
} | ||
} | ||
|
||
extension VFL.PredicateList { | ||
/// returns constraints: `lhs (==|<=|>=) rhs + constant` | ||
@discardableResult | ||
func constraints<T>(lhs: NSLayoutAnchor<T>, rhs: NSLayoutAnchor<T>, metrics: [String: CGFloat]) -> [NSLayoutConstraint] { | ||
let cs: [NSLayoutConstraint] | ||
switch self { | ||
case let .simplePredicate(p): | ||
guard let constant = p.value(metrics) else { return [] } | ||
cs = [lhs.constraint(equalTo: rhs, constant: constant)] | ||
case let .predicateListWithParens(predicates): | ||
cs = predicates.flatMap { p in | ||
guard case let .constant(c) = p.objectOfPredicate else { return nil } // NOTE: For the objectOfPredicate production, viewName is acceptable only if the subject of the predicate is the width or height of a view | ||
guard let constant = c.value(metrics) else { return nil } | ||
|
||
let constraint: NSLayoutConstraint | ||
switch p.relation { | ||
case .eq?, nil: | ||
constraint = lhs.constraint(equalTo: rhs, constant: constant) | ||
case .le?: | ||
constraint = lhs.constraint(lessThanOrEqualTo: rhs, constant: constant) | ||
case .ge?: | ||
constraint = lhs.constraint(greaterThanOrEqualTo: rhs, constant: constant) | ||
} | ||
_ = p.priority?.value(metrics).map {constraint.priority = LayoutPriority(rawValue: Float($0))} | ||
return constraint | ||
} | ||
} | ||
cs.forEach {$0.isActive = true} | ||
return cs | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import Foundation | ||
import FootlessParser | ||
|
||
// AST for VisualFormatLanguage | ||
// currently only needed for VFL.edgeDecomposed for Safe Area handling | ||
struct VFL { | ||
let orientation: Orientation | ||
let firstBound: Connection? | ||
let firstView: View | ||
let views: [(Connection, View)] | ||
var lastView: View {return views.last?.1 ?? firstView} | ||
let lastBound: Connection? | ||
|
||
enum Orientation {case h, v} | ||
|
||
struct View { | ||
let name: String | ||
let predicateListWithParens: [Predicate] | ||
} | ||
|
||
struct Connection { | ||
let predicateList: PredicateList | ||
} | ||
|
||
typealias ViewName = String | ||
typealias MetricName = String | ||
|
||
enum PredicateList { | ||
case simplePredicate(SimplePredicate) | ||
case predicateListWithParens([Predicate]) | ||
} | ||
|
||
enum SimplePredicate { | ||
case metricName(MetricName) | ||
case positiveNumber(CGFloat) | ||
} | ||
|
||
enum Relation {case eq, le, ge} | ||
|
||
enum ObjectOfPredicate { | ||
case constant(Constant) | ||
case viewName(ViewName) | ||
} | ||
|
||
enum Constant { | ||
case metricName(MetricName) | ||
case number(CGFloat) | ||
} | ||
|
||
enum Priority { | ||
case metricName(MetricName) | ||
case number(CGFloat) | ||
} | ||
|
||
struct Predicate { | ||
let relation: Relation? | ||
let objectOfPredicate: ObjectOfPredicate | ||
let priority: Priority? | ||
} | ||
} | ||
|
||
private let identifier = {String($0)} <^> oneOrMore(char("_") <|> alphanumeric) | ||
private let possibleNumber: Parser<Character, String> = (extend <^> optional(string("-"), otherwise: "") <*> oneOrMore(char(".") <|> digit)) | ||
private let numberParser: Parser<Character, CGFloat> = possibleNumber >>- {Double($0).map {pure(CGFloat($0))} ?? fail(.Mismatch(AnyCollection([]), "CGFloat", "not a number: \($0)"))} | ||
|
||
extension VFL.Relation { | ||
static var parser: Parser<Character, VFL.Relation> { | ||
return {_ in .eq} <^> string("==") | ||
<|> {_ in .le} <^> string("<=") | ||
<|> {_ in .ge} <^> string(">=") | ||
} | ||
} | ||
|
||
extension VFL.Priority { | ||
static var parser: Parser<Character, VFL.Priority> { | ||
return {.number($0)} <^> numberParser | ||
<|> {.metricName($0)} <^> identifier | ||
} | ||
} | ||
|
||
extension VFL.Constant { | ||
static var parser: Parser<Character, VFL.Constant> { | ||
return {.number($0)} <^> numberParser | ||
<|> {.metricName($0)} <^> identifier | ||
} | ||
} | ||
|
||
extension VFL.ObjectOfPredicate { | ||
static var parser: Parser<Character, VFL.ObjectOfPredicate> { | ||
return {.constant($0)} <^> VFL.Constant.parser | ||
<|> {.viewName($0)} <^> identifier | ||
} | ||
} | ||
|
||
extension VFL.Predicate { | ||
static var parser: Parser<Character, VFL.Predicate> { | ||
return curry(VFL.Predicate.init) | ||
<^> optional(VFL.Relation.parser) | ||
<*> VFL.ObjectOfPredicate.parser | ||
<*> optional(char("@") *> VFL.Priority.parser) | ||
} | ||
} | ||
|
||
extension VFL { | ||
init(format: String) throws { | ||
self = try parse(VFL.parser, format) | ||
} | ||
|
||
/// ``` | ||
/// <visualFormatString> ::= | ||
/// (<orientation>:)? | ||
/// (<superview><connection>)? | ||
/// <view>(<connection><view>)* | ||
/// (<connection><superview>)?``` | ||
static var parser: Parser<Character, VFL> { | ||
let metricName = identifier | ||
let positiveNumber: Parser<Character, CGFloat> = numberParser >>- {$0 > 0 ? pure($0) : fail(.Mismatch(AnyCollection([]), "positive", "negative: \($0)"))} | ||
let simplePredicate: Parser<Character, VFL.SimplePredicate> = ({.metricName($0)} <^> metricName) <|> ({.positiveNumber($0)} <^> positiveNumber) | ||
let predicateListWithParens: Parser<Character, [VFL.Predicate]> = extend | ||
<^> (char("(") *> ({[$0]} <^> VFL.Predicate.parser)) | ||
<*> zeroOrMore(char(",") *> VFL.Predicate.parser) <* char(")") | ||
let predicateList: Parser<Character, VFL.PredicateList> = {.simplePredicate($0)} <^> simplePredicate | ||
<|> {.predicateListWithParens($0)} <^> predicateListWithParens | ||
let superview = char("|") | ||
let connection = (VFL.Connection.init) <^> (char("-") *> predicateList <* char("-") | ||
<|> {_ in VFL.PredicateList.simplePredicate(.positiveNumber(8))} <^> char("-") | ||
<|> {_ in VFL.PredicateList.simplePredicate(.positiveNumber(0))} <^> string("")) | ||
let view = curry(VFL.View.init) | ||
<^> (char("[") *> identifier) | ||
<*> optional(predicateListWithParens, otherwise: []) <* char("]") | ||
let views = zeroOrMore({a in {(a, $0)}} <^> connection <*> view) | ||
let orientation: Parser<Character, VFL.Orientation> = {_ in .v} <^> string("V:") | ||
<|> {_ in .h} <^> (string("H:") <|> string("")) | ||
return curry(VFL.init) | ||
<^> orientation | ||
<*> optional(superview *> connection) | ||
<*> view | ||
<*> views | ||
<*> optional(connection <* superview) | ||
} | ||
} |
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,10 +10,11 @@ Pod::Spec.new do |s| | |
s.author = { "banjun" => "[email protected]" } | ||
s.source = { :git => "https://github.com/banjun/NorthLayout.git", :tag => s.version.to_s } | ||
s.social_media_url = 'https://twitter.com/banjun' | ||
s.ios.deployment_target = '8.0' | ||
s.osx.deployment_target = '10.10' | ||
s.ios.deployment_target = '9.0' | ||
s.osx.deployment_target = '10.11' | ||
s.source_files = 'Classes/**/*' | ||
s.ios.frameworks = 'UIKit' | ||
s.osx.frameworks = 'AppKit' | ||
s.requires_arc = true | ||
s.dependency 'FootlessParser', '~> 0.4' | ||
end |