Hi, I'm Alex (short for Alexander).
- Blog: kean.blog
- Built Nuke, Pulse, and other popular frameworks and tools
- Developed the initial CreateAPI version which is now maintained by @cookpad
Contacts
- Mastodon: @[email protected]
Intuitive and powerful Auto Layout library
License: MIT License
Hi, I'm Alex (short for Alexander).
Contacts
Hi, @kean thanks for great library ๐
I don't know if this ever entered the framework or not, if I have case set size with same width and height like
view.ancors.size.equal(CGSize(width: 100 height: 100))
Can I just write simple code like this ?
view.ancors.size.equal(100)
I'm just giving a suggestion what if AnchorCollectionSize improve like
public protocol ConstraintSize {}
extension CGSize: ConstraintSize {}
extension Int: ConstraintSize {}
extension Double: ConstraintSize {}
extension CGFloat: ConstraintSize {}
public struct AnchorCollectionSize {
/// Set the size of item.
@discardableResult public func equal(_ size: ConstraintSize) -> [NSLayoutConstraint] {
switch size {
case is CGFloat, is Int, is Double:
let _size: CGFloat = convertSizeToEqual(from: size)
return [width.equal(_size), height.equal(_size)]
case let size as CGSize:
return [width.equal(size.width), height.equal(size.height)]
default:
return []
}
}
/// Set the size of item.
@discardableResult public func greaterThanOrEqul(_ size: ConstraintSize) -> [NSLayoutConstraint] {
switch size {
case is CGFloat, is Int, is Double:
let _size: CGFloat = convertSizeToEqual(from: size)
return [width.greaterThanOrEqual(_size), height.greaterThanOrEqual(_size)]
case let size as CGSize:
return [width.greaterThanOrEqual(size.width), height.greaterThanOrEqual(size.height)]
default:
return []
}
}
/// Set the size of item.
@discardableResult public func lessThanOrEqual(_ size: ConstraintSize) -> [NSLayoutConstraint] {
switch size {
case is CGFloat, is Int, is Double:
let _size: CGFloat = convertSizeToEqual(from: size)
return [width.lessThanOrEqual(_size), height.lessThanOrEqual(_size)]
case let size as CGSize:
return [width.lessThanOrEqual(size.width), height.lessThanOrEqual(size.height)]
default:
return []
}
}
/// Makes convert ConstraintSize to CGFloat
private func convertSizeToEqual(from size: ConstraintSize) -> CGFloat {
var _size: CGFloat = 0
if let size = size as? Int {
_size = CGFloat(size)
} else if let size = size as? Double {
_size = CGFloat(size)
} else if let size = size as? CGFloat {
_size = size
}
return _size
}
}
Hi!
Thanks for building a small and simple Auto Layout library. I've been integrating it into our project and came up with couple thoughts/questions about the implementation ๐
Thanks in advance for having a look at them!
I've found this extension in the tests part of the project and I think it's very useful. What was the motivation to remove it from the project itself?
public extension UIView {
@discardableResult @nonobjc func addSubview(_ a: UIView, constraints: (LayoutAnchors<UIView>) -> Void) -> Constraints {
addSubview(a)
return Constraints(for: a, constraints)
}
}
I've been able to easily pin anchors to the superview, but for the SafeArea
I didn't find a simple way to do that. So I've implemented this extension. Though I've been wondering if it fits the Align philosophy. Maybe I don't see some big picture here ๐ค
public extension LayoutAnchors where Base: UIView {
var superviewSafeArea: LayoutItem? {
return self.base.superview?.safeAreaLayoutGuide
}
@discardableResult func toSafeArea(insets: CGFloat = 0.0) -> [NSLayoutConstraint] {
edges.pin(to: self.base.superview?.safeAreaLayoutGuide, insets: insets)
}
}
Combining the above 2 I've been able to write code like this:
view.addSubview(loadingView) {
$0.toSafeArea()
}
Or this:
view.addSubview(progressView) {
$0.top.pin(to: $0.superviewSafeArea)
$0.leading.pin()
$0.trailing.pin()
}
So from my perspective the UIView+AddSubview
is super nice addition ๐
I wanted to have the same interface so I've created couple more extensions:
public extension AnchorCollectionCenter {
@discardableResult func pin() -> [NSLayoutConstraint] {
self.align()
}
@discardableResult func pin<Item: LayoutItem>(to item: Item) -> [NSLayoutConstraint] {
self.align(with: item)
}
}
public extension Anchor where Type: AnchorType.Center {
@discardableResult func pin(offset: CGFloat = 0) -> NSLayoutConstraint {
self.align(offset: offset)
}
}
public extension Anchor where Type: AnchorType.Edge {
@discardableResult func pin(to anchor: Anchor<Type, Axis>, spacing: CGFloat = 0, relation: NSLayoutConstraint.Relation = .equal) -> NSLayoutConstraint {
self.spacing(spacing, to: anchor, relation: relation)
}
}
Is there some specific reasons why you kept spacing
and align
and didn't rename them to pin
?
I've been looking at the source code and found that the implementation of the variable isInverted
is different in pin
and spacing
functions.
For example spacing
has this check:
(attribute == .trailing && anchor.attribute == .leading)
But doesn't have this one:
(attribute == .trailing && anchor.attribute == .trailing)
Which in my opinion is also needed to make sure we invert the spacing in this case. So maybe we can simplify it and use the same logic as in the pin
?
let isInverted = [.trailing, .right, .bottom].contains(attribute)
While writing the last point about the IsInverted
I've noticed that spacing
also inverts the relation
. Which is probably the reason why isInverted
is calculated differently here.
But still the spacing
function allows to write the following:
view1.anchors.trailing.spacing(8, to: view2.anchors.trailing)
Which will produce wrong layout ๐ค
Hey,
addSubview(bgView) {
$0.edges.pinToSuperview()
}
addSubview(searchBar) {
$0.edges(.left, .right, .top).pinToSuperview()
$0.height.set(44)
}
addSubview(tableView) {
$0.edges(.left, .right, .bottom).pinToSuperview()
$0.height.match(bgView.al.height - 44)
}
When I run this code, I get the following output, and I'm not sure what I'm doing wrong:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x6080000944b0 UIImageView:0x7f87650162c0.bottom == Testing.MyTableView:0x7f8765010b70.bottom (active)>",
"<NSLayoutConstraint:0x6080000945a0 V:|-(0)-[UIImageView:0x7f87650162c0] (active, names: '|':Testing.MyTableView:0x7f8765010b70 )>",
"<NSLayoutConstraint:0x60400008e510 UITableView:0x7f8763865400.height == UIImageView:0x7f87650162c0.height - 44 (active)>",
"<NSLayoutConstraint:0x608000097110 '_UITemporaryLayoutHeight' Testing.MyTableView:0x7f8765010b70.height == 0 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6080000944b0 UIImageView:0x7f87650162c0.bottom == Testing.MyTableView:0x7f8765010b70.bottom (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Anybody have an idea how to fix that?
Thank you in advance.
Hi,
Do you plan to add support for iOS 8/9
Thanks.
Hey!
First of all, really enjoying the library, thank you!
I would like to create a constraint like this:
$0.height.lessThanOrEqual(superview.anchors.height, multiplier: 2 / 3)
So that height of a view is 2/3 of a superview. However it's not possible to provide a multiplier. So I'm wondering if extension functions on AnchorType.Dimension
miss a multiplier parameter?
Right now:
/// Adds a constraint that defines the anchors' attributes as equal to each other.
@discardableResult func equal<Type: AnchorType.Dimension, Axis>(_ anchor: Anchor<Type, Axis>, constant: CGFloat = 0) -> NSLayoutConstraint {
Constraints.add(self, anchor, constant: constant, relation: .equal)
}
@discardableResult func greaterThanOrEqual<Type: AnchorType.Dimension, Axis>(_ anchor: Anchor<Type, Axis>, constant: CGFloat = 0) -> NSLayoutConstraint {
Constraints.add(self, anchor, constant: constant, relation: .greaterThanOrEqual)
}
@discardableResult func lessThanOrEqual<Type: AnchorType.Dimension, Axis>(_ anchor: Anchor<Type, Axis>, constant: CGFloat = 0, multiplier: CGFloat = 1) -> NSLayoutConstraint {
Constraints.add(self, anchor, constant: constant, relation: .lessThanOrEqual)
}
}
With multiplier parameter:
public extension Anchor where Type: AnchorType.Dimension {
/// Adds a constraint that defines the anchors' attributes as equal to each other.
@discardableResult func equal<Type: AnchorType.Dimension, Axis>(_ anchor: Anchor<Type, Axis>, constant: CGFloat = 0, multiplier: CGFloat = 1) -> NSLayoutConstraint {
Constraints.add(self, anchor, constant: constant, multiplier: multiplier, relation: .equal)
}
@discardableResult func greaterThanOrEqual<Type: AnchorType.Dimension, Axis>(_ anchor: Anchor<Type, Axis>, constant: CGFloat = 0, multiplier: CGFloat = 1) -> NSLayoutConstraint {
Constraints.add(self, anchor, constant: constant, multiplier: multiplier, relation: .greaterThanOrEqual)
}
@discardableResult func lessThanOrEqual<Type: AnchorType.Dimension, Axis>(_ anchor: Anchor<Type, Axis>, constant: CGFloat = 0, multiplier: CGFloat = 1) -> NSLayoutConstraint {
Constraints.add(self, anchor, constant: constant, multiplier: multiplier, relation: .lessThanOrEqual)
}
}
Let me know if I missed something and behaviour that I would could be achieved in a different way ๐
For the moment I've implemented it without Align like this:
NSLayoutConstraint.activate([
childView.heightAnchor.constraint(
lessThanOrEqualTo: parentView.heightAnchor, multiplier: 2 / 3
)
])
Cheers!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.