I have a ViewController and a container view testcontrollerViewController inside of it. I noticed that after I'm adding constraints to that container view in the storyboard (all the edges match the safe area) its width decreases a little bit, but if I remove constraints width changes back to normal. (Switches from 724px to 712px and back)
Is it a bug or am I doing something wrong?
I print container view width in its viewDidAppear function.
class testcontrollerViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.backgroundColor = .red
print("container vc load \(testview.frame.width)")
}
override func viewDidAppear(_ animated: Bool) {
print("container vc appear \(testview.frame.width)")
print("container view safe area insets \(view.safeAreaInsets)")
}
#IBOutlet var testview: UIView!
In my opinion, I think because there is a gap between the superview and safearea. When you constraints the container view it relates to the safearea only - I think that is the reason the width changed above.
Related
I have a Scroll View set to a fixed height inside my View Controller. I want to use a navigation bar on top with large titles, so when i scroll the Scroll View, it should collapse like in a Navigation Controller. Is it possible to do this? My scene look like this:
The navigation bar has top/left/right 0 constraints agains the View. Currently it stays on top correctly, however it won't collapse on scroll as expected.
Do not use a "loose" navigation bar like this. Use a navigation controller, even if you do not intend to do any navigation. It gives you the desired behavior, for free.
In the end i created a custom view to replicate the Navigation Bar. Here you can see how it looks and read the steps below to replicate:
To setup your View Controller to be used with a custom Scroll View, first make sure you are using Freeform size for your controller. To do this, select Freeform in the size inspector and set the height to your new Scroll View's height:
Insert your Scroll View and setup 0 top/left/right/bottom constraints, so it will be the same size as your View Controller:
Add your content to your scroll view as usual
Now to create your custom Navigation Bar, add a View outside of your Scroll View and setup constraints like this:
Notice a few things:
the top constraint is aligned to the Superview instead of the Safe Area, so the view goes behind the status bar
The height is set to >= 44, so its a minimum height and can expand if the content is larger
On the Attribute Inspector, select clip to bounds, so your content inside the view won't overflow(like in CSS, overflow:hidden)
At this point you might see some errors in your Storyboard, but don't worry about it: its because you don't have any content in your View and it doesn't know how tall it should be
Set the View background to transparent and add a "Visual Effect View with Blur" inside, with 0 top/left/right/bottom constraints. This will blur the content behind the custom navigation bar
Now make sure that you check the Safe Area Layout Guide checkbox in your navigation bar view(its above the constraints setup):
This way you can add content inside the view that won't be behind the status bar, because its outside of the safe area. And it works with the notch too.
Add a label inside your view, set top and bottom constraints to Safe Area and make sure you have a fixed height constraint defined too:
Now you can also see that the errors in your Storyboard are gone :) At this point this is how everything should look like:
Now the coding part. In your ViewController, make outlets for both the ScrollView and the custom navigation bar. To do this, switch to the assistant editor(the venn-diagram symbol top right), select the element in your storyboard, hold down CTRL and drag inside your ViewController class:
Do the same for your View that is your navigation bar:
#IBOutlet weak var mainScrollView: UIScrollView!
#IBOutlet weak var customNavigationBar: UIView!
Next, you need to add the UIScrollViewDelegate to your class, so you can listen to the scroll event and get the actual Y position of the current scroll offset using the scrollViewDidScroll function:
class ViewController: UIViewController, UIScrollViewDelegate {
You also need to setup the delegate in your viewDidLoad hook:
mainScrollView.delegate = self
Create a new function called scrollViewDidScroll to get the scroll position and you can use this to do various animations with other elements. In this case, if the scroll position reaches 44(this is the height i set for my custom navigation bar), it will animate to full opacity:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let y = self.mainScrollView.contentOffset.y
let barHeight = 44
if(y < barHeight) {
customNavigationBar.alpha = y/CGFloat(barHeight)
} else {
customNavigationBar.alpha = 1
}
}
You can use the same logic to animate the label inside the navigation bar, change its size etc...
The full ViewController:
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var mainScrollView: UIScrollView!
#IBOutlet weak var customNavigationBar: UIView!
override func viewDidLoad() {
super.viewDidLoad()
mainScrollView.delegate = self
customNavigationBar.alpha = 0
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let y = self.mainScrollView.contentOffset.y
let barHeight = 44
if(y < 44) {
customNavigationBar.alpha = y/CGFloat(barHeight)
} else {
customNavigationBar.alpha = 1
}
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var height = CGFloat()
if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
height = 130
}
else {
height = 44
}
UIView.animate(withDuration: 0.5) {
self.navBarHeightConstraint?.constant = height
self.view.layoutIfNeeded()
}
}
I setup a view controller scene with interface builder. I add a view to my controller and add a custom class (CustomView) to it.
The view controller code
class ViewController: UIViewController {
#IBOutlet var myCustomView: CustomView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
print(myCustomView.frame.origin)
}
}
The question
Why the origin of myCustomView is equal to (-4.0, 64.0) and not (0, 64) ?
when you add the constraints,your constraints to margin remains unchecked. Because of constraints to margin it gives you a frame that starts with (-4.0,64.0) like that...
Try to pinned all your edges without constraints to margin that will solve your problem ..
For more guide about constraints to margin, check this link
I'm trying to set up a view inside a container view. It has to be done this way due to several different controllers for the views inside the swipeView.
#IBOutlet weak var containerView: ContainerView!
override func viewDidLoad() {
super.viewDidLoad()
//Some other stuff
//Create new swipeView
var swipeView = MDCSwipeToChooseView(frame: containerView.frame, options: options)
//Add the view from the controller to the swipeView
swipeView.addSubview(containerViewController.view)
//Add the swipeView to the main view
self.view.addSubview(swipeView)
I end up with this
The white area is the view that should inherit containerView's size. The containerView is the pink one in the background and it's shown properly. I have noticed that containerView.frame returns the size of the component from the storyboard, see pic 2. The frame obtained by calling on the containerView.frame is the one before the view is resized to meet all constrains. How do i get the proper values?
Placing the same code inside viewDidLayoutSubviews() instead of viewDidLoad() solved the issue.
I am adding a UIView to a container view programmatically, (the container view however is created in storyboard). Here is the code:
class ViewController: UIViewController{
#IBOutlet weak var dwView: UIView!
private var dwSelector = dwSelectorView()
override func viewDidLoad(){
super.viewDidLoad()
addDWSelector()
}
func addDWSelector(){
dwSelector.setTranslatesAutoresizingMaskIntoConstraints(false)
dwSelector.frame = CGRectMake(self.dwView.bounds.origin.x, self.dwView.bounds.origin.y, self.dwView.bounds.width / 2.0, self.dwView.frame.height)
println("dw height: \(self.dwView.frame.height)")
//prints 568, way too large of a value
self.dwView.addSubview(dwSelector)
}
}
The heigh of dwView is 123 in storyboard but the print state printed 568 and so now this is what it looks like:
You should always not rely on -(void)viewDidLoad since view bounds is incorrect at this point or - (void)viewWillAppear if you are using auto layout to set your view's frame. If you layout view in UIViewController, viewDidLayoutSubviews() is a appropriate place, if you layout subviews in UIView, it is layoutSubviews().
Check this article to get more details:Where to progmatically lay out views in iOS 5 (and handling orientation changes)
have you tried to call addDWSelector() in viewWillAppear()?
I have a container view that I want to initially be off the bottom of the screen. It should be easy, but I seem to be missing something fundamental. I have previously done something similar by moving controls off the left side of the screen with:
control.center.x -= view.bounds.width
I have a very simple storyboard setup:
And my view controller looks like this:
class ViewController: UIViewController {
#IBOutlet var containerView: UIView!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
containerView.center.y += view.bounds.height
}
}
When run, the container view is right where it shows in the storyboard and is not offscreen. Even setting the containerView's center.y to a specific value such as containerView.center.y = 50 will do nothing. The only way I can get it to show off screen is by adding animation in the viewDidAppear:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
UIView.animateWithDuration(0, animations: {self.containerView.center.y += self.view.bounds.height})
}
But that just seems like a workaround and not the proper way to do it. I have been dealing with this for hours, reading blogs, reading other Stackoverflow questions, but I cannot find the solution. I do not have constraints on the container, although I did test it with constraints and that did not make a difference. Any help is appreciated.
It is possible that the main view's `layoutSubviews is called after you reposition the container view, so it will default to the position specified in the storyboard.
The proper way to do is to have proper layout constraints in storyboard, make an outlet for the bottom edge constraint and set this constraint's constant property.