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.
Related
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.
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 have three child VCs that are added to a parent VC. In one of my child VC, I have a view being loaded from a nib. In that nib, I set up a UITextView with no constraints and disabled scrolling so that it dynamically sizes depending on the text. This all works fine so far, I can enter or remove text from the UITextView and it resizes accordingly.
However, if I switch tabs (child VCs), and return back to the one with my UITextView, my UITextView is now of height 0 (cannot be seen). I'm not sure what is causing this to happen, everything works fine until I switch views and return.
My first thought would be to reconfigure my UITextView on viewDidAppear() when returning to my child VC, except my UITextView outlet and setup method is in a separate UIView subclass so I cannot call viewDidAppear(). I'm not even sure if that would be a fix, just what I would guess.
MyParent.swift: UIViewController
MyChild1.swift: UIViewController
MyChild2.swift: UIViewController
MyChild3.swift: UIViewController
MyView.swift: UIView - Custom View loaded from Nib
(this is where my UITextView outlet is and setup for it)
In MyChild3 for example I create the custom view with:
let view = Bundle.main.loadNibNamed("MyView", owner: self, options: nil)?.first! as! MyView
view.myModel = model
view.configure()
Then, in MyView.swift which is the custom class that the nib uses, I have my UITextView outlet and setup method:
#IBOutlet weak var textView: UITextView!
public func configure() {
configureTextView()
}
private func configureTextView() {
textView.delegate = self
textView.text = myModel.text
textView.isScrollEnabled = false
}
EDIT:
I should mention that my UITextView is a part of a horizontal StackView along with a UIImageView, as follows:
UIImageView
UITextView
When I return to my VC, the UIImageView takes up 100% of the height in the StackView, although before leaving my VC, the UITextView height was sized correctly to its text. After doing Show View Hierarchy, I cannot even find my UITextView anymore, only the UIImageView seems to be in the StackView. I essentially want my UITextView to take up as much height as it needs in the StackView, and the UIImageView take up the rest, but the UIImageView is taking it all up.
I did manage to find this error?
I would try setting content compression resistance of the textView to a higher value, maybe that's what causing ambiguity:
textView.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 999), for: .vertical)
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'm trying to allow the height of the subview (the white box inside the view. The view controller swift file is a separate XIB file) depending on the amount of content in it. How do I do this?
This is what I have so far for it:
#IBOutlet weak var myScrollView: UIScrollView!
#IBOutlet weak var myContentView: UIView!
override func viewDidLayoutSubviews()
{
let scrollViewBounds = myScrollView.bounds
let containerViewBounds = //I am not sure how to do the rest
}
The best way to achieve this is to use autolayout. You have great tutorail here:
http://www.raywenderlich.com/50317/beginning-auto-layout-tutorial-in-ios-7-part-1
If you don't want to use it, set reference for your view inside your view controller for that view, and use various functions to calculate height. There are several ways for labels ( Adjust UILabel height to text ), text field( How to set UITextField height? ), etc.