Closed macabeus closed 7 years ago
@brunomacabeusbr can you please post the whole code for constrain(self.viewCircle, self.labelResult, self.viewDashed) { ...
?
@vfn Of course
constrain(self.viewCircle, self.labelResult, self.viewDashed) { circle, label, dashed in
circle.top == circle.superview!.top
circle.centerX == circle.superview!.centerX
circle.height == self.viewCircle.frame.width
circle.width == self.viewCircle.frame.width
label.center == circle.center
dashed.top == circle.bottom
dashed.centerX == dashed.superview!.centerX
dashed.height == self.frame.height - viewCircle.frame.height
dashed.width == 1
}
My UI is this:
@brunomacabeusbr 2 things:
constrain
block.dashed
view goes from the bottom of the circle
to the bottom of the superview
, just set the constrain that wayconstrain(self.viewCircle, self.labelResult, self.viewDashed) { circle, label, dashed in
circle.top == circle.superview!.top
circle.centerX == circle.superview!.centerX
circle.height == circle.superview!.width
circle.width == circle.superview!.width
label.center == circle.center
dashed.top == circle.bottom
dashed.centerX == dashed.superview!.centerX
dashed.bottom == dashed.superview!.bottom
dashed.width == 1
}
Abraço!
@vfn I wrote self.viewCircle.frame.width
instead of circle.superview!.width
because I want get the value setted in storyboard (well... this value is "constant"... is better I set literal number where... thank you).
But circle.superview!.width
not work for my situation because my circle should not fill the whole width in superview.
And, I tested dashed.bottom == dashed.superview!.bottom
instead of dashed.height == self.frame.height - viewCircle.frame.height
, but...
... not work correctly (image in left side). With dashed.height == self.frame.height - viewCircle.frame.height
work (image in right side).
I know that is weird use self.frame
in constrain
closure, but, I don't know a better way.
I'm working with Cartography inside one of the cell of CollectionView. Big example:
Obrigado =]
let padding: CGFloat = 20
constrain(self.viewCircle, self.labelResult, self.viewDashed, self.greenCircle) { circle, label, dashed, greenCircle in
circle.top == circle.superview!.top
circle.centerX == circle.superview!.centerX
circle.width == circle.superview!.width - 2 * padding // Use some sort of padding to make it smaller than the super view
circle.height == circle.width
label.center == circle.center
dashed.top == circle.bottom
dashed.centerX == dashed.superview!.centerX
dashed.bottom == greenCircle.top // This will make the line stop just before the green circle
dashed.width == 1
}
@brunomacabeusbr looking at the example where you have multiple circles, I'd suggest you to rethink your view hierarchy. Break it in smaller chunks, where each magenta-ish circle, dashed line, and text are a self contained view.
Then you create constraints placing one view after the other.
By doing that, you can make the dashed line go form the circle to the bottom of it's own container view
@vfn Wait, please. I'm creating a separate pod and I will create a new repository coming soon.
@vfn Finish: https://github.com/brunomacabeusbr/InputStepByStep
You can run the Example
and see the code in InputStepByStep
.
Thank you very much for your attention.
@brunomacabeusbr the issue there has nothing to do with Cartography. If you change the background color of your viewDashed
to something that's not .clear
you'll see that the height is correct when you make it go to the bottom of its superview.
The issue you have there is due to the fact that the dashed sublayer does not resize with the main layer.
In the image below the dashed view, in red, has the correct height, but the dashed layer does not.
Have a look at http://stackoverflow.com/questions/29111099/calayer-not-resizing-with-autolayout
@vfn Interesting. I don't know this expected behavior about UIKit. I thought that CALayer was resizing automatically when the constrains was update.
One question:
constrain(self.viewCircle, self.labelResult, self.viewDashed) { circle, label, dashed in
...
dashed.height == self.frame.height - viewCircle.frame.height
//dashed.bottom == dashed.superview!.bottom
}
viewDashed.setNeedsLayout()
viewDashed.layoutIfNeeded()
viewDashed.addDashedBorder()
This code work. The function addDashedBorder
draw the dash correctly. But, when I use dashed.bottom == dashed.superview!.bottom
instead of dashed.height == self.frame.height - viewCircle.frame.height
, not work correctly. It's equal as your photo.
Do you know why I need set one height
value? And, do you have any idea about one better way, without setNeedsLayout
and layoutIfNeeded
?
Thank you very much =]
@brunomacabeusbr your code works because you force viewDashed
to layout before it should and then you add the sublayer, by calling viewDashed.addDashedBorder()
. At that point viewDashed
gets the correct height.
For now, your code works, but it's not future proof and you'll find issues as soon as you need to change the size of dashed view change, and the dashed layer will be stuck with the initial height.
Another issue with your code, is that your cell is not reusable. Any new attempt to reuse it will add another dashed layer to it.
I'd suggest you to create a UIView subclass, and manage the dashed layer inside the UIView subclass.
Make your viewDashed
to use the following class
final class DashedView: UIView {
override class var layerClass: AnyClass {
return CAShapeLayer.self
}
override init(frame: CGRect = .zero) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
self.setupDash()
}
private func setupDash() {
self.alpha = 0.2
self.backgroundColor = .clear
self.clipsToBounds = true
guard let shapeLayer = self.layer as? CAShapeLayer else {
preconditionFailure()
}
let frameSize = self.frame.size
let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.strokeColor = UIColor.white.cgColor
shapeLayer.lineWidth = 1
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineDashPattern = [1, 1]
shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 1).cgPath
}
}
and then, make the startCell()
look like
func startCell() {
viewCircle.asCircle()
constrain(self.viewCircle, self.labelResult, self.viewDashed) { circle, label, dashed in
circle.top == circle.superview!.top
circle.centerX == circle.superview!.centerX
circle.height == 32
circle.width == 32
label.center == circle.center
dashed.top == circle.bottom
dashed.centerX == dashed.superview!.centerX
dashed.bottom == dashed.superview!.bottom ~ UILayoutPriorityRequired
dashed.width == 1
}
}
@vfn Many, many, many thank you!! Now, it's working elegantly.
I learned a lot because of your help =]
I had the follow difficulty:
But, the code after
constrain
didn't work correctly, and I din't understand why.Then, I debug the code, and, different that I thought, the value of
viewCircle.frame
don't change afterconstrain
, although the layout is changed. Then, I needed make some changes:Exists any way better? Maybe, is a good idea implicitly to call
setNeedsLayout
andlayoutIfNeeded
for each value used inconstrain
?