I'm new to Swift, and I'm trying to learn how to build iOS apps programmatically, with very limited use of the Storyboard. Right now, the goal is to create a menu at the top of the app, which will contain buttons. Based off of my research, it appears that the best way to do this is to nest the UIButtons inside of a ContainerView. However, the issue is that my UIButton is displayed outside of my ContainerView. Perhaps I need to set constraints on the ViewContainer and my UIButton?
Could you guys please point me in the right direction? Your help is very much appreciated.
Storyboard:
App Simulator (iPhone 7 Plus):
HomeController.swift:
class HomeController: UIViewController {
let topMenuContainerView = UIView()
topMenuContainerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(topMenuContainerView)
let topMenuController = storyboard!.instantiateViewController(withIdentifier: "Top_Menu_View_Controller")
addChildViewController(topMenuController)
topMenuController.view.translatesAutoresizingMaskIntoConstraints = false
topMenuContainerView.addSubview(topMenuController.view)
topMenuController.didMove(toParentViewController: self)
}
TopMenuController.swift:
class TopMenuController: UIViewController {
let createAdButton: UIButton = UIButton(type: UIButtonType.roundedRect)
createAdButton.setTitle("Create Ad", for: UIControlState.normal)
createAdButton.titleLabel?.textColor = UIColor.white
createAdButton.frame = view.frame
createAdButton.backgroundColor = UIColor.white
view.addSubview(createAdButton)
}
Yes, I believe adding constraints will solve your problem. It would be something like this:
let horizontalConstraint = NSLayoutConstraint(item: createAdButton, attribute: NSLayoutAttribute.centerX, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: createAdButton, attribute: NSLayoutAttribute.centerY, relatedBy: .equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0)
view.addConstraints([horizontalConstraint, verticalConstraint])
BUT, wouldn't it be better to have just a UIView inside your Home Controller instead of a View Controller just for the menu?
Related
I really need your help about this issue. I am creating a UIView object programatically without setting the bounds at the beggining. After creating I add constraints to that view in order to fit the required place in the screen.
But the problem is that, I want to use that UIView's bounds property in the following parts of the code but unfortunately I can not get the bounds after constraints sets as it shows on the screen.
Below I have sample code :
let previewView = UIView()
NSLayoutConstraint(item: previewView, attribute: .top, relatedBy: .equal, toItem: upperView, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint(item: previewView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0.0).isActive = true
previewView.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
previewView.translatesAutoresizingMaskIntoConstraints = false
print(previewView.bounds)
// (0.0, 0.0, 0.0, 0.0) ->>>> How can I get the bounds as it shows on the screen instead of 0,0,0,0
After this point, when I try to use, previewView.bounds property, it returns me its original size as 0.
How can I get the bounds as it shows on the screen with the related constaints' setup ?
Thank you very much Guys.
You can access it in
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if once {
once = false
// process here
print(previewView.bounds)
}
}
but first declare the view as instance variable , make once = false in view
var previewView:UIView!
var once= true
see here
in my game i have a ViewController that displays various items in a collectionView i need to make it so that when an item is pressed at an index...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
if indexPath.item == 0 {
//add a viewcontroller for viewing content
//other things here for customizing that view data
}
}
...a viewcontroller pops onscreen not taking up the whole screen but a small portion in the middle of the main ViewController
(i need the view to be reusable and adaptable) i have tried making a viewController and adding it to the main ViewController as a subview of sorts but no luck
i want to display different info depending what cell is selected if you could help me i would appreciate it
As far as I understand , You want to open custom view which is loaded from xib on your ViewControllers view or a different ViewControllers view on your ViewController(that display various item in collectionView).
If yes then use below code
//Create optional property
var myViewController : YourCustomViewController?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
if indexPath.item == 0 {
//add below line to load custom View Xib
loadCustomView(onView:self.view)
//OR To Add ViewController View use below code
loadViewController(onView:self.view)
}
}
fun loadCustomView(onView:UIView) {
let allViewsInXibArray = Bundle.init(for: type(of: self)).loadNibNamed("CustomView", owner: self, options: nil)
//If you only have one view in the xib and you set it's class to MyView class
let myCustomView = allViewsInXibArray?.first as! CustomView
onView addSubview(myCustomView)
addConstraints(onView: myCustomView)
}
func loadViewController(onView:UIView) {
myViewController = YourCustomViewController(nibName: "TestViewController", bundle: nil);
onView addSubview((myViewController?.view)!)
addConstraints(onView: (myViewController?.view)!)
}
func addConstraints(onView : UIView) {
onView.translatesAutoresizingMaskIntoConstraints = false;
let widthConstraint = NSLayoutConstraint(item: onView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal,
toItem: onView.superview, attribute: .width, multiplier: 0.8, constant: 0)
let heightConstraint = NSLayoutConstraint(item: onView, attribute: .height, relatedBy: .equal,
toItem: onView.superview, attribute: .height, multiplier: 0.6, constant: 0)
let xConstraint = NSLayoutConstraint(item: onView, attribute: .centerX, relatedBy: .equal, toItem:onView.superview , attribute: .centerX, multiplier: 1, constant: 0)
let yConstraint = NSLayoutConstraint(item: onView, attribute: .centerY, relatedBy: .equal, toItem: onView.superview, attribute: .centerY, multiplier: 1, constant: 0)
NSLayoutConstraint.activate([widthConstraint, heightConstraint, xConstraint, yConstraint])
}
Further you can add popup and popdown Animation to your custom view.
Comment me if you need anything else.
In one of my games I am using a UICollectionView as the level select menu. I recently added a UIPageControl to it programatically.
/// Page control
func setupPageControl() {
pageControl.hidesForSinglePage = true
pageControl.numberOfPages = DataSource.worlds
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = DataSource.pageControlColors[pageControl.currentPage]
pageControl.pageIndicatorTintColor = UIColor.white.withAlphaComponent(0.8)
pageControl.addTarget(self, action: #selector(didPressPageControl), for: .valueChanged)
view.addSubview(pageControl)
let leading = NSLayoutConstraint(item: pageControl, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
let trailing = NSLayoutConstraint(item: pageControl, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
let bottomConstant = Device.isPad ? view.frame.width / 9 : view.frame.width / 17
let bottom = NSLayoutConstraint(item: pageControl, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: -bottomConstant)
view.addConstraints([leading, trailing, bottom])
}
Everything is fine on iOS but on tvOS the PageController has a translucent background that stretches all across the screen.
How can I turn this off? I tried setting the pageControl background color but that does not seem to work.
As usual just when I post a question I find the answer a minute later.
You can remove the background on tvOS by calling this
#if os(tvOS)
for subview in pageControl.subviews {
let effectView = subview as? UIVisualEffectView
effectView?.removeFromSuperview()
}
#endif
Change background color for page control
I'm trying to center an image to a view but I'm getting error..this is my code:
let markerimage = #imageLiteral(resourceName: "ic_new_mark_icon")
let size = CGSize(width: 60, height: 60)
var newimage = imageWithImage(image: markerimage, scaledToSize: size)
var imageview = UIImageView(image: newimage)
self.view.addSubview(imageview)
let xConstraint = NSLayoutConstraint(item: imageview, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1, constant: 0)
let yConstraint = NSLayoutConstraint(item: imageview, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1, constant: 0)
imageview.addConstraint(xConstraint)
imageview.addConstraint(yConstraint)
and my error is
2016-12-22 16:29:11.305417 Googlemappractice[21426:362773] [LayoutConstraints] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x600000285f00 UIImageView:0x7ff772f15560.centerX == UIView:0x7ff772c0bd30.centerX (inactive)>
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout)
The constraints need to be added to the earliest common ancestor of the two views affected by the constraint. In this case, self.view is earlier in the hierarchy so the constraints should be added to it instead of to imageview.
Since iOS 8, there is an easier way. Once you've added your view to the view hierarchy (with addSubview), you can activate the constraints instead of adding them to views.
You can do this by setting the isActive property to true:
xConstraint.isActive = true
yConstraint.isActive = true
or by using the activate class function of NSLayoutConstraint to active multiple constraints:
NSLayoutConstraint.activate([xConstraint, yConstraint])
As mentioned by #dfd in the comments, you should also set this flag:
imageview.translatesAutoresizingMaskIntoConstraints = false
This tells iOS to not create constraints for imageview based upon its frame. If you don't do this, you'll end up with conflicting constraints.
You are going to need width and height constraints for your imageview as well, because you've only specified its position so far.
I have a View where I load buttons dynamically. So I have a for loop to loop through all buttons. Since this is dynamically I want to create the auto layout programmatically. Right now I have the following code:
for var i = 0; i < data.count; i++ {
let button = UIButton.buttonWithType(UIButtonType.System) as! UIButton
button.backgroundColor = UIColor.greenColor()
button.setTitle("Button", forState: UIControlState.Normal)
button.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(button)
let centerXConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 0)
let centerYConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant:15)
let widthConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 200)
let heightConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant:100)
scrollView.addConstraints([centerXConstraint, centerYConstraint, widthConstraint, heightConstraint])
}
This creates the first button and places it 15px under the top bar. The problem I have is how to place the next button 15px under the first one, the third button 15px under the second one etc. Anyone got any ideas?
Thats definitely possible, but firstly, I should mention it's not required that you add constraints for the buttons' width and height since they have an intrinsic content size (like UILabel) which depends on attributes such their text and font.
Back to your problem!
Here's the code, the explanation for each step is below:
override func viewDidLoad() {
super.viewDidLoad()
// 1.
var upperView: UIView = scrollView
for i in 0..<data.count {
let button = UIButton.buttonWithType(UIButtonType.System) as! UIButton
button.backgroundColor = UIColor.greenColor()
button.setTitle("Button", forState: UIControlState.Normal)
button.setTranslatesAutoresizingMaskIntoConstraints(false)
// 2.
scrollView.addSubview(button)
// 3.
let attribute: NSLayoutAttribute = i == 0 ? .Top : .Bottom
// 4.
let topEdgeConstraint = NSLayoutConstraint(item: button,
attribute: .Top,
relatedBy: .Equal,
toItem: upperView,
attribute: attribute,
multiplier: 1.0,
constant: 15.0)
let centerXConstraint = NSLayoutConstraint(item: button,
attribute: .CenterX,
relatedBy: .Equal,
toItem: scrollView,
attribute: .CenterX,
multiplier: 1.0,
constant: 0.0)
scrollView.addConstraint(topEdgeConstraint)
scrollView.addConstraint(centerXConstraint)
// 5.
if i == data.count - 1 {
let bottomConstraint = NSLayoutConstraint(item: button,
attribute: .Bottom,
relatedBy: .Equal,
toItem: scrollView,
attribute: .Bottom,
multiplier: 1.0,
constant: -15.0)
scrollView.addConstraint(bottomConstraint)
}
upperView = button
}
}
1. upperView is used to keep track of the view 'above' the current button. For example, when the first button is created, the upperView is the UIScrollView For the second button, upperView is the first button; for the third button, upperView is the second button and so on...
2. Your buttons should be added to the UIScrollView, not the self.view. Otherwise you'll get the error:
The view hierarchy is not prepared for the constraint...
3. This line selects the attribute on the upperView that will relate the button to the upperView. Here's a picture to demonstrate what I mean:
a) The .Top of the button is related to the .Top of the UIScrollView.
b) The .Top of the button is related to the .Bottom of the previous UIButton.
4. Making the top and centre X constraints - that's all pretty self explanatory.
5. For the UIScrollView to correctly calculate its contentSize it must have constraints in an unbroken chain from it top to bottom (the top to the bottom, in this case, because it needs to scroll vertically). Therefore, if it's the last UIButton a constraint from its bottom edge is added to the UIScrollView's bottom edge.
Hope that helps!