So I have 3 views set up in interface builder (XCode 6). They are linked to the ViewController that owns them. Also I have 3 subclasses of UIVIew in my project. At runtime I would need to change the class of one of the views from UIView to my custom view subclass.
How do I do this in swift? (I need all the autolayout set up in IB to work the same after the change).
To achieve what you need you can create a view in IB and later in the code add required view as a subview.
To make added view occupy all container view space you need either update child view's frame or setup auto-layout constraints. Variant with frames needs to be repeated each time container view changes it size. Code bellow:
Auto-Layout Contraints
override func viewDidLoad() {
super.viewDidLoad()
self.myView = UIView(frame: CGRect())
self.myView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.myViewContainer.addSubview(self.myView)
self.myViewContainer.addConstraint(NSLayoutConstraint(item: self.myView, attribute: .Top, relatedBy: .Equal, toItem: self.myViewContainer, attribute: .Top, multiplier: 1.0, constant: 0))
self.myViewContainer.addConstraint(NSLayoutConstraint(item: self.myView, attribute: .Right, relatedBy: .Equal, toItem: self.myViewContainer, attribute: .Right, multiplier: 1.0, constant: 0))
self.myViewContainer.addConstraint(NSLayoutConstraint(item: self.myView, attribute: .Bottom, relatedBy: .Equal, toItem: self.myViewContainer, attribute: .Bottom, multiplier: 1.0, constant: 0))
self.myViewContainer.addConstraint(NSLayoutConstraint(item: self.myView, attribute: .Left, relatedBy: .Equal, toItem: self.myViewContainer, attribute: .Left, multiplier: 1.0, constant: 0))
}
Manual Frame Updates
#IBOutlet var myViewContainer: UIView
var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.myView = UIView(frame: CGRect())
self.myViewContainer.addSubview(self.myView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.myView.frame = self.myViewContainer.bounds
}
Frame updates can be done even if container view has auto-layout constraints.
Related
I want to place header view on top of screen with NSLayoutConstraint (I must use NSLayoutConstraint). When I do it like in below code, view places corruptly in somewhere else and also controllers background color turns black and nothing works. Where am I doing wrong?
I searched below posts for not opening a duplicate post but nothing fixed it:
Programmatically creating constraints bound to view controller margins
Programmatically Add CenterX/CenterY Constraints
EDIT: This controller is inside navigation controller but I'm not sure If It is related.
override func viewDidLoad(){
self.view.backgroundColor = UIColor.white
boxView.backgroundColor = Color.Common.welcomeScreenBackgroundColor.withAlphaComponent(0.5)
boxView.translatesAutoresizingMaskIntoConstraints = false
self.view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubView(boxView)
}
override func viewDidLayoutSubviews() {
//Header = 20 from left edge of screen
let cn1 = NSLayoutConstraint(item: boxView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1.0, constant: 0)
//Header view trailing end is 20 px from right edge of the screen
let cn2 = NSLayoutConstraint(item: boxView, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1.0, constant: 0)
//Header view height = constant 240
let cn3 = NSLayoutConstraint(item: boxView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant:240)
//Header view vertical padding from the top edge of the screen = 20
let cn5 = NSLayoutConstraint(item: boxView, attribute: .top, relatedBy: .equal, toItem: self.topLayoutGuide, attribute: .bottom, multiplier: 1.0, constant: 0)
self.view.addConstraints([cn1,cn2,cn3,cn5])
}
The problem was setting translatesAutoresizingMaskIntoConstraints to false on Superview. So I deleted the;
self.view.translatesAutoresizingMaskIntoConstraints = false
and this solves the problem. I think this causes app creates constraint for superview.
Summary:
I want to implement my own UIView object with specific layout constraints utilizing Swift 3. I'm not sure how to configure the layout constraints in the object I'm customizing. I was thinking of passing in a reference to the super view to append the layout constraints onto.
Question:
How can I implement my own custom user interface object while also maintaining that objects layout constraints in the model instead of the view?
Code:
Custom View:
import Foundation
import UIKit
class CustomView: UIView {
var header: UIView?
var users: [Array<OtherObject>]?
init() {
super.init(frame: UIScreen.main.bounds);
//for debug validation
print("My Custom Init");
return;
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented");
}
func configureTitleHeader(){
header = UIView()
}
func configureConstraints(superView: UIView){
self.configureTitleHeader()
let titleConstraints: [NSLayoutConstraint] = [
NSLayoutConstraint(item: self.header!, attribute: .left, relatedBy: .equal, toItem: superView, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.header!, attribute: .right, relatedBy: .equal, toItem: superView, attribute: .right, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.header!, attribute: .top, relatedBy: .equal, toItem: superView, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.header!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 100)
]
superView.addConstraints(titleConstraints)
}
}
UINavigationController:
override func viewDidLoad() {
super.viewDidLoad()
let customView = CustomView()
customView.configureConstraints(superView: self.view)
self.view.addSubview(customView)
}
I have a controller where I add a subview programmatically. With the configuration of the subview I add autolayout constraints programmatically. Everthing is working except that the view doesn't react on touches if I add the constraints and even the set backgroundcolor is not displayed.
The buttonView should be displayed in the lower right corner of my parent view.
Any ideas what could be wrong?
Here is how I add my constraints:
private func configureAutolayoutConstraints(buttonView: UIView, parentView: UIView){
buttonView.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint = NSLayoutConstraint(item: buttonView, attribute:
.Bottom, relatedBy: .Equal, toItem: parentView, attribute: .Bottom, multiplier: 1.0, constant: -130)
parentView.addConstraint(bottomConstraint)
let trailingConstraint = NSLayoutConstraint(item: buttonView, attribute:
.Trailing, relatedBy: .Equal, toItem: parentView, attribute: .Trailing, multiplier: 1.0, constant: -90)
parentView.addConstraint(trailingConstraint)
}
Autolayout engine needs at least 4 constraints to determine the frame of view. You have applied bottom and trailing constraints only. You need either width+height OR leading+top constraint to make it work.
I have already created a circular button which is a custom UIView.Here's the code:
class HelpTips: UIView {
weak var hotSpot: UIButton!
override init(frame: CGRect) {
super.init(frame: frame)
let strongHotSpot = UIButton()
hotSpot = strongHotSpot
self.addSubview(strongHotSpot)
hotSpotOne.translatesAutoresizingMaskIntoConstraints = false
hotSpotOne.backgroundColor = UIColor.TRLMHelpTipYellowColor()
hotSpotOne.layer.borderColor = UIColor.TRLMHelpTipStrokeColor().CGColor
hotSpotOne.layer.borderWidth = 1
let horizontalConstraint = NSLayoutConstraint(item: hotSpot, attribute: .Leading, relatedBy: .Equal, toItem: self, attribute: .Leading, multiplier: 1.0, constant: -1)
let verticalConstraint = NSLayoutConstraint(item: hotSpot, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1.0, constant: 16)
let widthConstraint = NSLayoutConstraint(item: hotSpot, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 40)
let heightConstraint = NSLayoutConstraint(item: hotSpot, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 40)
self.addConstraints([verticalConstraint, horizontalConstraint, widthConstraint, heightConstraint])
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Now this same button is used throughout the app at several places but it's placed at different positions. So each View Controller will make use of that UIView.
So technically the look of the button remains the same but the constraints for that button keep on changing depending on it's position. I want to follow DRY(Don't repeat yourself) technique here.
I have done this kind of thing before but the code was being repeated several times and was not efficient. How to go about this?
Any help is appreciated. Thanks!
You could create a custom navigation controller and store the view as a property in it. Every view controller will have access to the navigation controller, so they can just reference that property and time you need to use the view. Should keep it DRY.
Create a custom view using an xib. Add your UIButton as subview.
This tutorial will be helpful.
As an academic exercise for a future UI, I am trying to add constraints between two table views, belonging to two different child view controllers of the root controller. In my RootViewController class below, tvc is displayed as expected in a 400x500 frame, but tvc2 is consuming the entire frame instead of being a 400x500 frame to the right of tvc. Basically, the constraints are apparently being ignored. I'm using an iPad sim in landscape.
override func viewDidLoad() {
super.viewDidLoad()
let v = self.view
v.backgroundColor = UIColor.greenColor()
var tvc :OrderTableViewController = OrderTableViewController(style: UITableViewStyle.Plain)
var tvc2 :OrderTableViewController = OrderTableViewController(style: UITableViewStyle.Plain)
self.addChildViewController(tvc)
self.addChildViewController(tvc2)
v.addSubview(tvc.view)
v.addSubview(tvc2.view)
tvc.didMoveToParentViewController(self)
tvc2.didMoveToParentViewController(self)
//tvc.view.setTranslatesAutoresizingMaskIntoConstraints(false)
//tvc2.view.setTranslatesAutoresizingMaskIntoConstraints(false)
//self.view.setTranslatesAutoresizingMaskIntoConstraints(false)
tvc.view.frame = CGRectMake(0, 0, 400, 500)
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Top,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Top,
multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Bottom,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Bottom,
multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1, constant: 400))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Left,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Right,
multiplier: 1, constant: 0))
}
This line,
tvc2.view.setTranslatesAutoresizingMaskIntoConstraints(false)
should be uncommented. When you add a view programmatically, and you're using constraints, you should always set that to false. On the other hand, you should not set that to false for controller's main view.
Also, the width constraint, since it only applies to tvc2, should be added to tvc2, not to self.view (although it should work either way).
It's also a bit odd, that you're adding one view using frames, and the other with constraints. It would be better to do both using the same paradigm.