I want to add a staticUIView in the bottom of my UIViewController. This is a story board app written in swift. in my viewDidLoad() I did something like this.
self.vWindow = UIApplication.sharedApplication().keyWindow
let mainBound = CGRectMake(0, self.view.frame.size.height-135, self.view.frame.size.width, 135)//CGSizeMake(UIScreen.mainScreen().bounds.size.width, 135.0)
self.vwVideo = UIView.init(frame: mainBound)
self.vwVideo.backgroundColor = UIColor.blackColor()
self.vWindow.addSubview(self.vwVideo)
But this adds nothing to my viewController. Why is that? Please help me.
Thanks
let vWindow = UIApplication.sharedApplication().keyWindow
let mainBound = CGRectMake(0, self.view.frame.size.height-135, self.view.frame.size.width, 135)//CGSizeMake(UIScreen.mainScreen().bounds.size.width, 135.0)
self.vwVideo = UIView.init(frame: mainBound)
self.vwVideo.backgroundColor = UIColor.blackColor()
vWindow!.addSubview(self.vwVideo)
Use this code it will add your view in window at the bottom side.
You shouldn't be attaching views directly to the window, add it to the ViewController.
try something like this:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// without autolayout
let header = UIView(frame: CGRectMake(0,0, view.bounds.size.width, 50))
header.backgroundColor = UIColor.redColor()
view.addSubview(header)
// with auto layout
let footer = UIView()
footer.backgroundColor = UIColor.blackColor()
footer.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(footer)
NSLayoutConstraint(item: footer, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 50).active = true
NSLayoutConstraint(item: footer, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1.0, constant: 0).active = true
NSLayoutConstraint(item: footer, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1.0, constant: 0).active = true
NSLayoutConstraint(item: footer, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1.0, constant: 0).active = true
}
Here you have two ways of attaching the view, One with autolayout and one with a fixed size and position.
If you want to add complex views, you may be better creating a nib.. .a view in storyboard which you can load when required.
If you need this view to appear in many places you could look at instead attaching it to a parent view, maybe something like a navigation controller or paging view controller, but you would need to manage when it should and shouldn't be displayed.
you are adding your created view to window i think. You need to that view to your current content view something like,
self.view.addSubView(self.vwVideo)
And you not need to take window and bounds of it, you can simply achieve it something like,
let bottomView: UIView = UIView(frame: CGRectMake(0, self.view.frame.size.height - 135, self.view.frame.size.width, 135))
self.view!.addSubview(bottomView)
Hope this will help :)
Its better to do it using AutoLayout.
Add your view to superview
view.addSubview(footerView)
then disable auto conversion of masks to constraints using
footerView.translatesAutoresizingMaskIntoConstraints = false
then add the constraint using following lines of code
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: [], metrics: nil, views: ["view":footerView]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[view(135)]-0-|", options: [], metrics: nil, views: ["view":footerView]))
Related
I have two UIViewControllers with Storyboard identifiers "vc0" and "vc1", which I am trying to instantiate objects of within a UIScrollView with paging enabled such that the two view controllers are adjacent to each other. The goal is to have the user swipe left and right to swipe between view controllers, similar to SnapChat.
When running my code, the first page of the scrollview contains the first view controller, while the second page contains nothing at all. I am assuming that this is because the first view controller is overlapping the second. How can I alter my constraints (or anything at all) to fix this issue, so that the right edge of vc0 meets with the left edge of vc1?
The UIScrollView is contained within a UIView, within a view controller. Here is the viewDidLoad() which contains all the relevant code. Please let me know of any additional helpful information I should provide.
override func viewDidLoad()
{
super.viewDidLoad()
let screenWidth = self.view.frame.width
let screenHeight = self.view.frame.height
let vc0 = self.storyboard?.instantiateViewControllerWithIdentifier("vc0")
self.addChildViewController(vc0!)
self.scrollView.addSubview(vc0!.view)
vc0!.didMoveToParentViewController(self)
let vc1 = self.storyboard?.instantiateViewControllerWithIdentifier("vc1")
var frame1 = vc1!.view.frame
frame1.origin.x = self.view.frame.size.width
vc1!.view.frame = frame1
self.addChildViewController(vc1!)
self.scrollView.addSubview(vc1!.view)
vc1!.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height);
//ADD CONSTRAINTS TO vc0
vc0!.view.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: vc0!.view, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 0)
view.addConstraint(horizontalConstraint)
let widthConstraint = NSLayoutConstraint(item: vc0!.view, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: screenWidth)
view.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: vc0!.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: screenHeight)
view.addConstraint(heightConstraint)
//ADD CONSTRAINTS TO vc1
vc1!.view.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint2 = NSLayoutConstraint(item: vc1!.view, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: vc0!.view, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 0)
view.addConstraint(horizontalConstraint2)
let widthConstraint2 = NSLayoutConstraint(item: vc1!.view, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: screenWidth)
view.addConstraint(widthConstraint)
let heightConstraint2 = NSLayoutConstraint(item: vc1!.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: screenHeight)
view.addConstraint(heightConstraint)
}
I think there is a problem in constraints!!
You should add one view to scrollview and then add views of both VC to that view not direct to scrollview.
view's size should be same as scrollview's contentsize
Now your constrains should be like :
scrollview : top,bottom,leading,trailing
View in scrollview : top,bottom,leading,trailing, Vertical center in container(center Y) and fixed width (because you want horizontal scrolling), If want vertical scrolling then constraint should be (horizontal center in container (center X) and fixed height).
And you views from both VCs : top,leading,fixed width, fixed height (width and height should be same as screen size)
Hope this will help :)
I simply want to set constraints which allows the UIWebView to fill the screen. (Screen has a UINavigationbar and a UIToolBar), I've tried following the examples given in the apple documentation but always get a warning and my view doesnt show up. Below is what I've tried.
private func addSubviews(){
navigationController?.setToolbarHidden(false, animated: false)
// webView = UIWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: UIScreen.mainScreen().bounds.height))
webView = UIWebView(frame: CGRectZero)
webView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(webView)
let views = ["myView" : webView]
let formatString = "|-[myView]-|"
let constraints = NSLayoutConstraint.constraintsWithVisualFormat(formatString, options:[] , metrics: nil, views: views)
NSLayoutConstraint.activateConstraints(constraints)
}
Oofh, visual constraints make my head hurt. I never was a fan of the visual language, never made any sense to me. This may be a bit more code but I think it's much easier to read as a developer and far more expressive as to intent.
self.view.addSubview(webView)
self.view.addConstraint(NSLayoutConstraint(item: self.view, attribute: .Left, relatedBy: .Equal, toItem: webView, attribute: .Left, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: self.view, attribute: .Top, relatedBy: .Equal, toItem: webView, attribute: .Top, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: self.view, attribute: .Bottom, relatedBy: .Equal, toItem: webView, attribute: .Bottom, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: self.view, attribute: .Right, relatedBy: .Equal, toItem: webView, attribute: .Right, multiplier: 1, constant: 0))
These constraints pin all edges of a view to the respective edge with a distance of 0.
Then at the end of your constraints, don't forget:
self.view.layoutIfNeeded()
My problem with the visual language is I can't look at it and immediately be clear as to what it does. Here, it's quite explicit. Just for fun, here's a nice function you can use whenever your constant and multiplier is 0:
/**
Adds an edge constraint between the superview and subview with a constant of 0.
- parameter attribute: Layout attribute.
- parameter subview: Subview.
*/
func addEdgeConstraintWithAttribute(attribute: NSLayoutAttribute, withSubview subview: UIView) {
self.addConstraint(NSLayoutConstraint(item: subview, attribute: attribute, relatedBy: .Equal, toItem: self, attribute: attribute, multiplier: 1, constant: 0))
}
Used like:
self.addEdgeConstraint(.Top, withSubview: webView)
where the function is an extension on UIView
Im my opinion, your view didn't show up because you didn't set up enough constraints for it.
Basically, with a view, you should add constraints to let it know at least the position and the size. In some cases, you dont need to setup size constraints because that view has intrinsic content size like UILabel, UIButton
Back to your example, you should add constraints like this:
private func addSubviews(){
navigationController?.setToolbarHidden(false, animated: false)
// webView = UIWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.mainScreen().bounds.width, height: UIScreen.mainScreen().bounds.height))
let webView = UIWebView(frame: CGRectZero)
webView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(webView)
let views = ["myView" : webView]
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[myView]-|", options:[] , metrics: nil, views: views))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[myView]-|", options:[] , metrics: nil, views: views))
}
H in H:|-[myView]-| stands for horizontal.
V in V:|-[myView]-| stands for vertical.
I have just started exploring the Uber iOS Sdk. I was checking for RequestButton and have noticed that its not taking any frame which we are passing. Is it possible to put button on desired location of a view?
let button = RequestButton(colorStyle:.White)
view.addSubview(button)
button.frame = CGRectMake(100,300, button.frame.size.width, button.frame.size.height)
It is possible to add the RequestButton in a desired location. The RequestButton is positioned with Auto Layout (or Constraint Based Layout) rather than Frame-Based Layout. See Apple's notes on Auto Layout Versus Frame-Based Layout.
Auto Layout guarantees resizing when the frame changes orientation and for different device sizes. In Auto Layout, you need to place UIViews based on their relative position to other views.
Here is an example of how you would center a RequestButton (button) inside a parent UIView (inView)
func centerButton(forButton button: RequestButton, inView: UIView) {
button.translatesAutoresizingMaskIntoConstraints = false
// position constraints
let horizontalConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: inView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: inView, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
// add constraints to view
inView.addConstraints([horizontalConstraint, verticalConstraint])
}
There might be an easier way but I found this solution for positioning the Uber RequestButton using frame based layouts.
Place the request button inside a wrapper UIView. Set the constraints on the wrapper view via Christine Kim's answer. Then you can position the wrapper's frame and the RequestButton will move with it
// MyView.swift
let uber = RequestButton()
let uberWrapper = UIView()
uberWrapper.addSubview(uber)
self.addSubview(uberWrapper)
//Set constraints
let horizontalConstraint = NSLayoutConstraint(item: uber, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: uberWrapper, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: uber, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: uberWrapper, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
// add constraints to view
uberWrapper.addConstraints([horizontalConstraint, verticalConstraint])
//layoutSubviews()
//set your origin
uberWrapper.frame = CGRect(origin: CGPointMake(0, 0), size: uber.intrinsicContentSize())
I'm trying to center my subview with a button in its superview. So I want the center of the subview be the center of the superview. I'm trying that with following code:
override func viewDidLoad() {
self.view.backgroundColor = UIColor.redColor()
var menuView = UIView()
var newPlayButton = UIButton()
//var newPlayImage = UIImage(named: "new_game_button_5cs")
var newPlayImageView = UIImageView(image: UIImage(named: "new_game_button_5cs"))
newPlayButton.frame = CGRectMake(0, 0, newPlayImageView.frame.width, newPlayImageView.frame.height)
newPlayButton.setImage(newPlayImage, forState: .Normal)
newPlayButton.backgroundColor = UIColor.whiteColor()
menuView.backgroundColor = UIColor.whiteColor()
menuView.addSubview(newPlayButton)
menuView.addConstraint(
NSLayoutConstraint(item: self.view,
attribute: .CenterX,
relatedBy: .Equal,
toItem: menuView,
attribute: .CenterX,
multiplier: 1, constant: 0)
)
}
Unfortunately the program breaks when I try to run it.
(Thread 1: signal SIGABRT)
Your code triggers an assertion saying:
When added to a view, the constraint's items must be descendants of
that view (or the view itself).
This means you have to add menuView as a subview to self.view before adding constraints. You should also add the constraints to self.view, not the menuView. Last but not least, remove autoresizing masks constraints that were implicitly added to menuView by calling setTranslatesAutoresizingMaskIntoConstraints(false) or autolayout will complain about conflicting constraints.
menuView.addSubview(newPlayButton)
menuView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(menuView)
self.view.addConstraint(
NSLayoutConstraint(item: self.view,
attribute: .CenterX,
relatedBy: .Equal,
toItem: menuView,
attribute: .CenterX,
multiplier: 1, constant: 0)
)
I'm trying to place my subview with a left margin based on the width of the parent view. This sounds simple but I can't figure out how to do it using autolayout.
Logically, I would only need to set the left margin value at a certain percentage value of the parent's width but at the moment, I fail to translate that logic to autolayout.
This is my code at the moment:
var view = UIView();
view.backgroundColor = UIColor.redColor();
view.frame = CGRectMake(0, 0, 320, 400);
var sview = UIView();
sview.setTranslatesAutoresizingMaskIntoConstraints(false);
sview.backgroundColor = UIColor.yellowColor();
//sview.frame = CGRectMake(0, 0, 50, 50);
view.addSubview(sview);
var dict = Dictionary<String, UIView>()
dict["box"] = sview;
var con1 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 20.0);
view.addConstraint(con1);
var con2 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 0.75, constant: 0.0);
view.addConstraint(con2);
var con3 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0);
view.addConstraint(con3);
var con4 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0.0);
view.addConstraint(con4);
This is the where the code returns an error:
var con2 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 0.75, constant: 0.0);
view.addConstraint(con2);
Error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* +[NSLayoutConstraint
constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]:
Invalid pairing of layout attributes'
Does anyone have any idea on how to achieve this? I just want the left margin to be 0.75% of the parent view's width.
Thanks.
What you want is the left of sview to be at some point of the left of view and you are writing that you want the left of sview to be at some point of the width of view which is not a correct pairing of layout attributes as your error says.
Here is what you need to do:
NSLayoutConstraint(item: sview,
attribute: NSLayoutAttribute.Left,
relatedBy: NSLayoutRelation.Equal,
toItem: view,
attribute: NSLayoutAttribute.Left,
multiplier: 1,
constant: (CGRectGetWidth(view.bounds) * 0.75));
Hope it helps!
EDIT
I found a great article about Percented based margins: https://web.archive.org/web/20170624134422/http://simblestudios.com/blog/development/percentage-width-in-autolayout.html
Or even simpler:
https://web.archive.org/web/20170704113819/http://simblestudios.com/blog/development/easier-percentage-width-in-autolayout.html
It is possible to create a percentage-based margins with auto layout constraints between a subview and its superview. The margin will change dynamically as the size of the superview changes. For example, this is how to create a trailing 10% margin.
Create a trailing constraint between your view and its superview.
Make sure the first constraint item is the subview and the second item is the superview. This can be done by clicking on the first item drop down and selecting Reverse First and Second Item.
Change the constant value of the constraint to zero in the attributes inspector.
Change the multiplier value to 0.9.
The is one problem with this manual approach though - it does not work in right-to-left language, Arabic, for example. Right-to-left layouts require a bit different settings for the constraint but we can not keep both in one storyboard.
Here is a library that I wrote that lets you create percentage-based constraints. It does handle the right-to-left language case.
https://github.com/exchangegroup/PercentageMargin
You can subclass NSLayoutConstraint to accept a margin percentage via #IBInspectable. Then subscribe to UIDeviceOrientationDidChangeNotification to run the calculation and stuff into the constant value so it is updated whenever the layout changes.
/// Layout constraint to calculate size based on multiplier.
class PercentLayoutConstraint: NSLayoutConstraint {
#IBInspectable var marginPercent: CGFloat = 0
var screenSize: (width: CGFloat, height: CGFloat) {
return (UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
}
override func awakeFromNib() {
super.awakeFromNib()
guard marginPercent > 0 else { return }
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(layoutDidChange),
name: UIDeviceOrientationDidChangeNotification,
object: nil)
}
/**
Re-calculate constant based on orientation and percentage.
*/
func layoutDidChange() {
guard marginPercent > 0 else { return }
switch firstAttribute {
case .Top, .TopMargin, .Bottom, .BottomMargin:
constant = screenSize.height * marginPercent
case .Leading, .LeadingMargin, .Trailing, .TrailingMargin:
constant = screenSize.width * marginPercent
default: break
}
}
deinit {
guard marginPercent > 0 else { return }
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
First you specify the new subclass in Identity Inspector:
Then you can use it like this:
The only caveat I can think of is the constants in the Storyboard are not used at runtime, but instead are overwritten with the percentage based calculation. So it does require some duplicate effort, once to actually layout the views on Storyboard based on points just so you get a sense of what the screen layout looks like, then percentages kick in at runtime.
For more details, check out this article: http://basememara.com/percentage-based-margin-using-autolayout-storyboard/
Along with the Kevin answer you also need to add constraint for the box superview.
var view = UIView();
view.backgroundColor = UIColor.redColor();
view.setTranslatesAutoresizingMaskIntoConstraints(false);
self.view.addSubview(view);
var viewObject = ["mainview" : self.view , "view" : view];
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[view(==320)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewObject));
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[view(==400)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewObject));