I have a parent view controller with 2 views.On Top, I have View, that contains Page View Controller and on Bottom I have other view that shows different content.
Everything works well, except getting white space below first view ( Page View Controller) and above second view.
I have added following code for constraints,
let views:[String: Any] = ["pageView": pageViewController.view!]
articleContentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[pageView]-0-|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0),
metrics: nil, views: views))
articleContentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[pageView]-0-|",
options: NSLayoutConstraint.FormatOptions(rawValue: 0),
metrics: nil, views: views))
If I remove this code , My Page View Controller exceeds its view and occupy full screen.
Any suggestions much appriaciated.
The "blank space" is almost certainly the Page View Controller's built-in UIPageControl.
Here's a quick example, with two views... the top view has a UIPageViewController added as a child view controller, and the Top of the bottom view is constrained to the Bottom of the top view:
Notice the "empty space" ...
Using Xcode's Debug View Hierarchy, it's pretty obvious why:
So, if I set a background color on the Top view, I see this:
It appears to be empty space, because the default Page Control uses tinted white dots - so we don't see anything on a white background.
Edit
The PageControl is automatically shown if your controller's DataSource implements both of these optional methods:
optional func presentationCount(for pageViewController: UIPageViewController) -> Int
optional func presentationIndex(for pageViewController: UIPageViewController) -> Int
Removing one or both will automatically remove the page control.
Related
I want to create an info view that will show at the top of the screen and disappear after some time, and later it can show again and so on.
I have created UIView and set constraints:
topInfoView.leadingAnchor.constraint(equalTo: mainView.leadingAnchor)
topInfoView.trailingAnchor.constraint(equalTo: smainView.trailingAnchor)
topInfoView.heightAnchor.constraint(equalToConstant: HEIGHT)
Closed state:
topInfoView.topAnchor.constraint(equalTo: mainView.topAnchor, constant: -HEIGHT)
Open state:
topInfoView.topAnchor.constraint(equalTo: mainView.topAnchor, constant: 0)
where mainView is main UIViewCcontroller view.
I set HEIGHT as my user height + "status bar height" (bar with battery, Wifi etc). Problem is, that sometimes status bar height is 0 and my topInfoView is incorrectly placed. I am obtaining "status bar height" via this:
func statusBarHeight() -> CGFloat {
let statusBarSize = UIApplication.shared.statusBarFrame.size
return Swift.min(statusBarSize.width, statusBarSize.height)
}
but it sometimes not works (views are not inited?) and I am also not sure about new iPhone X, where status bar is solved differently. Is there any other way, without calculating the height?
Use the vertical stack view. Put the tableView and then your view vertically one after the another. Set the height of the table view. Keep the distribution property of stack view to fill. Create an outlet of the tableview. With this arrangement when you will hide the tableview, your view will fill the whole area. When you again set isHidden property to false of table view your table view and view will appear as original arrangement. You can animate while hiding and showing table view to give a good user experience.
So, I have the following XIB
This XIB when loaded as a tableviewcell looks like this
I've since decided that I will not need a TableView, so I changed my XIB class from UITableViewCell to UIView. In a ViewController I added this code to viewDidLoad()
var nView = MyChartView.instanceFromNib() as! MyChartView
self.view.addSubview(nView)
And I got this as a result
As you can see, it ignores the margins and continues to right side (ignore the red color since I was using it to try and debug the problem. No chart data is not the problem either). I've printed the xib's frame width and I've seen that it's quite a bit bigger than the screen size, but I've not been able to fix it. Anyone can figure out the problem?
When you add a subView programmatically, you should also add constraints between the subView and its superView
var nView = MyChartView.instanceFromNib() as! MyChartView
self.view.addSubview(nView)
nView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[nView]|", options: [], metrics: nil, views: ["nView": nView]))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[nView]|", options: [], metrics: nil, views: ["nView": nView]))
You haven't constrained the MyChartView instance's width, either by specifying an exact size when you add the subview, or programmatically adding constraints between the MyChartView instance and its superview. Without doing one or the other of these, the view's dimensions will match whatever they are in the xib.
I have the code below which shows a popup. The popup has a main view which is black, and one subview which is white, and that subview has some labels, button and stuff the user interacts with. I want the surrounding black part to be partially transparent (alpha = 0.7), but the white part inside it to be fully opaque (alpha = 1.0). I could not achieve that, for some reason the value of the parent overrides its child values, and I get either a fully opaque view, or a view where also the labels and buttons are partially transparent.
What Can I do?
The code which shows the popup:
#IBAction func getLocation(sender: AnyObject) {
var p = PopupViewConrtoller(list: list, callback)
var x = UINib(nibName: "PickerPopup", bundle: nil).instantiateWithOwner(p, options: nil)
self.presentViewController(p, animated: false, completion: nil)
}
I changed opacity only in the attributes inspector.
It seems, it is not possible to make main view partially transparent without affecting the subviews added to main view.
What you can do is,
-set mainView background color as clear color
-add one more subview1 to mainView with size same as main view and set the background color as blackColor with desired transparency(alpha=0.7 as in required)
-add your subview2(which is having labels, buttons etc.) to mainView but above subview1 in layer order.
I hope it will help you to achieve what you want.
I figurred it is impossible to do it the way I was trying to. Rajeev has a good idea, but the best (and the one that apple reviewers will probably approve) is using popovers. This tutorial and this tutorial were helpful.
Edited following comments below - using constraints to control the layout.
I am trying to add/remove views controllers, which are set up within the storyboard, via a button. The button and any previously added views should move down the screen as per the following visual format strings:
Optional("V:|-50-[viewXIB0(100)]-30-[buttonKey]")
Optional("V:|-50-[viewXIB1(100)]-30-[viewXIB0(100)]-30-[buttonKey]")
Optional("V:|-50-[viewXIB2(100)]-30-[viewXIB1(100)]-30-[viewXIB0(100)]-30-[buttonKey]")
(This is the println output of the visual format string fed into NSLayoutConstraint.constraintsWithVisualFormat.)
Thanks already to the comments below, the view does appear each time the button is pressed, and moves down the page correctly. However, the spacing between the superview and top view is zero - without margin. A similar issue also when additional views are added - each one gets tucked underneath the spacing. The bottom spacing is greater than the desired 30. And, after a few inserts, even weirder things start to happen towards the top of the screen.
There are no constraint errors on build or running. When my computer is running super slow, I do see it appear briefly in the correct place, and then snap to the top of the screen. I think I can hear it laughing at me too, but that might be a joke. Autolayout is enabled in both View Controllers.
The full method I'm using to add the view from a touch up inside button is:
#IBAction func addVCBenj(sender: AnyObject)
{
// remove original constraint from button
if (topConstraintBenjV != nil)
{
view.removeConstraint(topConstraintBenjV)
topConstraintBenjV = nil
println("addVCBen constraints removed")
}
// remove previous constraints as views are added
if (constraintV != nil)
{
view.removeConstraints(constraintV!)
println("removed")
}
// instantiate add add the view within the storyboard VC
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myVC = storyboard.instantiateViewControllerWithIdentifier("VCtoInsert") as UIViewController
view.addSubview(myVC.view)
// create and add a unique key for each view in the dictionary of views
viewsDictionaryKey = "\(viewsDictionaryString)\(viewsDictionaryCounter)"
viewsDictionaryCounter += 1
viewsDictionary[viewsDictionaryKey!] = myVC.view
// create the visual format string for layout
suffixVisualFormatString_V = "[" + viewsDictionaryKey! + "(100)]-30-" + suffixVisualFormatString_V
visualFormatString_V = prefixVisualFormatString_V + suffixVisualFormatString_V
println(visualFormatString_V)
// create and add horizontal and vertical constraints
let constraintH = NSLayoutConstraint.constraintsWithVisualFormat("H:[\(viewsDictionaryKey!)(>=300)]", options: NSLayoutFormatOptions(0), metrics: metricsDictionary, views: viewsDictionary)
myVC.view.setTranslatesAutoresizingMaskIntoConstraints(false)
constraintV = NSLayoutConstraint.constraintsWithVisualFormat(visualFormatString_V!, options: NSLayoutFormatOptions(0), metrics: metricsDictionary, views: viewsDictionary)
self.view.addConstraints(constraintV!)
self.view.addConstraints(constraintH)
}
I've seen the WWDC videos, and searched for a similar problem, but in truth, I'm now so confused, I don't know what to even search for anymore.
As per a comment by BooRanger, by adding an IBOutlet to the top and bottom contraints, it was possible to manipulate the views each time a button was pressed. Ultimately, however, collection views are an easier way to add and remove views.
I'd like to create an "adding a new credit card viewController".
We don't want to aggravate users with all of the required fields presented at once.
This action contains several steps.
On each step the view-controller reveals a new subview (which contains one or more textfields) and collapses an old one (the current text field after it's text is validated).
I've created the ViewController on the storyboard. and placed all of its subviews one above the other.
I've created all of the constraints on the storyBoard, each subviews' clips to the above subview etc'.
i.e:
NSMutableArray *constraints = [[NSLayoutConstraint
constraintsWithVisualFormat:
#"V:|[titleView]-[subtitleView]-[amountView]-[cardNumView]-[cardsImagesView]-[mmYYCvvView]-[billingInfoView]-[buttomView]|"
options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom
metrics:nil
views:variableBindings] mutableCopy];
Each of these subviews contain a height constraint.
In each step one of the height constraints are set to zero and another one is changed from zero to the required height.
i.e:
self.hgtCrtMMYYCvv.constant = showFields? 50 : 0;
self.hgtCrtBillingInfo.constant = showFields? 140 : 0;
self.mmYYCvvView.hidden = !showFields;
self.billingInfoView.hidden = !showFields;
I got two issues:
Without calling layoutIfNeeded the initial layout was valid but did not change after changing the height constraints.
Calling layoutIfNeeded did not clip the bottom view to the last visible one - placed it at the bottom of the view as if all the subviews appear at once, but since some are hidden a gap was created.
changing the height constraint of the subviews was applied on the screen but still the gap stayed.
Please advise.
Calling "layoutIfNeeded" did not clip the bottom view to the last visible one - placed it at the bottom of the view as if all the subviews appear at once
Look at your constraints. You have pinned the bottom of the bottom view to the bottom of its superview! So its bottom must appear at the bottom of the superview, since that is what you instructed it to do.
Indeed, I am surprised that your constraints work at all. You have basically overdetermined them. If you give every field a height and pin its top and bottom, for every field, then it will be impossible to satisfy your constraints unless you are very lucky. The height of the superview is fixed, so your constraints would have to add up perfectly to that height.
I'm going to suggest a complete alternative approach, which I think you will find easier. Instead of messing with individual constants, plan what the correct (not overdetermined) constraints would be for each possible situation, and store those constraints in properties. Now when you want to hide/reveal a field, you just remove all the constraints and swap in another set.
This will also solve the layoutIfNeeded problem.
It happens that I have an actual example showing how to do this. (It is written in Swift, but I'm sure you can compensate mentally.) In my example code, we have three rectangles; I then remove one rectangle and close the gap between the remaining two. The preparation of two sets of constraints is tedious but elementary:
let c1 = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(20)-[v(100)]", options: nil, metrics: nil, views: ["v":v1]) as [NSLayoutConstraint]
let c2 = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(20)-[v(100)]", options: nil, metrics: nil, views: ["v":v2]) as [NSLayoutConstraint]
let c3 = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(20)-[v(100)]", options: nil, metrics: nil, views: ["v":v3]) as [NSLayoutConstraint]
let c4 = NSLayoutConstraint.constraintsWithVisualFormat("V:|-(100)-[v(20)]", options: nil, metrics: nil, views: ["v":v1]) as [NSLayoutConstraint]
let c5with = NSLayoutConstraint.constraintsWithVisualFormat("V:[v1]-(20)-[v2(20)]-(20)-[v3(20)]", options: nil, metrics: nil, views: ["v1":v1, "v2":v2, "v3":v3]) as [NSLayoutConstraint]
let c5without = NSLayoutConstraint.constraintsWithVisualFormat("V:[v1]-(20)-[v3(20)]", options: nil, metrics: nil, views: ["v1":v1, "v3":v3]) as [NSLayoutConstraint]
self.constraintsWith.extend(c1)
self.constraintsWith.extend(c2)
self.constraintsWith.extend(c3)
self.constraintsWith.extend(c4)
self.constraintsWith.extend(c5with)
self.constraintsWithout.extend(c1)
self.constraintsWithout.extend(c3)
self.constraintsWithout.extend(c4)
self.constraintsWithout.extend(c5without)
NSLayoutConstraint.activateConstraints(self.constraintsWith)
But the payoff comes when it is time to swap the middle view in or out of the interface: it's trivial. Just remove or insert it, and then remove all constraints and now insert the complete new set of constraints appropriate to the situation, which we have already prepared:
#IBAction func doSwap(sender: AnyObject) {
if self.v2.superview != nil {
self.v2.removeFromSuperview()
NSLayoutConstraint.deactivateConstraints(self.constraintsWith)
NSLayoutConstraint.activateConstraints(self.constraintsWithout)
} else {
self.view.addSubview(v2)
NSLayoutConstraint.deactivateConstraints(self.constraintsWithout)
NSLayoutConstraint.activateConstraints(self.constraintsWith)
}
}
The preparation of the multiple sets of constraints is tedious but can be done by rule, i.e. the constraints can be "machine-generated" in a loop (writing this is left as an exercise for you). Swapping constraints in and out is again according to a simple rule, since only one set will be right for the particular set of fields you wish to show/hide. So once this is set up it will be much simpler and more maintainable than what you are doing now.