Adding Subview creates layout issue - ios

I'm programmatically adding a view inside another view like so:
func addViewControllerToSpecificView( view: UIView, controller: UIViewController) {
controller.willMoveToParentViewController(self)
view.addSubview(controller.view)
self.addChildViewController(controller)
controller.didMoveToParentViewController(self)
}
The issue is that the parent view ends up being wider than it should be (the width of the screen).
When I don't load the subview using the above method, the positioning is perfect (no extra padding). I have no idea why it's adding an extra ~30px

add following line in your code....
controller.view.frame = self.view.layer.bounds;
this will fix the problem.

I dont know that are you adding Auto layout constraint for this view.
Please check that this layout constraints are added in your code, if not then add below constraint after you add childViewController.
let metrices = ["width" : view.bounds.size.width, "height" : view.bounds.size.height]
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[childView(width)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: metrices, views: ["childView" : controller.view]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[childView(height)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: metrices, views: ["childView" : controller.view]))

Related

Add subviews programmatically to ScrollView using Visual Format Language

scroll view - layout programmatically - swift 3
Dear friends: I hope someone could revise this project and help me before my brain been burned. Thanks in advance.
The task: Horizontal Scroll - Layout an array of 4 images, square of 240 x 240 and 20 of spacing. The constraints for the scroll view set directly in the storyboard, but the images subviews had been added programmatically using Visual Format Language. Content size for scroll suppose done by this constraints.
What I have done: Set the array of images, create de ImageView programmatically and add the array using a for in loop. Create the constraints using the visual format. A way to do this can be found in this article: http://www.apeth.com/iOSBook/ch20.html.
Here the link to the project in GitHub
https://github.com/ricardovaldes/soloScrollEjercicio
Constraints for the ScrollView added directly in the storyboard.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myScroll: UIScrollView!
var carsArray = [UIImage]()
var constraints = [NSLayoutConstraint]()
override func viewDidLoad() {
super.viewDidLoad()
carsArray = [#imageLiteral(resourceName: "fasto1"), #imageLiteral(resourceName: "fasto2"), #imageLiteral(resourceName: "fasto3"), #imageLiteral(resourceName: "fasto4")]
var const = [NSLayoutConstraint]()
var views: [String: UIView]
var previous: UIImageView? = nil
for index in 0..<carsArray.count{
let newImageView = UIImageView()
newImageView.image = carsArray[index]
newImageView.translatesAutoresizingMaskIntoConstraints = false
myScroll.addSubview(newImageView)
self.myScroll.setNeedsLayout()
views = ["newImageView": newImageView, "myScroll": myScroll]
if previous == nil{
const.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:|[newImageView(240)]", metrics: nil, views: views))
}
else{
const.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:[previous]-20-[newImageView(240)]", metrics: nil, views: ["newImageView": newImageView, "previous": previous!]))
}
previous = newImageView
const.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "H:[previous]|", metrics: nil, views: ["previous": newImageView]))
const.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: "V:|[newImageView(240)]|", metrics: nil, views: views))
}
NSLayoutConstraint.activate(const)
}
}
Even though I have tried a lot of combinations I have the same error:
2018-04-29 21:24:34.347466-0500 soloScrollEjercicio[12002:1665919] [LayoutConstraints] Unable to simultaneously satisfy constraints.
each loop round, you add a right side pin constraint to scroll view for every new
added imageView, but there is 20 points between each other.
|-previous-20-newOne-| ** second round loop **
-20-newOne'-| ** third round loop **
this breaks imageView width(240) constraint
one way deal with it:
only add right side pin constraint to the last imageView.
scroll view constraint in your main storyboard also has a break.
+-------------------+
| | |
|-scroll view (240)-|
the bottom one with vertical spacing to super view should not be there. it would
break the scroll view height(240), so delete it will be fine.
maybe you should try:
set it's constraint priority to 999, or some other value not equal to
1000
uncheck installed box
delete it
and now, your scroll view should be OK.
p.s. I found your reference book is based on iOS 6? in the year 2018, starts from iOS 10 or iOS 11 may be a better choice.
happy hacking :)

Swift get error when addConstraints for a UITextField with Visual Format Language

I want to add an UITextField into an UITableCell and set both it's width and height 100%;
My swift code :
nameTextField.translatesAutoresizingMaskIntoConstraints = false
addSubview( nameTextField )
let views = [
"name" : nameTextField
];
let widthLayoutConstraint = NSLayoutConstraint.constraintsWithVisualFormat( "H:|-[name]-|", options: [], metrics: nil, views: views )
nameTextField.addConstraints( widthLayoutConstraint )
let heightLayoutConstraint = NSLayoutConstraint.constraintsWithVisualFormat( "V:|-[name]-|", options: [], metrics: nil, views: views )
nameTextField.addConstraints( heightLayoutConstraint )
I got the follow error message while the table will show:
2016-07-31 19:16:43.027 NOIB[40193:1889520] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view. Does the constraint reference something from outside the subtree of the view? That's illegal. constraint:<NSLayoutConstraint:0x7ffbfa70f480 UITextField:0x7ffbfa41dff0.leading == NOIB.NameCell:0x7ffbfa41da10'NameCell'.leadingMargin> view:<UITextField: 0x7ffbfa41dff0; frame = (0 0; 0 0); text = ''; clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x7ffbfa41e5c0>>'
It's odd that you are calling addSubview without specifying a view. Usually it is used like:
cell.contentView.addSubview(nameTextField)
Since you aren't specifying a view, then it must be using self.
Two views are involved in the constraints that are returned by your VFL: your nameTextField and its superview. In VFL, "|" represents the superview so it doesn't have to be explicitly included in the view dictionary that you pass in. You need to add the constraints to the view that is higher in the hierarchy. In your case, you need to add the constraints to the superview of nameTextField, which is self. So you could probably do:
self.addConstraints(widthLayoutConstraint)
self.addConstraints(heightLayoutConstraint)
As of iOS 8, there is an easier way. The constraints know which views they need to be added to, so you just have to set their active properties to true. VFL can return multiple constraints, so you need to make all of them active. You can use the class method activateConstraints of NSLayoutConstraint to activate multiple constraints:
let widthLayoutConstraint = NSLayoutConstraint.constraintsWithVisualFormat( "H:|-[name]-|", options: [], metrics: nil, views: views )
NSLayoutConstraint.activateConstraints(widthLayoutConstraint)
let heightLayoutConstraint = NSLayoutConstraint.constraintsWithVisualFormat( "V:|-[name]-|", options: [], metrics: nil, views: views )
NSLayoutConstraint.activateConstraints(heightLayoutConstraint)

Constraints not respected when loading from XIB

So, I have the following XIB
This XIB when loaded as a tableviewcell looks like this
I've since decided that I will not need a TableView, so I changed my XIB class from UITableViewCell to UIView. In a ViewController I added this code to viewDidLoad()
var nView = MyChartView.instanceFromNib() as! MyChartView
self.view.addSubview(nView)
And I got this as a result
As you can see, it ignores the margins and continues to right side (ignore the red color since I was using it to try and debug the problem. No chart data is not the problem either). I've printed the xib's frame width and I've seen that it's quite a bit bigger than the screen size, but I've not been able to fix it. Anyone can figure out the problem?
When you add a subView programmatically, you should also add constraints between the subView and its superView
var nView = MyChartView.instanceFromNib() as! MyChartView
self.view.addSubview(nView)
nView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[nView]|", options: [], metrics: nil, views: ["nView": nView]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[nView]|", options: [], metrics: nil, views: ["nView": nView]))
You haven't constrained the MyChartView instance's width, either by specifying an exact size when you add the subview, or programmatically adding constraints between the MyChartView instance and its superview. Without doing one or the other of these, the view's dimensions will match whatever they are in the xib.

How to make an evenly spaced row of images with AutoLayout? [duplicate]

This question already has answers here:
Evenly space multiple views within a container view
(29 answers)
Closed 6 years ago.
I've just started working on this card game in Swift, and I was trying to figure out how to lay out a row of 6 cards horizontally near the top of the screen.
I've tried putting 6 imageViews in a stack, but my manual constraints ended up causing the last image to stretch to the edge:
Could someone show me how to set up a row of imageViews so that each of them has a fixed width and they're all centered? I'm kinda new to AutoLayout, so screenshots would be helpful.
I'd recommend using UIStackView. Have a look at the following Ray Wenderlich tutorial:
https://www.raywenderlich.com/114552/uistackview-tutorial-introducing-stack-views
However, before moving on to more complex views such as the aforementioned stack view; you should learn to use auto layout to avoid making any silly mistakes.
Here is another great tutorial from the same site:
https://www.raywenderlich.com/115440/auto-layout-tutorial-in-ios-9-part-1-getting-started-2
EDIT:
Improved answer:
UIStackView allows you to arrange elements with ease, in a row or in a column. This saves you a lot of time and makes your storyboard look a little bit cleaner as less constraints are needed.
The description of UIStackView on developer.apple.com:
The UIStackView class provides a streamlined interface for laying out a collection of views in either a column or a row. Stack views let you leverage the power of Auto Layout, creating user interfaces that can dynamically adapt to the device’s orientation, screen size, and any changes in the available space. The stack view manages the layout of all the views in its arrangedSubviews property. These views are arranged along the stack view’s axis, based on their order in the arrangedSubviews array. The exact layout varies depending on the stack view’s axis, distribution, alignment, spacing, and other properties.
UIStackViews functionality doesn't stop at the simplified view alignement. Indeed, you can also alter the properties that define the stack view.
The axis property determines the stack’s orientation, either
vertically or horizontally.
The distribution property determines the layout of the arranged views
along the stack’s axis.
The alignment property determines the layout of the arranged views
perpendicular to the stack’s axis.
The spacing property determines the minimum spacing between arranged
views.
The baselineRelativeArrangement property determines whether the
vertical spacing between views is measured from the baselines.
The layoutMarginsRelativeArrangement property determines whether the
stack view lays out its arranged views relative to its layout margins.
Despite the advantages mentioned above, UIStackView has limits.
The UIStackView is a nonrendering subclass of UIView; that is, it does
not provide any user interface of its own. Instead, it just manages
the position and size of its arranged views. As a result, some
properties (like backgroundColor) have no effect on the stack view.
Similarly, you cannot override layerClass, drawRect:, or
drawLayer:inContext:.
Note that UIStackView can't scroll. If you ever need it to scroll, embed a stack view within a UIScrollView.
Hope this helps!
I recommend pure coding, you learn more.
If you specify that all your cards are equal width and height, it will ensure the last card doesn't get stretched.
This is how I often build my UI:
import UIKit
class ViewController: UIViewController {
var container:UIView = UIView();
var card1:UIView! = nil;
var card2:UIView! = nil;
var card3:UIView! = nil;
var card4:UIView! = nil;
var card5:UIView! = nil;
var card6:UIView! = nil;
override func viewDidLoad() {
super.viewDidLoad()
self.initViews();
self.initConstraints();
}
func cardView() -> UIView
{
let card = UIView();
card.backgroundColor = UIColor.orangeColor();
return card;
}
func initViews()
{
self.card1 = self.cardView();
self.card2 = self.cardView();
self.card3 = self.cardView();
self.card4 = self.cardView();
self.card5 = self.cardView();
self.card6 = self.cardView();
self.container.addSubview(self.card1);
self.container.addSubview(self.card2);
self.container.addSubview(self.card3);
self.container.addSubview(self.card4);
self.container.addSubview(self.card5);
self.container.addSubview(self.card6);
self.view.addSubview(self.container);
}
func initConstraints()
{
self.container.translatesAutoresizingMaskIntoConstraints = false;
self.card1.translatesAutoresizingMaskIntoConstraints = false;
self.card2.translatesAutoresizingMaskIntoConstraints = false;
self.card3.translatesAutoresizingMaskIntoConstraints = false;
self.card4.translatesAutoresizingMaskIntoConstraints = false;
self.card5.translatesAutoresizingMaskIntoConstraints = false;
self.card6.translatesAutoresizingMaskIntoConstraints = false;
var views = [String: AnyObject]();
views["container"] = self.container;
views["card1"] = self.card1;
views["card2"] = self.card2;
views["card3"] = self.card3;
views["card4"] = self.card4;
views["card5"] = self.card5;
views["card6"] = self.card6;
self.view.addConstraint(NSLayoutConstraint(item: self.container, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 0.0));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[container]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[card1(60)]-10-[card2(==card1)]-10-[card3(==card1)]-10-[card4(==card1)]-10-[card5(==card1)]-10-[card6(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card1(100)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card2(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card3(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card4(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card5(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
self.container.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[card6(==card1)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views));
}
}
You end up with something like this:

create UIbutton programmatically with constraints in swift

Thanks in advance for the help.
I want to create a new custom UIButton every time I click an ADD button.
Once I click the add button, I want the add button to slide over and make room for the new button. I want this to happen every time, then create a new row once the buttons fill up the horizontal space of the view
How would I got about doing this?
Note:
I definitely understand how to create a button programmatically, its the constraints/way of getting the buttons to animate sliding over, and spacing correctly that I don't understand
Thank you so much
The image is a quick idea of what I want it to look like. Top row is before I added several buttons, and bottom row is having so many buttons a new row is required
To be able to animate the views you have to set the constant attribute of your constraint variable(the value and direction depends of the attribute, of course), and later you have to call layoutIfNeeded() inside of an UIView animation block.
Code sample:
...
let newButton = UIButton()
containerView.addSubview(newButton)
...
// after adding the button
let horizontalSpace = ButtonWidth + horizontalSpace * 2
let newLeft = lastButtonX + 2 * (buttonWidth + horizontalSpace) + horizontalSpace
let newLeftConstraint = NSLayoutConstraint(item: newButton, attribute: .Left, relatedBy: .Equal, toItem: lastButton, attribute: .Right, multiplier: 0, constant: horizontalInset)
lastButton = newButton
if newLeft + addButtonWidth >= screenWidth {
containerView.layoutIfNeeded()
addButtonLeftConstraint.constant = horizontalSpace
addButtonTopConstraint.constant = buttonRowsNumber * rowHeight + verticalSpace
UIView.animateWithDuration(animationTime) {
containerView.layoutIfNeeded()
}
} else {
containerView.layoutIfNeeded()
addButtonLeftConstraint = newLeft
UIView.animateWithDuration(animationTime) {
containerView.layoutIfNeeded()
}
}
NOTES:
You'll need to keep a var for each constraint you want to animate later on. And depending of you layout behavior, you also need a var to the last button, so you can make the measurements of the positions.
the constant number 0 represents the initial state of the constraint, when it was added and created, so based in this initial state, the view will be moved from it's initial position(starting on the left, or right or whatever initial place you choose).
I suggest you to create the NSLayoutConstraint variables with the class constructor rather than using the visual language, as it generates an array of NSLayoutConstraints and this makes the detections of constraints harder for one specific constraint.
And the final note: I suggest one AL small library to manipulate the constraints more easily trough code, as you can see, constructing NSLayoutConstraints can be very boring and hard to maintain. As you're using Swift, please, take a look at this project: https://github.com/robb/Cartography
I've been using it in my projects, it's really helpful for those situations.
You can add constraint to any view by applying below code.
self.view.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[myLabel]-|", options: nil, metrics: nil, views: viewsDict))
self.view.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-[myButton]-|",
options: nil, metrics: nil, views: viewsDict))
self.view.addConstraints(
NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-[myLabel]-[myButton]-|", options: nil, metrics: nil,
views: viewsDict))
There is a good tutorial you can follow from :http://makeapppie.com/2014/07/26/the-swift-swift-tutorial-how-to-use-uiviews-with-auto-layout-programmatically/

Resources