Add a subview with constraints do not work - swift

I want to add a subview with some constraints.
But my view do not appear.
Below is some code, does anyone know what is going wrong?
(If i add a UITextField e.g. it works fine...)
class TestViewController:UIViewController {
override func viewDidLoad() {
//Do not work...
addAndLayout(v: UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 0)))
//Works fine
//addAndLayout(v: UITextField(frame: CGRect(x: 0, y: 0, width: 0, height: 0)))
}
func addAndLayout(v:UIView) {
v.backgroundColor = UIColor.red
view.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
let leading = NSLayoutConstraint(item: v, attribute: .leading, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
let trailing = NSLayoutConstraint(item: v, attribute: .trailing, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
let top = NSLayoutConstraint(item: v, attribute: .top, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: .top, multiplier: 1, constant: 0)
view.addConstraints([leading, trailing, top])
}
}

Add a height constraint or bottom constraint and you should see the view. The reason it works with UITextField is that UITextField has an intrinsic content size while UIView does not.

Related

Setting 2 label in header programatically - not label seen

I'm trying to set 2 labels, one under other with different font size in the header. The function is called like this:
viewController.navigationItem.titleView = self.setHeader()
And code responsible for generating label is :
private func setHeader(agentName: String = "", isTyping: Bool = false) -> UIView {
let headerLabel: UILabel = {
let label = UILabel()
label.text = self.title
label.font = UIFont.systemFont(ofSize: 21)
label.textColor = UIColor.white
return label
}()
let subheaderLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 10)
return label
}()
let headerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(headerLabel)
view.addConstraints([
NSLayoutConstraint(item: headerLabel, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: headerLabel, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: headerLabel, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 16),
NSLayoutConstraint(item: headerLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 16)
])
return view
}()
if (!agentName.isEmpty) {
if (isTyping) {
subheaderLabel.text = agentName + " is typing ..."
} else {
subheaderLabel.text = agentName
}
headerView.addSubview(subheaderLabel)
}
return headerView
}
When I running IOS app there is nothing shown in the header. What is a reason?
I think you need to set the frame for the headerView. So in the initialization code for the headerView, use initializer with frame:
let headerView: UIView = {
// initialize the view with frame
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 35))
// you want to call this on the headerLabel, not on view
headerLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(headerLabel)
view.addConstraints([
NSLayoutConstraint(item: headerLabel, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: headerLabel, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: headerLabel, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 16),
NSLayoutConstraint(item: headerLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 16)
])
return view
}()
Moreover, I believe headerLabel you want to set translatesAutoresizingMaskIntoConstraints on headerLabel rather than on headerView (headerView is positioned through frame and not Autolayout).
Also, notice that you add subheaderLabel to the view, but you never position it, don't forget about it either (although this should cause only subheaderLabel not to be rendered properly).

Programmatically adding root view to view controller does not position correctly

I am trying to add the root view and subviews to a viewcontroller programmatically, but the view is not filling to screen as expected:
override func loadView() {
self.view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
self.view.backgroundColor = UIColor.blue
}
override func viewDidLoad() {
let alertView = Bundle.main.loadNibNamed("CHRApptTakenAlertView", owner: self, options: nil)![0] as! CHRApptTakenAlertView
self.view.translatesAutoresizingMaskIntoConstraints = false
alertView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(alertView)
self.view.addConstraint(NSLayoutConstraint(item: alertView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: alertView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0))
alertView.addConstraint(NSLayoutConstraint(item: alertView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.width, multiplier: 1, constant: 350))
alertView.addConstraint(NSLayoutConstraint(item: alertView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.height, multiplier: 1, constant: 250))
alertView.closeBtn.addTarget(self, action: #selector(self.closeBtnTouch), for: UIControlEvents.touchUpInside)
}
Remove the line self.view.translatesAutoresizingMaskIntoConstraints = false, and call self.view.layoutIfNeeded() after the adding the constraints.

Programming autolayout swift

I have in app: TextView and TableView. I create their in code. And now I need to make programming autolayout. But I don't know how it make. Please help.
P.S. Sorry for my English =)
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
myTextView = UITextView(frame: CGRect(x: 0, y: 20, width: displayWidth, height: displayHeight / 3))
creatTextView()
myTableView = UITableView(frame: CGRect(x: 0, y: displayHeight / 3, width: displayWidth, height: displayHeight * 2 / 3))
createTable()
A quick AutoLayout guide
Documentation: https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/
I usually set up constraints to the left, right, bottom/top and width/height constraints. That can be achieved in multiple ways.
Some keywords:
Leading: Means the left part of the object
Trailing: Means the right part of an object
First you want to make all the necessary variables to hold your autolayout guides and for the view you are using autolayout on you'll need to set translatesAutoresizingMaskIntoConstraints to false like this:
self.btn.translatesAutoresizingMaskIntoConstraints = false
var btnLeading: NSLayoutConstraint!
var btnBottom: NSLayoutConstraint!
var btnTop: NSLayoutConstraint!
var btnWidth: NSLayoutConstraint!
I just copied some code I used in a project, but I think you'll get the hang of it eventually. self.userLocationBtn is just a button in my view I want to position in a UIView I have subclassed.
self.btnLeading = NSLayoutConstraint(
item: self.userLocationBtn,
attribute: .leading,
relatedBy: .equal,
toItem: self,
attribute: .leading,
multiplier: 1.0,
constant: 5.0)
self.btnBottom = NSLayoutConstraint(
item: self.userLocationBtn,
attribute: .bottom,
relatedBy: .equal,
toItem: self,
attribute: .bottom,
multiplier: 1.0,
constant: 0.0)
self.btnTop = NSLayoutConstraint(
item: self.userLocationBtn,
attribute: .top,
relatedBy: .equal,
toItem: self,
attribute: .top,
multiplier: 1.0,
constant: 0.0)
self.btnWidth = NSLayoutConstraint(
item: self.userLocationBtn,
attribute: .width,
relatedBy: .equal,
toItem: self,
attribute: .height,
multiplier: 1.0,
constant: 0.0)
self.addSubview(self.doneButton)
After the view is added we need to activate the constraints and then update the view.
NSLayoutConstraint.activate([self.btnLeading, self.btnBottom, self.btnTop, self.btnWidth])
self.view.layoutIfNeeded() //Lays out the subviews immediately.

Swift how to animate up the screen while staying centered, but also shrink in size

I want to do a simple animation where the logo moves from point A on the screen at size 1 to point B on the screen at size 2
I found
self.logoOutlet.frame = CGRect(x: x, y: y, width: 115, height: 115)
how do i get it so the x is centered no matter what size the phone is?
You either have to do math, to calculate the value of x based on the bounds of the superview of logoOutlet or use AutoLayout and just use a center X constraint (which is probably the better choice).
You can read about the specifics in the documentation: Auto Layout Guide, or use one of the many tutorials on Auto Layout you can find via Google search.
Through a combination of two different answers I found a solution that works
self.logoOutlet.frame = CGRect(x: self.view.frame.midX, y: 26, width: 115, height: 115)
self.logoOutlet.center.x = self.view.center.x
Here's an example using Auto Layout:
#IBAction func animateLogo(sender: UIButton) {
let newView = UIView()
newView.backgroundColor = .redColor()
newView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(newView)
let topConstraint = NSLayoutConstraint(item: newView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1.0, constant: 500.0)
let centerXConstraint = NSLayoutConstraint(item: newView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: 0.0)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 200)
let heightConstraint = NSLayoutConstraint(item: newView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 200)
NSLayoutConstraint.activateConstraints([topConstraint, centerXConstraint, widthConstraint, heightConstraint])
self.view.layoutIfNeeded()
// Now change the constraint for the second point
widthConstraint.constant = 50
heightConstraint.constant = 50
topConstraint.constant = 50
// Animate the move over 5 seconds
UIView.animateWithDuration(5.0) {
self.view.layoutIfNeeded()
}
}

embedding navbar/tabbar breaks constraints of a normally functional view

I have a simple
UIViewController that normally works:
view.backgroundColor
UITextView
UIView (as a spacer between the bottom and the textview)
constraints to pin textview to the view which in turn is pinned to the bottomlayoutguide
tapping textview loads keyboard and the spacer view expands accordingly to avoid the keyboard overlapping my textview
...
//var memoArea = UITextView(frame: CGRectMake(20, 291, 275, 225))
memoArea.addConstraint(NSLayoutConstraint(item: memoArea, attribute: .Width, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 275.0))
memoArea.addConstraint(NSLayoutConstraint(item: memoArea, attribute: .Height, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 225.0))
self.view.addConstraint(NSLayoutConstraint(item: memoArea, attribute: .Leading, relatedBy: .Equal,
toItem: self.view, attribute: .Leading, multiplier: 1.0, constant: 20.0))
// var spacer:UIView = UIView(frame: CGRectMake(84, 518, 160, 6))
spacer.addConstraint(NSLayoutConstraint(item: spacer, attribute: .Width, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 160.0))
spacer.addConstraint(NSLayoutConstraint(item: spacer, attribute: .Height, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 6.0))
self.view.addConstraint(NSLayoutConstraint(item: spacer, attribute: .Leading, relatedBy: .Equal,
toItem: self.view, attribute: .Leading, multiplier: 1.0, constant: 84.0))
view.setTranslatesAutoresizingMaskIntoConstraints(false)
spacer.setTranslatesAutoresizingMaskIntoConstraints(false)
memoArea.setTranslatesAutoresizingMaskIntoConstraints(false)
...
...
func updateBottomLayoutConstraintWithNotification(notification: NSNotification) {
let userInfo = notification.userInfo!
let animationDuration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as NSNumber).doubleValue
let keyboardEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as NSValue).CGRectValue()
let convertedKeyboardEndFrame = view.convertRect(keyboardEndFrame, fromView: view.window)
let rawAnimationCurve = (notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as NSNumber).unsignedIntValue << 16
let animationCurve = UIViewAnimationOptions.init(UInt(rawAnimationCurve))
let frame = self.tabBarController?.tabBar.frame
let height = frame?.size.height
spacerToBottom.constant = CGRectGetMaxY(view.bounds) - CGRectGetMinY(convertedKeyboardEndFrame) - height! - 5
UIView.animateWithDuration(animationDuration, delay: 0.0, options: .BeginFromCurrentState | animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
...
But, after adding either a tab bar or a nav bar to a view that has normally working constraints,
3 things break:
the background no longer renders, yielding a black background
the textview doesn't register taps i.e. keyboard doesn't load
view disregards the bottomlayoutguide. it just shifts my objects up as high as their .Top constraints allow them. the constraints between the textview and uiview are still honored though.
commenting out view.setTranslatesAutoresizingMaskIntoConstraints(false) got rid of all errors.