rightpoint / anchorage Goto Github PK
View Code? Open in Web Editor NEWA collection of operators and utilities that simplify iOS layout code.
License: MIT License
A collection of operators and utilities that simplify iOS layout code.
License: MIT License
Hello,
I was trying to do the following:
view.centerXAnchor == 2 * containe.trailingAnchor / 3
but I receive the compiler error:
Binary operator '*' cannot be applied to operands of type 'Int' and 'NSLayoutXAxisAnchor'
Swift Version: 3.2
How can I achieve this relative positioning?
Not sure if that's the right term for it, but this idea is inspired by SnapKit, which can do this:
someView.snp.makeConstraints {
innerView.width.centerX.top.equalToSuperview()
}
I was thinking that an Anchorage-like way to do that would be something more like this:
innerView.anchors([.width, .centerX, .top]) == otherView
As a convenience, we could also supply a constant that means "the superview of the view to the left of the ==
:
innerView.anchors([.width, .centerX, .top]) == Anchorage.Superview() // name TBD
Question: would this work with operators that we use to inset/offset things? I think probably not, just to simplify things. We already provide verticalAnchors
, horizontalAnchors
, edgeAnchors
, and centerAnchors
for cases where you would want to pin common combinations with an inset/offset. So, I think this should be a compilation error:
innerView.anchors([.width, .centerX, .top]) == otherView + 10
Hi,
It's will be great to create a Swift 3 version of your component
Article about Swift 3:
With Swift 3 it would also be nice to add support for SwiftPM
Articles about SwiftPM:
On your README.md
add on top and add section for the installation with SwiftPM.
Not sure what the right approach is. Do we have to use the new edge inset guide on the view?
AspectImageCell.swift:25:44: error: binary operator '~' cannot be applied to operands of type 'Int' and 'Float'
imageView.widthAnchor == 0 ~ UILayoutPriorityRequired - 1
~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can work around this with parentheses and casting, but it would be nice if we could fix it with operator precedence. Here's a workaround:
imageView.widthAnchor == 0 ~ UILayoutPriority(UILayoutPriorityRequired - 1)
I get a bad access exception by switching the version from 4.0.0 to 4.1.0 (or higher):
Internal.swift
line 155: switch (first, anchors.first, second, anchors.second) {
Full error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x600100000000)
By calling horizontalAnchors
in a custom UIView.
//...
private let containerView: UIView
override init(frame: CGRect) {
//...
super.init(frame: frame)
self.addSubview(self.containerView)
self.containerView.horizontalAnchors == self.horizontalAnchors // Runtime error here.
}
//...
I've tried searching for any associated zombie objects, but couldn't find any.
Everything is working fine prior to version 4.1.
Did something significant change between the versions that I am not aware of? Or am I looking over something trivial?
If I'm binding to a view's superview
, which is a UIView?
, I need to unwrap it first. It would be nice if ==
had an override for (lhs: UIView?, rhs: UIView?) -> NSLayoutConstraint?
, so I could write something like:
myView.widthAnchor == myView.superView?.widthAnchor
Hi, guys,
I met a problem when I was following the tutorial on how to build an IB-free app, https://www.raizlabs.com/dev/2016/08/ib-free-living-without-interface-builder/, after I finish configuring my AppDelegate Info.plist and my VC. The view could not display in full-screen size, there is black gap on top and another on at the bottom.
I check all the files, it seems all right. I created another project and did it again and everything works.
I am just curious where the bug is. So I will really appreciate the help if anyone could spend a couple minutes to look at my code at
https://github.com/InfinityCode777/LearnIBFree
SHA: f52b165905f780c00490c6b037b2d13d6fbb1ea1
The screen snapshot of iPhone8Plus simulator is contained in repo above.
Best
Jing
I love how concise Anchorage makes constraint equations. But we could do even better by providing view properties that drop the Anchor
suffix and alias the anchor properties. For example:
view.top = container.top + 3
view.edges = container.edges
If interested, I could put a PR together.
// Context
let view1 = UIView()
let view2 = UIView()
let window = UIWindow()
window.addSubview(view1)
window.addSubview(view2)
view1.widthAnchor == 50
view2.widthAnchor == (view1.widthAnchor + 150) / 2
// Expectation: (50 + 150) / 2
view2.frame.width == 100
// Actual: (50 / 2) + 150
// <NSLayoutConstraint: UIView.height == 0.5 * UIView.height + 150 (active)>
view2.frame.width == 175
Hey guys, I have one question. Having this code:
viewTopAnchor = (view_1.topAnchor = view_2.topAnchor)
then at some point I need to updated the viewTopAnchor property
removeConstraint(viewTopAnchor)
viewTopAnchor = (view_1.topAnchor = view_3.bottomAnchor)
addConstraint(viewTopAnchor) // is this optional or mandatory after removeConstraint?
The question is, due to the Anchorage is adding automatically the constraint when you declare it, should I add it manually if I remove and redeclare it again? Because seems that is I don't add it manually the view is broken.
Adding constraints should probably prevent duplicates.
From basic testing, duplicate constraints do not appear to cause technical problems. However, if those constraints are iterated/manipulated it could cause problems.
ConstraintBatch
would need to perform a "union" instead of "append". (See here.) Unfortunately, even though NSLayoutConstraint
is Hashable
, apparently duplicate constraints still have unique hash values, so Set
can't be used to remove duplicates. You would need to do an Equatable
based "union" instead. I have some code for that here.
Hi,
For iOS and macOS projets it's will be great to make your component Carthage compatible
Take a look at these articles:
On your README.md
add on top and add section for the installation with Carthage.
We should to take into consideration the case where one of the layout anchors are optional. If I do this:
view.leftAnchor == view.window?.leftAnchor
it uses the equality operator rather than anchorage ==
operator.
It would be great if there was a proper project with a demo app in the repository.
Anchorage should be made compatible with Swift 3.0. There are a few issues that stand in the way of this goal:
why can't I do this?
let w = 100
view.widthAnchor == w
Compile says
Binary operator '==' cannot be applied to operants of type NSLayoutDimension' and 'Int'
while this is possible
view.widthAnchor == 100
I feel like I'm probably just missing something stupid, but the following code demonstrates a problem I'm having:
let n: Double = 44
button.heightAnchor == 44 // works
button.heightAnchor == CGFloat(n) // works
button.heightAnchor == n // Failes with "Binary operator '==' cannot be applied to operands of type 'NSLayoutDimension' and 'Double'"
I feel like it should work without wrapping it because of this operator definition: https://github.com/Raizlabs/Anchorage/blob/master/Source/Anchorage.swift#L45.
Am I doing something wrong?
I just wrote this by mistake:
someView.verticalAnchors == someView.verticalAnchors
I meant to write the similar but distinct:
someView.verticalAnchors == someViewContainer.verticalAnchors
This could be caught at run-time by asserting that the sides of ==
are not ===
.
From the iOS 11 release notes:
Updated
NSLayoutXAxisAnchor
andNSLayoutYAxisAnchor
to provide factory methods that create constraints using the system spacing between two anchors. Previously the only way to create such a constraint was with the dash (-
) in the Visual Format Language.
Anchorage should add some kind of syntactic sugar or named CGFloat
value like .system
or something. The new API looks like this:
func constraintEqualToSystemSpacingAfter(NSLayoutXAxisAnchor, multiplier: CGFloat)
It might be nice to use it like this:
view2.trailingAnchor == view1.leadingAnchor + SystemSpacing()
https://swift.org/source-compatibility/#adding-projects
According to the acceptance criteria, eligible projects must "add value not already included in the suite." I think Anchorage satisfies this because of its extensive use of operators and generic operators, especially as they impact compile times.
horizontalEdge operator shifts the constraint, so:
view.horizontalAnchors == horizontalAnchors + 10
This shifts the leading and trailing constraint right by 10. This makes sense, but isn't overly useful in practice. It feels like EdgeAnchors or horizontal / vertical anchors should inset the anchors so +10 would shift the leading left and the trailing right.
With the new enumerations provided in #33, it would be nice to add some extra operators so that you can do:
view1.leadingAnchor == view2.trailingAnchor ~ .low + 1
Instead, you have to do:
view1.leadingAnchor == view2.trailingAnchor ~ .custom(UILayoutPriorityLow + 1)
viewA.horizontalAnchors == viewB.edgeAnchors
viewA.horizontalAnchors == viewB.verticalAnchors
These both compile, but then crash at runtime because they are invalid constraints. The types exposed by Anchorage should turn these cases into compile errors.
On the pattern of horizontalAnchors
and verticalAnchors
:
view1.centerAnchors == view2.centerAnchors
// same as:
view1.centerXAnchor == view2.centerXAnchor
view1.centerYAnchor == view2.centerYAnchor
I recently hit a bug where I had a child VC inside a parent VC, and had set the child VC's edge anchors to the parent view's edges. (Not the child VC view, but the child VC itself.) This led to a bug where on X series phones, in a UINavigationController presented modally, when the parent (containing the child) VC was pushed onto the nav controller, the VC's bottom edge appeared to sink lower towards the bottom edge of the screen, and some content wound up underneath the Home Indicator.
Changing the line of code from
childVC.edgeAnchors == parentVC.view.edgeAnchors
to
childVC.view.edgeAnchors == parentVC.view.edgeAnchors
fixed the issue.
I propose removing the convenience accessors for things like edgeAnchors
from UIViewController, as it appears to produce different behaviors on different devices, introduces ambiguity to the layout semantics, and can be difficult to spot.
I'm using Anchorage version 3.0.0 in a Swift 3 project.
I want to set a priority to a constraint. From the README, I should use the ~
operator.
thumbnailTopConstraint = thumbnailImageView.topAnchor == contentView.topAnchor + 15 ~ 950
^~error
I love the idea of stating a constraint as an equation. However, using the bare equality and comparison operators for this purpose seems semantically incorrect and is bound to make the code harder to understand than it should be. I would suggest that we surround the equality and comparison operators with vertical bars to indicate "constraint". For example:
view.topAnchor |>=| container.topAnchor + 5
This also lets us change the operator precedence if we need to.
In order to preserve backward compatibility, we could make the adorned operators the default, but allow bare operators via conditional compilation.
If you are interested in this, I could put a PR together.
I'm having a problem using Anchorage with XCode 12 and iOS 14.
Running on a simulator I get - (if using command line tools 11.7 for Carthage) "Building for iOS Simulator, but the linked framework 'Anchorage.framework' was built for iOS."
If I use command line tools 12 for Carthage build fails ("Task failed with exit code 1: ,, This usually indicates that project itself failed to compile."). I checked the xcode logs and they succeeded but with many warnings - "*.pcm: No such file or directory."
If anyone one has any ideas or need more info to investigate that would be great.
My biggest question though is if Anchorage is still supported and if this issue is on my side or should I wait for a fix?
I was also thinking of pulling the Anchorage source, try fix myself and add the framework in the project, but I don't know if that's a good idea?
Any help would be great.
I want to position an element at 1/3 of the screen's Y axis, which works fine using
NSLayoutConstraint(
item: logo,
attribute: .centerY,
relatedBy: .equal,
toItem: self,
attribute: .bottom,
multiplier: 0.333,
constant: 0.0).isActive = true
Is it possible to write this constraint in Anchorage syntax? I tried logo.centerYAnchor == bottomAnchor * 1 / 3
and logo.centerYAnchor == 0.333 * bottomAnchor
but both result in compile errors.
label.leadingAnchor == view.topAnchor
Expected behavior: a compile-time error, telling you that this combination is invalid.
Actual behavior: a warning telling you that the result of ==
is unused.
The current behavior is that, in the absence of an override of ==
for <NSLayoutXAxisAnchor, NSLayoutYAxisAnchor>
, it’s falling back to the global ==
→ Equatable
conformance for NSObject
.
Ideally, we could write the appropriate overrides and mark them as @available(*, unavailable)
, but unfortunately, Swift is too clever for that. It realizes that there’s no point in choosing that implementation, so it again falls back to the NSObject
version.
We can mark the methods as @available(*, deprecated, message: "It’s invalid to mix X and Y axes in this way")
and then fatalError
or -> Never
(or both). But we still get a compile-time warning, not error.
One more sweeping workaround would be to stop using built-in UIKit anchors in favor of custom shorthand anchors, like .leading
and .top
instead of .leadingAnchor
and .topAnchor
. Those types can do whatever we want, which in this case would be to not inherit from NSObject
, so then we would have more control over which operators are valid. It’s the nuclear option, but it might make layout expressions a little nicer to use.
As a stopgap, we should probably implement and deprecate the invalid combinations, just to improve error messaging.
someView.topAnchor == otherView.bottomAnchor + 50 ~ UILayoutPriorityDefaultHigh
Gives this error in Swift 3:
error: adjacent operators are in unordered precedence groups 'AdditionPrecedence' and 'PriorityPrecedence'
someView.topAnchor == otherView.bottomAnchor + 50 ~ UILayoutPriorityDefaultHigh
^ ~
If this is fixed, it should be fixed in the readme as well.
It would be nice if Anchorage had prebuilt frameworks attached to the releases so carthage update/install went faster.
https://github.com/Carthage/Carthage#archive-prebuilt-frameworks-into-one-zip-file
In the Anchorage demo app, nothing in the root view controller is pinned using Anchorage. I'm trying to make my first app using Anchorage, and if I try using it to pin stuff I added to the root view that was automatically created by IB, I just get a big mess. Surely I'm doing it wrong or making my configureLayout() call too early. In any case, a demo using Anchorage in every view, including the root view controller, would really help.
It would be nice to be able to take any Anchorage expression and add + "someString"
to it, and get a constraint that has an identifier. Constraint identifiers show up in debug logs and the Xcode view debugger, and they also appear on wtfautolayout.com.
I’m guessing that overloading func +(lhs: LayoutExpression, rhs: String)
might adversely affect compile times, so I’d be OK with exploring other operators, but it’s hard to beat good ol’ +
for readability.
According to the readme the priority can be set with a number like
view.centerXAnchor == view.superview.centerXAnchor + 20 ~ 752
but that doesn't seem to work. The compiler always complains about it with the message Cannot convert value of type 'Int' to expected argument type 'Priority'
.
However, using an instance of Priority
works:
view.centerXAnchor == view.superview.centerXAnchor + 20 ~ .init(752)
So either the documentation is wrong or there is a bug which doesn't allow to use an Integer directly.
When I was using Anchorage for the first time, I was trying to anchor a label within a UIViewController. I didn't realize the "container" in the docs was in specific reference to the view, so I first did
label.centerAnchors == self.centerAnchors
(instead of label.centerAnchors == self.view.centerAnchors
)
which actually worked, adding to my confusion. I realized the issue when I tried to access the UIViewController's leading and trailing anchors, which of course it has none. However, it does have center, edge, size, vertical, and horizontal anchors, which led me in the wrong direction.
It also asymmetrically sets translates... = false
only on the left side of an (in)equality. This should be documented as well.
I plan to use Anchorage in an iOS Swift project soon. I'd prefer to avoid CocoaPods or Carthage and simply add the source files to my project manually. It would be useful to include instructions for a manual installation in the README. Thanks!
To extend Anchorage in an application-specific way, one might create convenience methods to return an AnchorPair
, make new AnchorGroupProviding
conformers, or extend AnchorGroupProviding
to specify new groups. These approaches start with being able to construct an AnchorPair
outside of the Anchorage module.
I'm not sure this qualifies as the most "practical" example, but here is my real-world use case. I've implemented a vertical columns-based layout system in order to match the way my designer works, to support handling devices with different widths by scaling the UI rather than implementing (and designing) a responsive design or doing nothing.
So for example, I'd like to be express a layout like
titleLabel.horizontalAnchors == AnchorPair(first: columnOne.leadingAnchor, second: columnTwo.trailingAnchor)
That's pretty raw, and you might imagine a convenience method to make this
titleLabel.horizontalAnchors == anchorsSpanningColumns(.one, .two)
The AnchorPair
initializer is marked internal
, so I can't use it directly. I've hobbled by with a hack of replicating the internal initializer with slightly different parameter labels, intending to bring this very question in front of this group. With Swift 4.1 and SE-0189, this is now explicitly deprecated.
What is the reasoning behind disallowing AnchorPair
construction outside of Anchorage? Is there a downside or complication to making its initializer public
that I'm not seeing?
Obviously, I have alternatives. I can always use Anchorage as the implementation for layout convenience methods, but integration with the DSL is so much nicer than mixing it with method calls.
I'd love to hear what you think. In the event that there aren't objections – maybe this initializer was just very conservatively marked internal
– I'll post a PR. In that case, I'd have some organizational questions around the Internal.swift
file.
We're using anchorage in our app and have something akin to this in our xcconfig:
OTHER_SWIFT_FLAGS = -Xfrontend -warn-long-function-bodies=400 -Xfrontend -warn-long-expression-type-checking=400
We compile Anchorage from source due to needing a static lib and Anchorage code as well as our app are consistently causing warnings.
For Anchorage, it's the performInBatch(closure:)
call in func constraints(forAnchors:, firstConstant c1:, secondConstant:, priority:, builder:)
(Internal.swift
).
In our app, functions that have numerous calls to Anchorage.batch(active:closure:)
ini the same function tends to also fail this check.
Anchorage won't work after updating xcode to 11.4 which is using swift 5.2.
import Anchorage cause an error :
Module compiled with Swift 5.1.2 cannot be imported by the Swift 5.2 compiler.
On top of fixing up the podspec
to include swift_version
(#68) and fixing Xcode 10.2+ warnings (#73), this project should be updated to Swift 5.0.
I took a quick pass at this and it appears to only require a few minor changes. However, someone with access needs to fix the CI build (it currently points to an inaccessible CircleCI page).
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.