UIStackView inside UIScrollview - ios

I've added UIScrollView in which added UIStackView in storyboard.
I'm trying to add UIStackView having two UILabels dynamically in parent UIStackView Although i'm giving height of child view ,it takes all the space of parent UIStackView how do i solve this or any other way to achieve this?
My code is
func createSummaryView()
{
let labelSummary:UILabel = UILabel(frame: CGRectZero)
labelSummary.text = "Summary"
labelSummary.heightAnchor.constraintEqualToConstant(30).active = true
let summaryparentView:UIStackView = UIStackView(frame: CGRectZero)
summaryparentView.axis = .Vertical
summaryparentView.alignment = .Leading
summaryparentView.spacing = 1
let summaryViewWidth = width!-50
summaryparentView.heightAnchor.constraintEqualToConstant(180).active = true
//summaryparentView.heightAnchor.constraintEqualToAnchor(summaryparentView.heightAnchor, multiplier: 2).active = true
summaryparentView.addArrangedSubview(labelSummary)
for _ in 1...5
{
let viewParent:UIStackView = UIStackView(frame: CGRectZero)
viewParent.axis = .Horizontal
viewParent.widthAnchor.constraintEqualToConstant(summaryViewWidth).active = true
viewParent.heightAnchor.constraintEqualToConstant(30).active = true
viewParent.distribution = .FillEqually
let labelTitle:UILabel = UILabel()
let labelValue:UILabel = UILabel()
print("summary view width \(summaryViewWidth)")
labelTitle.text = "BUILD"
labelValue.text = "2012"
viewParent.addArrangedSubview(labelTitle)
viewParent.addArrangedSubview(labelValue)
summaryparentView.addArrangedSubview(viewParent)
summaryparentView.translatesAutoresizingMaskIntoConstraints = false
}
stackViewVertical.layoutMarginsRelativeArrangement = true
stackViewVertical.addArrangedSubview(summaryparentView)
stackViewVertical.translatesAutoresizingMaskIntoConstraints = false
}
where width is width = stackViewVertical.frame.size.width

Maybe this library could help you : https://github.com/gurhub/ScrollableStackView
It's Objective-C and Swift compatible.
Sample Code (Swift)
import ScrollableStackView
var scrollable = ScrollableStackView(frame: view.frame)
view.addSubview(scrollable)
// add your views with
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 55))
rectangle.backgroundColor = UIColor.blue
scrollable.stackView.addArrangedSubview(rectangle)
// ...
Sample Code (Objective-C)
#import ScrollableStackView
ScrollableStackView *scrollable = [[ScrollableStackView alloc] initWithFrame:self.view.frame];
scrollable.stackView.distribution = UIStackViewDistributionFillProportionally;
scrollable.stackView.alignment = UIStackViewAlignmentCenter;
scrollable.stackView.axis = UILayoutConstraintAxisVertical;
[self.view addSubview:scrollable];
UIView *rectangle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 55)];
[rectangle setBackgroundColor:[UIColor blueColor]];
// add your views with
[scrollable.stackView addArrangedSubview:rectangle];
// ...

Related

I cannot stretch the height of the titleView in the UINavigationBar

This is my code:
class UINavigationControllerCustom : UINavigationController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews();
navigationBar.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 120);
}
}
class PreferenceInput: UIViewController {
override func viewDidLoad() {
let l1 = UILabel()
l1.backgroundColor = .clear
l1.numberOfLines = 1
l1.font = UIFont.boldSystemFont(ofSize: 30.0)
l1.textAlignment = .left
l1.textColor = .black
l1.text = "Bold title"
l1.sizeToFit();
let l2 = UILabel()
l2.backgroundColor = .clear
l2.numberOfLines = 2
l2.font = UIFont.boldSystemFont(ofSize: 16.0)
l2.textAlignment = .left
l2.textColor = .darkGray;
l2.text = "This is a\nmultiline string for the navBar"
l2.sizeToFit();
let tView = UIStackView(arrangedSubviews: [l1,l2]);
tView.axis = .vertical;
let spacer = UIView()
let constraint = spacer.widthAnchor.constraint(greaterThanOrEqualToConstant: CGFloat.greatestFiniteMagnitude)
constraint.isActive = true
constraint.priority = .defaultLow
let stack = UIStackView(arrangedSubviews: [tView, spacer])
stack.axis = .horizontal
navigationItem.titleView = stack
navigationItem.titleView.sizeToFit();
}
}
And this is what I get:
I can set the height of the navigation bar but how and where should I set the constraint to stretch the height in respect of the given 120px of the custom navigation controller?
I want to provide an individual height of the navigation bar.

Custom Navigation Title in iOS 12

I am trying to implement a custom Navigation Title on an iOS app.
The StoryBoard looks like this:
The place that I want to have the custom Navigation Title is the last view ( the message view ), and because I use an image and text this means that I need to have custom width and height. By needing this if I do in viewDidLoad:
let rect = CGRect(x: 0, y:0, width: 150, height: 88)
titleView = UIView(frame: rect)
......
titleView?.addSubview(imageView)
......
titleView?.addSubview(label)
navigationItem.titleView = titleView
The height of the title is blocked to 44pt.
But how I managed to do it is adding the subViews to the navigation bar:
var navigationBar: MessagesNavigationBar? {
guard let navigationBar = navigationController?.navigationBar as? MessagesNavigationBar else {
return nil
}
return navigationBar
}
And in viewDidLoad
let rect = CGRect(x: 0, y:0, width: 150, height: 88)
titleView = UIView(frame: rect)
......
titleView?.addSubview(imageView)
......
titleView?.addSubview(label)
navigationBar?.addSubview(titleView!)
But the problem is that I have to remove the subviews when I leave the view, otherwise whatever I add there will be present in the table view as well.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if navigationBar != nil {
titleView?.removeFromSuperview()
}
}
Which kinda makes me feel that I'm not doing the right thing and I find difficult to add a fade out animation to those subViews when I leave the conversation. (i.e. native messages app on iOS).
So what is the right way of creating a custom Title Navigation Bar in iOS 12?
Scenes
Creating your custom titleView and assigning it to navigationItem.titleView is what you want. On older systems (pre iOS 11) you just might need to call sizeToFit() on the titleView.
This way you can create this titleView
Swift
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView()
NSLayoutConstraint.activate([
imageView.heightAnchor.constraint(equalToConstant: 20),
imageView.widthAnchor.constraint(equalToConstant: 20)
])
imageView.backgroundColor = .red
let titleLabel = UILabel()
titleLabel.text = "Custom title"
let hStack = UIStackView(arrangedSubviews: [imageView, titleLabel])
hStack.spacing = 5
hStack.alignment = .center
navigationItem.titleView = hStack
}
Obj-C
- (void)viewDidLoad {
[super viewDidLoad];
UIImageView *imageView = [[UIImageView alloc] init];
[NSLayoutConstraint activateConstraints:#[
[imageView.heightAnchor constraintEqualToConstant:20],
[imageView.widthAnchor constraintEqualToConstant:20]
]];
imageView.backgroundColor = [UIColor redColor];
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = #"Custom title";
UIStackView *hStack = [[UIStackView alloc] initWithArrangedSubviews:#[imageView, titleLabel]];
hStack.spacing = 5;
hStack.alignment = UIStackViewAlignmentCenter;
self.navigationItem.titleView = hStack;
}
You might also need to have the right set of autolayout constraints or use UIStackView.
These lines have no effect on the size of a title view:
let rect = CGRect(x: 0, y:0, width: 150, height: 88)
titleView = UIView(frame: rect)
Instead (or in addition) give your title view a width constraint and a height constraint. That is how the runtime knows what size you want.

iOS - UIView does not display inside UIStackView

I have a UIStackView which needs a background color in one of the stacks, so I placed a UIView inside, but the UIView is never displayed. The UIView currently has one subview, a UILabel, but should eventually have another UIStackView instead. If I display the UILabel as a direct child of the UIStackView, rather than the UIView, then the UILabel displays properly. So, what constraints are missing or wrong on my UIView?
let stack = UIStackView()
stack.axis = .Vertical
stack.alignment = .Leading
stack.distribution = .EqualCentering
stack.spacing = 0
let width = UIScreen.mainScreen().bounds.size.width
let frame = CGRect(x: 0, y: 0, width: width, height: 50)
let viewHolder = UIView(frame: frame)
viewHolder.backgroundColor = blueColor
//Needs a blue background
let name = UILabel()
name.text = nameText
name.textColor = yellowColor
name.backgroundColor = blueColor
name.font = UIFont.boldSystemFontOfSize(32.0)
let address = UILabel()
address.text = addressText
let dateTime = UILabel()
dateTime.text = calString
viewHolder.addSubview(name)
stack.addArrangedSubview(viewHolder)
stack.addArrangedSubview(address)
stack.addArrangedSubview(dateTime)
self.stackView.addArrangedSubview(stack)
And when run it produces this:
If I remove the UIView from the equation, ie:
let stack = UIStackView()
stack.axis = .Vertical
stack.alignment = .Leading
stack.distribution = .EqualCentering
stack.spacing = 0
let width = UIScreen.mainScreen().bounds.size.width
let frame = CGRect(x: 0, y: 0, width: width, height: 50)
let viewHolder = UIView(frame: frame)
viewHolder.backgroundColor = blueColor
//Needs a blue background
let name = UILabel()
name.text = nameText
name.textColor = yellowColor
name.backgroundColor = blueColor
name.font = UIFont.boldSystemFontOfSize(32.0)
let address = UILabel()
address.text = addressText
let dateTime = UILabel()
dateTime.text = calString
//viewHolder.addSubview(name)
stack.addArrangedSubview(name) //Add the label directly to the UIStackView
stack.addArrangedSubview(address)
stack.addArrangedSubview(dateTime)
self.stackView.addArrangedSubview(stack)
I get:
I just need the blue part to stretch across the screen
So, answer is, don't use UIStackView. Use the built in UITableView for this kind of layout.

Add views in UIStackView programmatically

I'm trying to add views in UIStackView programmatically.
For now My code is:
UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor blackColor];
[view1 setFrame:CGRectMake(0, 0, 100, 100)];
UIView *view2 = [[UIView alloc]init];
view2.backgroundColor = [UIColor greenColor];
[view2 setFrame:CGRectMake(0, 100, 100, 100)];
[self.stack1 addArrangedSubview:view1];
[self.stack1 addArrangedSubview:view2];
When i deploy the app, there is only 1 view and it is with black colour.(view1 get the parameters for view2 too)
Stack views use intrinsic content size, so use layout constraints to define the dimensions of the views.
There is an easy way to add constraints quickly (example):
[view1.heightAnchor constraintEqualToConstant:100].active = true;
Complete Code:
- (void) setup {
//View 1
UIView *view1 = [[UIView alloc] init];
view1.backgroundColor = [UIColor blueColor];
[view1.heightAnchor constraintEqualToConstant:100].active = true;
[view1.widthAnchor constraintEqualToConstant:120].active = true;
//View 2
UIView *view2 = [[UIView alloc] init];
view2.backgroundColor = [UIColor greenColor];
[view2.heightAnchor constraintEqualToConstant:100].active = true;
[view2.widthAnchor constraintEqualToConstant:70].active = true;
//View 3
UIView *view3 = [[UIView alloc] init];
view3.backgroundColor = [UIColor magentaColor];
[view3.heightAnchor constraintEqualToConstant:100].active = true;
[view3.widthAnchor constraintEqualToConstant:180].active = true;
//Stack View
UIStackView *stackView = [[UIStackView alloc] init];
stackView.axis = UILayoutConstraintAxisVertical;
stackView.distribution = UIStackViewDistributionEqualSpacing;
stackView.alignment = UIStackViewAlignmentCenter;
stackView.spacing = 30;
[stackView addArrangedSubview:view1];
[stackView addArrangedSubview:view2];
[stackView addArrangedSubview:view3];
stackView.translatesAutoresizingMaskIntoConstraints = false;
[self.view addSubview:stackView];
//Layout for Stack View
[stackView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = true;
[stackView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = true;
}
Note: This was tested on iOS 9
Swift 5.0
//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blue
imageView.heightAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text = "Hi World"
textLabel.textAlignment = .center
//Stack View
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.vertical
stackView.distribution = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing = 16.0
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
Based on #user1046037 answer.
In Swift 4.2
let redView = UIView()
redView.backgroundColor = .red
let blueView = UIView()
blueView.backgroundColor = .blue
let stackView = UIStackView(arrangedSubviews: [redView, blueView])
stackView.axis = .vertical
stackView.distribution = .fillEqually
view.addSubview(stackView)
// stackView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
// autolayout constraint
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.topAnchor),
stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
stackView.heightAnchor.constraint(equalToConstant: 200)
])
UIStackView uses constraints internally to position its arranged subviews. Exactly what constraints are created depends on how the stack view itself is configured. By default, a stack view will create constraints that lay out its arranged subviews in a horizontal line, pinning the leading and trailing views to its own leading and trailing edges. So your code would produce a layout that looks like this:
|[view1][view2]|
The space that is allocated to each subview is determined by a number of factors including the subview's intrinsic content size and it's compression resistance and content hugging priorities. By default, UIView instances don't define an intrinsic content size. This is something that is generally provided by a subclass, such as UILabel or UIButton.
Since the content compression resistance and content hugging priorities of two new UIView instances will be the same, and neither view provides an intrinsic content size, the layout engine must make its best guess as to what size should be allocated to each view. In your case, it is assigning the first view 100% of the available space, and nothing to the second view.
If you modify your code to use UILabel instances instead, you will get better results:
UILabel *label1 = [UILabel new];
label1.text = #"Label 1";
label1.backgroundColor = [UIColor blueColor];
UILabel *label2 = [UILabel new];
label2.text = #"Label 2";
label2.backgroundColor = [UIColor greenColor];
[self.stack1 addArrangedSubview:label1];
[self.stack1 addArrangedSubview:label2];
Note that it is not necessary to explictly create any constraints yourself. This is the main benefit of using UIStackView - it hides the (often ugly) details of constraint management from the developer.
You have to set you distribution type.
In your code, juste add:
self.stack1.distribution = UIStackViewDistributionFillEqually;
Or you can set the distribution directly in your interface builder.
For example:
Hope that helps ;)
Lapinou.
Following two lines fixed my issue
view.heightAnchor.constraintEqualToConstant(50).active = true;
view.widthAnchor.constraintEqualToConstant(350).active = true;
Swift version -
var DynamicView=UIView(frame: CGRectMake(100, 200, 100, 100))
DynamicView.backgroundColor=UIColor.greenColor()
DynamicView.layer.cornerRadius=25
DynamicView.layer.borderWidth=2
self.view.addSubview(DynamicView)
DynamicView.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView.widthAnchor.constraintEqualToConstant(350).active = true;
var DynamicView2=UIView(frame: CGRectMake(100, 310, 100, 100))
DynamicView2.backgroundColor=UIColor.greenColor()
DynamicView2.layer.cornerRadius=25
DynamicView2.layer.borderWidth=2
self.view.addSubview(DynamicView2)
DynamicView2.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView2.widthAnchor.constraintEqualToConstant(350).active = true;
var DynamicView3:UIView=UIView(frame: CGRectMake(10, 420, 355, 100))
DynamicView3.backgroundColor=UIColor.greenColor()
DynamicView3.layer.cornerRadius=25
DynamicView3.layer.borderWidth=2
self.view.addSubview(DynamicView3)
let yourLabel:UILabel = UILabel(frame: CGRectMake(110, 10, 200, 20))
yourLabel.textColor = UIColor.whiteColor()
//yourLabel.backgroundColor = UIColor.blackColor()
yourLabel.text = "mylabel text"
DynamicView3.addSubview(yourLabel)
DynamicView3.heightAnchor.constraintEqualToConstant(50).active = true;
DynamicView3.widthAnchor.constraintEqualToConstant(350).active = true;
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.Vertical
stackView.distribution = UIStackViewDistribution.EqualSpacing
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing = 30
stackView.addArrangedSubview(DynamicView)
stackView.addArrangedSubview(DynamicView2)
stackView.addArrangedSubview(DynamicView3)
stackView.translatesAutoresizingMaskIntoConstraints = false;
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor).active = true
For the accepted answer when you try to hide any view inside stack view, the constraint works not correct.
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:0x618000086e50 UIView:0x7fc11c4051c0.height == 120 (active)>",
"<NSLayoutConstraint:0x610000084fb0 'UISV-hiding' UIView:0x7fc11c4051c0.height == 0 (active)>"
)
Reason is when hide the view in stackView it will set the height to 0 to animate it.
Solution change the constraint priority as below.
import UIKit
class ViewController: UIViewController {
let stackView = UIStackView()
let a = UIView()
let b = UIView()
override func viewDidLoad() {
super.viewDidLoad()
a.backgroundColor = UIColor.red
a.widthAnchor.constraint(equalToConstant: 200).isActive = true
let aHeight = a.heightAnchor.constraint(equalToConstant: 120)
aHeight.isActive = true
aHeight.priority = 999
let bHeight = b.heightAnchor.constraint(equalToConstant: 120)
bHeight.isActive = true
bHeight.priority = 999
b.backgroundColor = UIColor.green
b.widthAnchor.constraint(equalToConstant: 200).isActive = true
view.addSubview(stackView)
stackView.backgroundColor = UIColor.blue
stackView.addArrangedSubview(a)
stackView.addArrangedSubview(b)
stackView.axis = .vertical
stackView.distribution = .equalSpacing
stackView.translatesAutoresizingMaskIntoConstraints = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Just add a button in xib file or storyboard and add connect this action.
#IBAction func test(_ sender: Any) {
a.isHidden = !a.isHidden
}
}
//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blueColor()
imageView.heightAnchor.constraintEqualToConstant(120.0).active = true
imageView.widthAnchor.constraintEqualToConstant(120.0).active = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.greenColor()
textLabel.widthAnchor.constraintEqualToConstant(self.view.frame.width).active = true
textLabel.heightAnchor.constraintEqualToConstant(20.0).active = true
textLabel.text = "Hi World"
textLabel.textAlignment = .Center
//Third View
let thirdView = UIImageView()
thirdView.backgroundColor = UIColor.magentaColor()
thirdView.heightAnchor.constraintEqualToConstant(120.0).active = true
thirdView.widthAnchor.constraintEqualToConstant(120.0).active = true
thirdView.image = UIImage(named: "buttonFollowMagenta")
//Stack View
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.Vertical
stackView.distribution = UIStackViewDistribution.EqualSpacing
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing = 16.0
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.addArrangedSubview(thirdView)
stackView.translatesAutoresizingMaskIntoConstraints = false;
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraintEqualToAnchor(self.view.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor).active = true
Improved on the answer by #Oleg Popov
Swift 5 version of Oleg Popov's answer, which is based on user1046037's answer
//Image View
let imageView = UIImageView()
imageView.backgroundColor = UIColor.blue
imageView.heightAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 120.0).isActive = true
imageView.image = UIImage(named: "buttonFollowCheckGreen")
//Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.yellow
textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.text = "Hi World"
textLabel.textAlignment = .center
//Stack View
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.vertical
stackView.distribution = UIStackView.Distribution.equalSpacing
stackView.alignment = UIStackView.Alignment.center
stackView.spacing = 16.0
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
In my case the thing that was messing with I would expect was that I was missing this line:
stackView.translatesAutoresizingMaskIntoConstraints = false;
After that no need to set constraints to my arranged subviews whatsoever, the stackview is taking care of that.
If you have many UIViews to add, you can use the following extension where you pass an array of UIViews where it will add them into the UIStackView by order
extension UIStackView {
func addArrangedSubviews(_ subviews: [UIView]) {
subviews.forEach{ self.addArrangedSubview($0) }
}
}
If you have want to add UIView at specific position, use this
yourStackView.insertArrangedSubview(yourView, at: index)
I just came across very similar problem. Just like mentioned before the stack view's dimensions depend one intrinsic content size of the arranged subviews. Here is my solution in Swift 2.x and following view structure:
view - UIView
customView - CustomView:UIView
stackView - UISTackView
arranged subviews - custom UIView subclasses
//: [Previous](#previous)
import Foundation
import UIKit
import XCPlayground
/**Container for stack view*/
class CustomView:UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
init(){
super.init(frame: CGRectZero)
}
}
/**Custom Subclass*/
class CustomDrawing:UIView{
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup(){
// self.backgroundColor = UIColor.clearColor()
print("setup \(frame)")
}
override func drawRect(rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()
CGContextMoveToPoint(ctx, 0, 0)
CGContextAddLineToPoint(ctx, CGRectGetWidth(bounds), CGRectGetHeight(bounds))
CGContextStrokePath(ctx)
print("DrawRect")
}
}
//: [Next](#next)
let stackView = UIStackView()
stackView.distribution = .FillProportionally
stackView.alignment = .Center
stackView.axis = .Horizontal
stackView.spacing = 10
//container view
let view = UIView(frame: CGRectMake(0,0,320,640))
view.backgroundColor = UIColor.darkGrayColor()
//now custom view
let customView = CustomView()
view.addSubview(customView)
customView.translatesAutoresizingMaskIntoConstraints = false
customView.widthAnchor.constraintEqualToConstant(220).active = true
customView.heightAnchor.constraintEqualToConstant(60).active = true
customView.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor).active = true
customView.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor).active = true
customView.backgroundColor = UIColor.lightGrayColor()
//add a stack view
customView.addSubview(stackView)
stackView.centerXAnchor.constraintEqualToAnchor(customView.centerXAnchor).active = true
stackView.centerYAnchor.constraintEqualToAnchor(customView.centerYAnchor).active = true
stackView.translatesAutoresizingMaskIntoConstraints = false
let c1 = CustomDrawing()
c1.translatesAutoresizingMaskIntoConstraints = false
c1.backgroundColor = UIColor.redColor()
c1.widthAnchor.constraintEqualToConstant(30).active = true
c1.heightAnchor.constraintEqualToConstant(30).active = true
let c2 = CustomDrawing()
c2.translatesAutoresizingMaskIntoConstraints = false
c2.backgroundColor = UIColor.blueColor()
c2.widthAnchor.constraintEqualToConstant(30).active = true
c2.heightAnchor.constraintEqualToConstant(30).active = true
stackView.addArrangedSubview(c1)
stackView.addArrangedSubview(c2)
XCPlaygroundPage.currentPage.liveView = view
Instead of coding all the constrains you could use a subclass that handles .intrinsicContentSize of UIView class in a simpler way.
This solution improves also Interface Builder a little in a way to support with "intrinsicWidth" and "intrinsicHeight" of views. While you could extend UIView's and have those properties available on all UIViews in IB its cleaner to subclass.
// IntrinsicView.h
#import UIKit
IB_DESIGNABLE
#interface IntrinsicView : UIView
-(instancetype)initWithFrame:(CGRect)rect;
#property IBInspectable CGSize intrinsic;
#end
// IntrinsicView.m
#import "IntrinsicView.h"
#implementation IntrinsicView {
CGSize _intrinsic;
}
- (instancetype)initWithFrame:(CGRect)frame {
_intrinsic = frame.size;
if ( !(self = [super initWithFrame:frame]) ) return nil;
// your stuff here..
return self;
}
-(CGSize)intrinsicContentSize {
return _intrinsic;
}
-(void)prepareForInterfaceBuilder {
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, _intrinsic.width,_intrinsic.height);
}
#end
Which means you can just allocate those IntrinsicView's and the self.frame.size is taken as intrinsicContentSize. That way it does not disturb the normal layout and you dont need to set constraint relations that don't even apply in full with UIStackViews
#import "IntrinsicView.h"
- (void)viewDidLoad {
[super viewDidLoad];
UIStackView *column = [[UIStackView alloc] initWithFrame:self.view.frame];
column.spacing = 2;
column.alignment = UIStackViewAlignmentFill;
column.axis = UILayoutConstraintAxisVertical; //Up-Down
column.distribution = UIStackViewDistributionFillEqually;
for (int row=0; row<5; row++) {
//..frame:(CGRect) defines here proportions and
//relation to axis of StackView
IntrinsicView *intrinsicView = [[IntrinsicView alloc] initWithFrame:CGRectMake(0, 0, 30.0, 30.0)];
[column addArrangedSubview:intrinsicView];
}
[self.view addSubview:column];
}
now you can go crazy with UIStackView's
or in swift + encoding, decoding, IB support, Objective-C support
#IBDesignable #objc class IntrinsicView : UIView {
#IBInspectable var intrinsic : CGSize
#objc override init(frame: CGRect) {
intrinsic = frame.size
super.init(frame: frame)
}
required init?(coder: NSCoder) {
intrinsic = coder.decodeCGSize(forKey: "intrinsic")
super.init(coder: coder)
}
override func encode(with coder: NSCoder) {
coder.encode(intrinsic, forKey: "intrinsic")
super.encode(with: coder)
}
override var intrinsicContentSize: CGSize {
return intrinsic
}
override func prepareForInterfaceBuilder() {
frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: intrinsic.width, height: intrinsic.height)
}
}
It is really not recommended to set a height constraint... If you can, never, never, never set a height! You need to check all the constraints of the views inside your UIStackView and be sure that there is constraints for bottom, top, leading and trailing. Someone said to me: it is like a guy pushing on walls. If he don't push on 4 sides, one of the wall will fall on him.
func configureHorizontalView(){
containerView.addSubview(horizontalStackView)
_ = horizontalStackView.anchor(top: secondCurrencyTextField.bottomAnchor,
left: containerView.leftAnchor,
bottom: nil,
right: containerView.rightAnchor,
topConstant: 40,
leftConstant: 30,
bottomConstant: 0,
rightConstant: 30,
widthConstant: 0,
heightConstant: 65)
}
func configureFirstDropDownlabel(){
//add a view to stackView with addArrangedSubview()
horizontalStackView.addArrangedSubview(firstDropDownlabel)
_ = firstDropDownlabel.anchor(top: horizontalStackView.bottomAnchor,
left: horizontalStackView.leftAnchor,
bottom: nil, right: nil,
topConstant: 40,
leftConstant: 30,
bottomConstant: 0,
rightConstant: 0,
widthConstant: 0,
heightConstant: 0)
firstDropDownlabel.widthAnchor.constraint(equalToConstant: 130).isActive = true
firstDropDownlabel.heightAnchor.constraint(equalToConstant: 65).isActive = true
}
Try below code:
UIView *view1 = [[UIView alloc]init];
view1.backgroundColor = [UIColor blackColor];
[view1 setFrame:CGRectMake(0, 0, 50, 50)];
UIView *view2 = [[UIView alloc]init];
view2.backgroundColor = [UIColor greenColor];
[view2 setFrame:CGRectMake(0, 100, 100, 100)];
NSArray *subView = [NSArray arrayWithObjects:view1,view2, nil];
[self.stack1 initWithArrangedSubviews:subView];

UILabel resizes its SuperView?

I have the following view which contains a UILabel:
class MyView : UIView {
func viewDidLoad() {
self.autoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth
bottomView = UIView(frame: CGRectMake(self.bounds.origin.x, self.bounds.origin.y + self.imageView!.bounds.size.width, self.bounds.size.width, self.bounds.size.height - self.imageView!.bounds.size.height))
// bottomView frame calculation is: (0.0, 355.0, 355.0, 130.0)
bottomView?.backgroundColor = UIColor.greenColor()
bottomView?.autoresizingMask = UIViewAutoresizing.FlexibleWidth
bottomView?.clipsToBounds = true
self.addSubview(self.bottomView!)
var descriptionRect: CGRect = CGRectInset(self.bottomView!.bounds, leftRightInset, 20/2)
let descriptionLabel = UILabel()
descriptionLabel.numberOfLines = 3
descriptionLabel.autoresizingMask = UIViewAutoresizing.FlexibleWidth
descriptionLabel.font = UIFont(name: MGFont.helvetica, size: 22)
descriptionLabel.textColor = UIColor.whiteColor()
descriptionLabel.textAlignment = NSTextAlignment.Left
descriptionLabel.backgroundColor = UIColor.blueColor()
var paragraphStyle:NSMutableParagraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 1.0
paragraphStyle.lineBreakMode = NSLineBreakMode.ByTruncatingTail
let attributes = [NSParagraphStyleAttributeName : paragraphStyle]
descriptionLabel.attributedText = NSAttributedString(string: previewCard.title, attributes:attributes)
bottomView?.addSubview(descriptionLabel)
descriptionLabel.bounds = descriptionRect
descriptionLabel.sizeToFit()
descriptionLabel.center = CGPointMake(bottomView!.bounds.width/2, bottomView!.bounds.height/2 - hotelNameLableHeight/2)
}
}
The height of the bottomView should always be fixed.
MyView is resized during runtime. This means that the green bottom view also increases in size.
Here is the result when the label has two and three lines:
It appears that the UILabel resizes its super view.
Note that I do not use AutoLayout.
override func layoutSubviews() {
super.layoutSubviews()
}
How can I prevent the UILabel from resizing its SuperView?
Edit: I also tried to comment bottomView?.clipsToBounds = true
Override setFrame: and setBounds: of the super view (subclass if they're plain UIViews), add breakpoints, and see the stack trace to find out what's causing them to resize.
There is no need to set the autoResizingMask on the label, just set the frame and it will get automatically centered. And of course you can set the insets for the UILabel accordingly. I've add below testing code FYI:
override func viewDidLayoutSubviews() {
addTestView(CGRectMake(0, 200, view.bounds.width, 50), labelStr: "I am a short testing label")
addTestView(CGRectMake(0, 260, view.bounds.width, 50), labelStr: "I am a very longlonglonglonglonglonglong testing label")
addTestView(CGRectMake(0, 320, view.bounds.width, 50), labelStr: "I am a very longlonglonglonglonglonglonglonglonglonglonglong testing label. Will be truncated")
}
func addTestView(frame:CGRect, labelStr: String){
let bottomView = UIView(frame:frame)
bottomView.backgroundColor = UIColor.greenColor()
bottomView.autoresizingMask = UIViewAutoresizing.FlexibleWidth
bottomView.clipsToBounds = true
view.addSubview(bottomView)
var label = UILabel(frame: bottomView.bounds)
label.textAlignment = NSTextAlignment.Left
label.numberOfLines = 0
label.text = labelStr
bottomView.addSubview(label)
}

Resources