How do I layout view which is on storyboard programmatically? - ios

I try to use auto layout programmatically by visual format language on a view which is on storyboard but failed. The code is as below:
(Btw, the storyboard which shows bView is not important, so I didn't upload storyboard's picture. And I didn't setup any constrain on storyboard.)
class ViewController: UIViewController {
#IBOutlet var bView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
bView.translatesAutoresizingMaskIntoConstraints = false
var allConstraints = [NSLayoutConstraint]()
let vC = NSLayoutConstraint.constraintsWithVisualFormat("V:|-[bView]-|", options: [], metrics: nil, views: ["bView":bView])
let hC = NSLayoutConstraint.constraintsWithVisualFormat("H:|-[bView]-|", options: [], metrics: nil, views: ["bView":bView])
allConstraints += vC
allConstraints += hC
NSLayoutConstraint.activateConstraints(allConstraints)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
}
}
But when it executes, it shows error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse constraint
format: Unable to interpret '|' character, because the related view
doesn't have a superview V:|-[bView]-|
^'
I guess maybe the superView (the view of the viewController) did add on the mainWindow. So I tried to fix this problem by moving the code to viewWillLayoutSubviews(). As Below:
class ViewController: UIViewController {
#IBOutlet var bView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
bView.translatesAutoresizingMaskIntoConstraints = false
var allConstraints = [NSLayoutConstraint]()
let vC = NSLayoutConstraint.constraintsWithVisualFormat("V:|-[bView]-|", options: [], metrics: nil, views: ["bView":bView])
let hC = NSLayoutConstraint.constraintsWithVisualFormat("H:|-[bView]-|", options: [], metrics: nil, views: ["bView":bView])
allConstraints += vC
allConstraints += hC
NSLayoutConstraint.activateConstraints(allConstraints)
}
}
Then it shows a really funny wrong result.
So, how do I layout a view which is on storyboard programmatically?
What's more, there's a funny result like this:
(It's not related to the question directly, but you can try it for fun!)
With code:
class ViewController: UIViewController {
#IBOutlet var bView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
bView.translatesAutoresizingMaskIntoConstraints = false
}
}

If you are trying to constrain the blue rectangle, then your first problem is that your bView outlet is incorrectly wired to your top level view (the white background). Delete that connection in the Connections Inspector and then wire the blue view to bView.
If you add a view in Interface Builder, but don't give it any constraints, then Xcode will add 4 constraints at build time. If you click on your view, you will see the following message in the Size Inspector:
The selected views have no constraints. At build time, explicit left,
top, width, and height constraints will be generated for the view.
So, your view is already fully specified. If you add any more constraints in code you will have conflicts.
So, how can you add constraints in code for a view laid out in the Storyboard? You need to do the following:
Add explicit constraints in the Storyboard for left, top, width, and height.
Edit these 4 constraints in the Size Inspector and check the box next to Placeholder [] Remove at build time.
Then, these 4 constraints will be removed at build time leaving the view without constraints. Now your code is free to add constraints.

Related

How to update constraints after adding UIView from xib on UIScrollView in swift?

I have a UIViewScroll(background color is blue) in view controller. I need a UIView(background color is white) that were from Xib. The Xib view has a UILabel(background color is green) with constraints. Now, the problem is UILabel constraints not applied after adding it to scrollView. How to add UIView without loss of constraints? Refer following screenshots and code.
Note:
I need to just update constraints of the sub views of the UIView without using IBOutlets of NSLayoutConstraints.
UIView on Xib:
Code:
class ViewController: UIViewController {
#IBOutlet var scrollView: UIScrollView!
var profileView:UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.profileView = UINib.init(nibName: "ProfileView", bundle: nil).instantiate(withOwner: self)[0] as! UIView
self.scrollView.addSubview(self.profileView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.profileView.layer.frame.size = CGSize(width: self.scrollView.frame.width, height: self.profileView.frame.height)
self.profileView.layer.position = CGPoint(x: self.scrollView.frame.width/2, y: (self.profileView.frame.height/2)+10)
}
}
Output:
Update: More Information
I am aware to set contentSize of the scrollView. I used layer properties of UIView for manipulating height and width of the UIView. Instead of changing height and width also I need to update constraints of the sub views of UIView.
This is an example for understanding. But, In real I will be add more views like that.
Github Repository :
https://github.com/RAJAMOHAN-S/ScrollViewTest
Required output:
Your best bet is to set the constraint of self.profileView programmatically, I've added an example below to get you started.
class TestVC: UIViewController {
#IBOutlet var scrollView: UIScrollView!
private var profileView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.profileView = UINib.init(nibName: "ProfileView", bundle: nil).instantiate(withOwner: self)[0] as! UIView
self.configureProfileView()
}
private func configureProfileView() -> Void {
self.profileView.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.addSubview(self.profileView)
self.profileView.widthAnchor.constraint(equalTo: self.scrollView.widthAnchor).isActive = true
self.profileView.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// Pin the profile view to the top of the scrollView
self.profileView.topAnchor.constraint(equalTo: self.scrollView.topAnchor).isActive = true
}
}
More information can be found here too: https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html

How to stack custom views inside a UIStackView

Note: I'm pretty new working with iOS UI.
I want to create a custom view that stacks a custom view inside.
So I created the custom UIStackView
class CustomStackView: UIStackView {
func addItem(color:UIColor){
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "RowView", bundle: bundle)
let rowView = RowView();
let view = nib.instantiate(withOwner: rowView, options: nil).first as! UIView
rowView.addSubview(view)
rowView.view.backgroundColor = color;
addArrangedSubview(rowView)
}
}
class RowView :UIView{
#IBOutlet var view: UIView!
override public var intrinsicContentSize: CGSize {
return CGSize(width: view.frame.width,height:view.frame.height)
}
}
in the RowView.xib I created a simple layout for testing:
Simulated Metrics = Freeform
Height = 100
And the ViewController.swift:
class ViewController: UIViewController {
#IBOutlet weak var customStackView: CustomStackView!
#IBOutlet weak var constraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
customStackView.addItem(color: UIColor.red)
customStackView.addItem(color: UIColor.blue)
customStackView.addItem(color: UIColor.green)
}
#IBAction func click(_ sender: Any) {
constraint.constant = -customStackView.frame.height
UIView.animate(withDuration: 4, animations: {
self.view.layoutIfNeeded();
},completion:nil)
}
}
The result:
The first and second item are displayed correctly but the third is higher than expected.
In addition if I click the button (which should hide the Stackview) keep the "extra" height visible:
How can I fix that?
Edit: Tried the #KristijanDelivuk solution adding a trailing view. And didn't work. Adding cyan color to the view I got this result:
override func viewDidLoad() {
super.viewDidLoad()
customStackView.addItem(color: UIColor.red)
customStackView.addItem(color: UIColor.blue)
customStackView.addItem(color: UIColor.green)
let view = UIView();
view.heightAnchor.constraint(equalToConstant: 50).isActive = true;
view.backgroundColor = UIColor.cyan;
customStackView.addArrangedSubview(view)
}
You can try adding an empty UIView as your last element of UIStackView:
So your hierarchy should look something like this:
- STACKVIEW
-- 1ST ADDED CUSTOM VIEW
-- 2ND ADDED CUSTOM VIEW
-- 3RD ADDED CUSTOM VIEW
-- EMPTY UIVIEW
Empty UIView will take all unallocated space from 3rd view and all should be displayed correctly.
For repositioning button after hiding/showing stackview you can create for example "top constraint" and then on tap change top constraint height to (-) stackview.height or (+) stackview.height - This shouldn't be any problem.

Sliding-In UIView From Bottom

I am trying to build a custom-looking action sheet. I thought the easiest way would be creating a view as a subview and assign constraint of subview's top to superview's bottom. And at the same time assigning a cover view with some opacity. Thus, I could have different versions of subview and I can initialise the necessary one and slide it.
I couldn't find anything useful for Swift, so, using this obj-c answer, I tried to convert it to Swift. I achieved the opaque background with this however translating constraints doesn't seem to work.
var coverView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
coverView.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
coverView.alpha = 1.0
self.view.addSubview(coverView)
self.view.bringSubviewToFront(coverView)
}
//doesn't work
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[coverView]|", options: kNilOptions, metrics: nil, views: NSDictionaryOfVariableBindings(coverView)))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[coverView]|", options: kNilOptions, metrics: nil, views: NSDictionaryOfVariableBindings(coverView)))
I got confused on instantiating the view and applying transition animation. If I choose to create a UIView under ViewController, I cannot adjust constraints to adjust equal width of subview to superview.
How can I use the UIView that I created as a Subview (in Storyboard) and then adjust its width constraints so the UI doesn't bug? Also, how can I apply the transition animation so it seems natural?
This link should be here...
I suggested you use UIView xib file and design your view then load in your view controller.
Ex:
Step 1:
Create xib for view
Step 2:
Set background color black for this view, opacity 62% and Alpha = 1
Step 3:
Take new simple UIView and Design your actual view and set constraint.
For Exp:
In your case set view in bottom.
Step 4:
Load xib in view controller.
class calendarViewController: UIViewController
{
var popUpView: popUpView!
override func viewDidLoad() {
super.viewDidLoad()
bookingConfirmView = NSBundle.mainBundle().loadNibNamed("popUpView", owner: self, options: nil).first as! popUpView
// Set Delegate for xib textField
self.popUpView.Name.delegate = self
self.popUpView.MobileNo.delegate = self
}
}
Step 5:
Add this line to where you want to populate view.
self.view.addSubview(bookingConfirmView)
self.bookingConfirmView.frame = self.view.bounds

How to make a UITextView full width and height of containing UIViewController

I have UITextView that I would like to make the same height and width of it's container. It is in a simple UIViewContainer.
I tried doing the following:
override public func viewDidLoad() {
self.edgesForExtendedLayout = .None
resultText.text = result
resultText.frame = view.frame
}
This seems to work for portrait but not landscape.
All I am trying to do is make the UITextView take up all the space of it's container.
If I could find the answer in Objective-C I could easily translate it to Swift. I am just looking for the answer for iOS.
I suggest you to use auto layout
Then click add 4 constraints.
If any warning,
Click Update Frames
Autolayout is your friend - It can be done easily using Interface Builder, or in code:
override public func viewDidLoad() {
super.viewDidLoad()
self.edgesForExtendedLayout = .None
resultText.text = result
// add vertical constraints to pin the view to the superview edge
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0.0-[resultText]-0.0-|", options: nil, metrics: nil, views: ["resultText": resultText]))
// add horizontal constrains to pin the view to the superview edge
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0.0-[resultText]-0.0-|", options: nil, metrics: nil, views: ["resultText": resultText]))
}

Programmatically retrieve the width and height of a UIView using Auto Layout and NSLayoutConstraints?

How do you get the width and height of a UIView who's size and position are set using Auto Layout and Apple's Visual Format Language?
Here's the code (view is just the variable from UIViewController):
// Create and add the view
var stageView = UIView()
stageView.setTranslatesAutoresizingMaskIntoConstraints(false) // Since I'm using Auto Layout I turn this off
view.addSubview(stageView)
// Create and add constraints to the containing view
let viewsDictionary = ["stageView":stageView]
let horizontalConstraints: NSArray = NSLayoutConstraint.constraintsWithVisualFormat("H:|-150-[stageView]-150-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
let verticalConstraints: NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:|-100-[stageView]-150-|", options: NSLayoutFormatOptions.AlignAllCenterX, metrics: nil, views: viewsDictionary)
view.addConstraints(horizontalConstraints)
view.addConstraints(verticalConstraints)
println("stageView.frame=\(stageView.frame)")
and got:
stageView.frame=(0.0,0.0,0.0,0.0)
so I tried:
let fittingSize = stageView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
println("fittingSize=\(fittingSize)")
and got:
fittingSize=(0.0,0.0)
I can't seem to find a way to get the size. I'm able to add subviews to stageView that place just fine using Auto Layout and Visual Format Language, but I can't get width and height for stageView which I need to further position those subviews.
Any ideas?
You have a few options:
You can force the layout engine to size the views immediately by calling setNeedsLayout and then call layoutIfNeeded. This is not recommended because it's inefficient and any manual frames required for layout might not have been set yet. You can read more about this approach on my answer to this question.
You can also wait until the subviews have been updated in the view controller:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
println("stageView.frame = \(stageView.frame)")
}
If you want to know within a UIView subclass (or more often, a UITableViewCell subclass, you can check after layoutSubviews has run:
override func layoutSubviews() {
super.layoutSubviews()
println("self.frame = \(self.frame)")
}
You need to check the frame inside viewDidLayoutSubviews.
This function run after constraint calculation
Its suppose to look something like this
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
//Print frame here
}

Resources