i'm tying to crate one UIView at the bottom with static height 60 and then a top one filling the rest. however this code seem to just make the top one fill the whole screen.
//bottomWrapperView
let bottomWrapperView = UIView()
bottomWrapperView.backgroundColor = UIColor.red
self.addSubview(bottomWrapperView)
//TopWrapperView
let topWrapperView = UIView()
topWrapperView.backgroundColor = UIColor.green
self.addSubview(topWrapperView)
//BottomWrapperView Constraints
bottomWrapperView.snp.makeConstraints { (make) -> Void in
make.height.equalTo(60)
make.left.equalTo(self).offset(0)
make.bottom.equalTo(self).offset(0)
make.right.equalTo(self).offset(0)
make.top.equalTo(topWrapperView)
}
//TopWrapperView Constraints
topWrapperView.snp.makeConstraints { (make) -> Void in
make.left.equalTo(self).offset(0)
make.top.equalTo(self).offset(0)
make.bottom.equalTo(bottomWrapperView)
make.right.equalTo(self).offset(0)
}
Here, you need to make bottom constraint for topWrapperView equal to the top of bottomWrapperView
//BottomWrapperView Constraints
bottomWrapperView.snp.makeConstraints { (make) -> Void in
make.height.equalTo(60)
make.left.equalTo(self).offset(0)
make.bottom.equalTo(self).offset(0)
make.right.equalTo(self).offset(0)
}
//TopWrapperView Constraints
topWrapperView.snp.makeConstraints { (make) -> Void in
make.left.equalTo(self).offset(0)
make.top.equalTo(self).offset(0)
make.bottom.equalTo(bottomWrapperView.snp.top)
make.right.equalTo(self).offset(0)
}
Related
I have a stackview with three subviews:
stackView.spacing = 8.0
stackView.addArrangeSubview(view1)
stackView.addArrangeSubview(view2)
stackView.addArrangeSubview(view3)
At some point, I add custom spacing after view2:
stackView.setCustomSpacing(24.0, after: view2)
Now, I want to remove the custom spacing. Is this possible using UIStackView? The only way I can think of is
stackView.setCustomSpacing(8.0, after: view2)
but this will break if I ever change the spacing of my stackView to something other than 8.0, because spacing will remain 8.0 after view2.
You should re-create stackView with specific spacing and replace the old one with the new stackView.
Try this extension:
extension UIStackView {
// remove all custom spacing
func removeCustomSpacing() -> Void {
let a = arrangedSubviews
arrangedSubviews.forEach {
$0.removeFromSuperview()
}
a.forEach {
addArrangedSubview($0)
}
}
// remove custom spacing after only a single view
func removeCustomSpacing(after arrangedSubview: UIView) -> Void {
guard let idx = arrangedSubviews.firstIndex(of: arrangedSubview) else { return }
removeArrangedSubview(arrangedSubview)
insertArrangedSubview(arrangedSubview, at: idx)
}
}
Use it simply as:
myStackView.removeCustomSpacing()
or, if you are setting custom spacing on multiple views, and you want to remove it from only one view, use:
theStackView.removeCustomSpacing(after: view2)
let spacingView = UIView()
[View1, View2, spacingView, View3].forEach { view in
stackView.addArrangeSubview(view)
}
when you want to remove it just remove the spacingView from the array and you can play with width and height of the spacingView like you prefer
I am using SnapKit and can't find a clean way to update the offset of a constraint. It's a simple loading bar view whose inner view (expiredTime) has to fill the parent (left to right) according to percentage.
I create the constraint in the awakeFromNib of a custom UIView
self.expiredTime.snp.makeConstraints { (make) in
make.left.equalTo(self)
make.top.equalTo(self)
make.bottom.equalTo(self)
self.constraintWidth = make.width.equalTo(self).constraint
}
setNeedsUpdateConstraints()
then whenever the time is updated i call setNeedsUpdateConstraints() which will trigger the default updateConstraints() which has been overloaded as by apple hint:
Does not work: (the expiredTime view always fit with the parent view)
override func updateConstraints() {
let offset = self.frame.width * CGFloat(percentage)
self.expiredTime.snp.updateConstraints { (make) in
make.width.equalTo(self).offset(offset).constraint
}
super.updateConstraints()
}
This also does not work:
override func updateConstraints() {
let offset = self.frame.width * CGFloat(percentage)
self.constraintWidth?.update(offset: offset)
super.updateConstraints()
}
Rebuilding all the constraint works but i would like to avoid it
override func updateConstraints() {
self.expiredTime.snp.remakeConstraints() { (make) in
make.left.equalTo(self)
make.top.equalTo(self)
make.bottom.equalTo(self)
self.constraintWidth = make.width.equalTo(self).multipliedBy(self.percentage).constraint
}
super.updateConstraints()
}
Your first solution does not work, because you already set the width of your expiredTime view to the full width before adding the offset. To make it work you have to set the width to 0 and then add the offset. But the offset is not really needed here, you can simply set the width to the calculated width:
override func updateConstraints() {
let width = self.frame.width * CGFloat(percentage)
self.expiredTime.snp.updateConstraints { (make) in
make.width.equalTo(width)
}
super.updateConstraints()
}
Or if you keep a reference to the constraint you don't have to override updateConstraints() at all. You can simply call the constraint's update method (without calling setNeedsUpdateConstraints() afterwards)
constraintWidth?.update(offset: CGFloat(percentage) * view.bounds.width)
Just remember to set the width to 0 when you initialize constraintWidth:
self.expiredTime.snp.makeConstraints { (make) in
make.left.top.bottom.equalTo(self)
self.constraintWidth = make.width.equalTo(0).constraint
}
I want to make a UIView rectangular with SnapKit in Swift, like this
lazy var customView: UIView = {
let view = UIView(frame: CGRect())
self.addSubview(view)
view.snp.makeConstraints({ (make) in
make.left.top.bottom.equalToSuperview().inset(self.inset)
make.width.equalTo(make.height) // Error in this line
})
return view
}()
You have to use view.snp.height instead of make.height:
lazy var customView: UIView = {
let view = UIView(frame: CGRect())
self.addSubview(view)
view.snp.makeConstraints({ (make) in
make.left.top.bottom.equalToSuperview().inset(self.inset)
make.width.equalTo(view.snp.height) // <---
})
return view
}()
If you have 2 view on same superview, you can do next:
view1.snp.makeConstraints { (make) in
make.leading.equalToSuperview()
make.bottom.equalToSuperview()
make.top.equalToSuperview()
}
view2.snp.makeConstraints { (make) in
make.trailing.equalToSuperview()
make.bottom.equalToSuperview()
make.top.equalToSuperview()
make.leading.equalTo(view1.snp.trailing)
make.width.equalTo(view1.snp.width)
}
and result
In same manner, using view.snp.width or view.snp.height you can setup equality of views using SnapKit
view.snp.makeConstraints({ (make) in
make.left.top.bottom.equalToSuperview().inset(self.inset)
make.width.equalTo(view.snp.height)
})
I'm trying to add 2 views to a CallOutView. The pushButton should be at the bottom with a static height of 20. The topView should then fill the rest. I've tried to do this programmatically using SnapKit. However it seems like the pushbutton just fills everything? what am i doing wrong?
callOutView = UIView(frame: CGRectMake(-70+(self.frame.width/2), -65, 140, 60))
callOutView!.backgroundColor = UIColor.clearColor()
callOutView?.clipsToBounds = true
callOutView?.layer.cornerRadius = 6
self.addSubview(callOutView!)
let topView = UIView()
topView.backgroundColor = UIColor.whiteColor().colorWithAlphaComponent(0.8)
callOutView?.addSubview(topView)
let pushButton = UIButton()
pushButton.backgroundColor = UIColor(rgba: "#09316e").colorWithAlphaComponent(0.8)
pushButton.setTitle("Se Mere", forState: UIControlState.Normal)
pushButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
pushButton.titleLabel?.font = UIFont.systemFontOfSize(8)
callOutView?.addSubview(pushButton)
topView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(callOutView!).offset(0)
make.left.equalTo(callOutView!).offset(0)
make.bottom.equalTo(pushButton).offset(0)
make.right.equalTo(callOutView!).offset(0)
make.height.equalTo(40)
}
pushButton.snp_makeConstraints { (make) -> Void in
make.height.equalTo(20)
make.top.equalTo(topView).offset(0)
make.left.equalTo(callOutView!).offset(0)
make.bottom.equalTo(0).offset(0)
make.right.equalTo(callOutView!).offset(0)
}
The problem is that there are some constraints conflicting with each other, which will make the autolayout system break some of them.
topView.snp_makeConstraints { (make) -> Void in
...
make.bottom.equalTo(pushButton).offset(0)
...
}
pushButton.snp_makeConstraints { (make) -> Void in
...
make.top.equalTo(topView).offset(0)
...
}
The two constraints above tell the autolayout system:
Place topView and pushButton with top edge aligned.
Place topView and pushButton with bottom edge aligned.
Which is impossible if you give topView and pushButton different heights.
Also, this is not what you want obviously. What you want is "place pushButton right below the topView".
Here's the modified code to make pushButton being placed right below the topView:
topView.snp_makeConstraints { (make) -> Void in
...
make.bottom.equalTo(pushButton.snp_top).offset(0)
...
}
pushButton.snp_makeConstraints { (make) -> Void in
...
make.top.equalTo(topView.snp_bottom).offset(0)
...
}
How can I have an UIStackView with the same space as padding and gap between views?
How can I achieve this layout:
When this one doesn't suit me:
Neither does this:
I just need the around views space to be the same as the between views space.
Why is it so hard?
Important
I'm using my fork of TZStackView to support iOS 7. So no layoutMargins for me :(
I know this is an older question, but the way I solved it was to add two UIViews with zero size at the beginning and end of my stack, then use the .equalSpacing distribution.
Note: this only guarantees equal around spacing along the main axis of the stack view (i.e. the left and right edges in my example)
let stack = UIStackView()
stack.axis = .horizontal
stack.alignment = .center
stack.distribution = .equalSpacing
// add content normally
// ...
// add extra views for spacing
stack.insertArrangedSubview(UIView(), at: 0)
stack.addArrangedSubview(UIView())
You can almost achieve what you want using a UIStackView. When you set some constraints yourself on the UIViews inside the UIStackView you can come up with this:
This is missing the left and right padding that you are looking for. The problem is that UIStackView is adding its own constraints when you add views to it. In this case you can add top and bottom constraints to get the vertical padding, but when you try to add a trailing constraint for the right padding, UIStackView ignores or overrides that constraint. Interestingly adding a leading constraint for the left padding works.
But setting constraints on UIStackView's arranged subviews is not what you want to do anyway. The whole point of using a UIStackView is to just give it some views and let UIStackView handle the rest.
To achieve what you are trying to do is actually not too hard. Here is an example of a UIViewController that contains a custom stack view that can handle padding on all sides (I used SnapKit for the constraints):
import UIKit
import SnapKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let padding: CGFloat = 30
let customStackView = UIView()
customStackView.backgroundColor = UIColor(white: 0, alpha: 0.1)
view.addSubview(customStackView)
customStackView.snp_makeConstraints { (make) -> Void in
make.top.left.equalTo(padding)
make.right.equalTo(-padding)
}
// define an array of subviews
let views = [UIView(), UIView(), UIView()]
// UIView does not have an intrinsic contentSize
// so you have to set some heights
// In a real implementation the height will be determined
// by the views' content, but for this example
// you have to set the height programmatically
views[0].snp_makeConstraints { (make) -> Void in
make.height.equalTo(150)
}
views[1].snp_makeConstraints { (make) -> Void in
make.height.equalTo(120)
}
views[2].snp_makeConstraints { (make) -> Void in
make.height.equalTo(130)
}
// Iterate through the views and set the constraints
var leftHandView: UIView? = nil
for view in views {
customStackView.addSubview(view)
view.backgroundColor = UIColor(white: 0, alpha: 0.15)
view.snp_makeConstraints(closure: { (make) -> Void in
make.top.equalTo(padding)
make.bottom.lessThanOrEqualTo(-padding)
if let leftHandView = leftHandView {
make.left.equalTo(leftHandView.snp_right).offset(padding)
make.width.equalTo(leftHandView)
} else {
make.left.equalTo(padding)
}
leftHandView = view
})
}
if let lastView = views.last {
lastView.snp_makeConstraints(closure: { (make) -> Void in
make.right.equalTo(-padding)
})
}
}
}
This produces the following results:
For those who keep getting here looking for a solution for this problem. I found that the best way (in my case) would be to use a parent UIView as background and padding, like this:
In this case the UIStackView is contrained to the edges of the UIView with a padding and separate the subviews with spacing.