UIViewController hidden behind UINavigationController but should be placed below - ios

I'm working on a app where I've got a NavigationBar at the top and added a UIViewController as an RootViewController.
Now my plan was to add a new Subview to this UIViewController. The new Subview extended also of UIViewController. I added de view (Gray Rect) of the controller to the UIViewController but it is placed behind the NavigationBar. I don't want that.. So I searched for a solution and found something interesting..:
When I just add a UIView() (Green Rect) to the UIViewController, the placing of the view works perfectly, as I would love see it from the other UIViewController-View.
My code looks like following:
class DashboardController: UIViewController {
var ccview:ContactCircleController!
override func viewDidLoad() {
super.viewDidLoad()
edgesForExtendedLayout = []
self.view.backgroundColor = .white
let test = UIView()
test.backgroundColor = .green
test.frame = CGRect(x: self.view.frame.width - 200, y: 0, width: self.view.frame.width / 2, height: self.view.frame.width / 2)
self.view.addSubview(test)
setup()
}
func setup(){
ccview = ContactCircleController()
ccview.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width / 2, height: self.view.frame.width / 2)
ccview.edgesForExtendedLayout = UIRectEdge.top
self.view.addSubview(ccview.view)
}}
I've already unchecked the "Extend Edges" - toggle of the navigationcontroller on the storyboard. I also added edgesForExtendedLayout = [] to the UIViewController and for the UIView it worked fine. But for the view of another UIViewController... it didn't worked.
Thank you!

If you use Debug View Hierarchy, you will see that your gray ccview.view is not behind the navigation bar, but rather it is not keeping the height of self.view.frame.width / 2.
This is because the .view of a UIViewController instantiated from Storyboard has by default an .autoresizingMask = [], whereas the .view of a UIViewController instantiated without a storyboard has a default .autoresizingMask = [.flexibleWidth, .flexibleHeight].
You can correct this by changing setup() to:
func setup(){
ccview = ContactCircleController()
ccview.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width / 2, height: self.view.frame.width / 2)
// remove this line
//ccview.edgesForExtendedLayout = UIRectEdge.top
// add this line
ccview.view.autoresizingMask = []
self.view.addSubview(ccview.view)
}

Related

How to Increase Navigation Bar Height

1. How can I increase the hight of my navigation bar just like Apple did for the iMessage app:
2. How do they make the navigation bar expand when the titleView is clicked to look like this:
I've tried creating a larger titleView, but it just gets clipped to the bounds of the default navigation bar height. How are they able to achieve this? Also, my view controllers are embedded in a navigation controller programmatically.
Unsure if you've just embedded Navigation into the app... or set this up programatically.
You'll need to add your navBar as a sort of "IBOulet", and then modify it's height based on an function/action/etc.
Say when you click "name button"; then something akin to "mainNavBar.heightAnchor.constant = 400" (where 400 is the new height).
Or if you've set this up manually, and have a auto layout constraint on the height of your nav bar, then you could set that up as a IBOutlet; and access it easier.
It would help if you posted some of your code, or atleast how you've set this up.
Use this class for navigation bar height :
import Foundation
import UIKit
class NavigationBar: UINavigationBar {
//set NavigationBar's height
//For iphonex, I recommended to set the minimum height to 88 or higher.
var customHeight : CGFloat = 100
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
translatesAutoresizingMaskIntoConstraints = false
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: customHeight)
}
override func layoutSubviews() {
super.layoutSubviews()
self.tintColor = .black
frame = CGRect(x: frame.origin.x, y: 0, width: frame.size.width, height: customHeight)
// title position (statusbar height / 2)
setTitleVerticalPositionAdjustment(-10, for: UIBarMetrics.default)
for subview in self.subviews {
var stringFromClass = NSStringFromClass(subview.classForCoder)
if stringFromClass.contains("BarBackground") {
subview.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: customHeight)
subview.backgroundColor = .yellow
}
stringFromClass = NSStringFromClass(subview.classForCoder)
if stringFromClass.contains("BarContent") {
subview.frame = CGRect(x: subview.frame.origin.x, y: 20, width: subview.frame.width, height: customHeight - 20)
}
}
}
}
Use navigation view instead of navigation bar or use custom view on viewDidLoad function hide navigation bar and show your custom view

Move UIView from ViewController to Window

Thanks for taking the time to read thus. So basically, I have a UIView in my UIViewController. I want a user to be able to press a button and then the UIView moves from my UIViewController to the my application's window so that the UIView will be above all UIViewControllers. The only thing I could think of doing was
class ViewController: UIViewController {
var window = UIApplication.shared.keyWindow!
var view = UIView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(view)
}
func tappedAction() {
window.bringSubview(toFront: view)
}
}
but that didn't work. Does anyone have any ideas on how to accomplish this?
You cannot just bring the subview that's in your UIViewController to the front of your UIWindow.
You need to:
Remove the UIView from the UIViewController.
Add the UIView to the main UIWindow.
I chose to do this in this way:
import UIKit
class ViewController: UIViewController {
var customView: UIView!
// Load the main view of the UIViewController.
override func loadView() {
view = UIView()
}
override func viewDidLoad() {
super.viewDidLoad()
// Load the custom view that we will be transferring.
self.customView = UIView(frame: .init(x: 100, y: 250, width: 250, height: 250))
self.customView.backgroundColor = .red
view.addSubview(customView)
// Transfer the view. Call this method in your trigger function.
transfer(self.customView)
}
func transfer(_ view: UIView) {
// Remove the view from the UIViewController.
view.removeFromSuperview()
// Add the view to the UIWindow.
UIApplication.shared.windows.first!.addSubview(view)
}
}
You must set frame fro view at var view = UIView()
then you should add to window window.addSubview(view)
If your view is added on window then window.bringSubview(toFront: view) will work otherwise it will not.
If your view is added on window then you can use bringSubview(toFront:) like that:
Example:
let window = UIApplication.shared.keyWindow!
let view1 = UIView(frame: CGRect(x: window.frame.origin.x, y: window.frame.origin.y, width: window.frame.width, height: window.frame.height))
window.addSubview(view1);
view1.backgroundColor = UIColor.black
let view2 = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 50))
view2.backgroundColor = UIColor.white
window.addSubview(view2)
UIApplication.shared.keyWindow!.bringSubview(toFront: view1)
So you need to add your view in window:
window.addSubview(view)

Frame on ViewController

I have this class:
extension UIViewController {
func waiting() -> UIView{
let strLabel = UILabel(frame: CGRect(x: 50, y: 0, width: 200, height: 50))
strLabel.text = "Aguarde..."
strLabel.textColor = UIColor.whiteColor()
let messageFrame = UIView(frame: CGRect(x: view.frame.midX - 90, y: view.frame.midY - 25 , width: 180, height: 50))
messageFrame.layer.cornerRadius = 15
messageFrame.backgroundColor = UIColor(white: 0, alpha: 0.40)
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.White)
activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
activityIndicator.startAnimating()
messageFrame.addSubview(activityIndicator)
messageFrame.addSubview(strLabel)
view.addSubview(messageFrame)
return messageFrame
}
}
When I need use this class I use:
class MyController: UIViewController{
....
func x(){
let messageFrame = waiting()
//my code
messageFrame.removeFromSuperview()
}
}
The problem is when the frame is showed if I touch anywhere on my app this frame is hidden. I need that when this frame is showed other options staying disabled, when I finish the frame, the options of app is enabled again. How can I do it?
Change the view that you are returning a bit:
Make a "container" view that has the same frame as the view controller's view. Give this view a "clear" background.
Make a "background" view, same size as the "container" view and add it to the container. Give this view a black background and an alpha of, say, 0.4.
Now put the view that you are currently building in your "waiting" method, and add it to the "container" view (NOT to the background view, this should be a sibling of the background view, otherwise your alert will also be transparent).
Make sure all user interaction is disabled on your views (might only need it disabled on the "container" view).
This gives you a container view that covers the entire screen, and it has 2 children: a transparent background of the same size, and your current "waiting" alert thing.
Now if you add the "container" view to your view controller's view, the user can only touch on the container view which does nothing.
(Keep in mind that this doesn't handle screen rotation...you will have to do that yourself if needed.)

How to embed stack view in scroll view programmatically

I have tried embedding it, but my stack view is dynamic and my app is also changing orientations time to time. I have segment control at the end of the view.
I have also tried googling it but had no luck. thanks in advance.
So far I have done:
In view did load:
mainStackView.axis = UILayoutConstraintAxis.Vertical
mainStackView.spacing = 3
scrollView.frame = self.view.bounds
scrollView.addSubview(mainStackView)
view.addSubview(scrollView)
In view did layout:
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
let top = topLayoutGuide.length
let bottom = bottomLayoutGuide.length
self.mainStackView.frame = CGRect(x: 0, y: top, width: view.frame.width, height: view.frame.height - top - bottom).insetBy(dx: 10, dy: 10)
dispatch_async(dispatch_get_main_queue())
{
self.scrollView.frame = self.view.bounds
self.scrollView.contentSize = CGSize(width: self.view.bounds.width, height: self.segmentedControl.frame.origin.y + self.segmentedControl.frame.height + 50)
}
print(scrollView.contentSize)
}
You need to set the height constraint of segment control.
For Example:
segmentedControl.heightAnchor.constraintEqualToConstant(50).active = true
More over, you can Add Empty bottom view to avoid stack view's must fill mechanism. This will show you desired view output.
var bottomView = UIView(frame: CGRectZero)
stackView.addArrangedSubview(bottomView)

Twitter-like UIScrollView with ViewControllers as pages

Video of the issue!
Example of what I mean by Twitter-like UIScrollView:
I basically have it working, but I have this small glaring issue and I don't know where it is coming from. I have checked all the constraints and values for my two view controllers, but something is off.
In short,
The code that creates the NavBar and then populates it with the two ViewControllers side by side:
override func viewDidLoad() {
super.viewDidLoad()
var navBar: UINavigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.bounds.width, 64))
navBar.barTintColor = UIColor.blackColor()
navBar.translucent = false
//Creating some shorthand for these values
var wBounds = self.view.bounds.width
var hBounds = self.view.bounds.height
// This houses all of the UIViews / content
scrollView = UIScrollView()
scrollView.backgroundColor = UIColor.clearColor()
scrollView.frame = self.view.frame
scrollView.pagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
scrollView.bounces = false
self.view.addSubview(scrollView)
self.scrollView.contentSize = CGSize(width: self.view.bounds.size.width * 2, height: hBounds)
//Putting a subview in the navigationbar to hold the titles and page dots
navbarView = UIView()
//Paging control is added to a subview in the uinavigationcontroller
pageControl = UIPageControl()
pageControl.frame = CGRect(x: 0, y: 35, width: 0, height: 0)
pageControl.pageIndicatorTintColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.3)
pageControl.currentPageIndicatorTintColor = UIColor.whiteColor()
pageControl.numberOfPages = 2
pageControl.currentPage = 0
self.navbarView.addSubview(pageControl)
//Titles for the nav controller (also added to a subview in the uinavigationcontroller)
//Setting size for the titles. FYI changing width will break the paging fades/movement
navTitleLabel1 = UILabel()
navTitleLabel1.frame = CGRect(x: 0, y: 8, width: wBounds, height: 20)
navTitleLabel1.textColor = UIColor.whiteColor()
navTitleLabel1.textAlignment = NSTextAlignment.Center
navTitleLabel1.text = "Title 1"
self.navbarView.addSubview(navTitleLabel1)
navTitleLabel2 = UILabel()
navTitleLabel2.alpha = 0.0
navTitleLabel2.frame = CGRect(x: 100, y: 8, width: wBounds, height: 20)
navTitleLabel2.textColor = UIColor.whiteColor()
navTitleLabel2.textAlignment = NSTextAlignment.Center
navTitleLabel2.text = "Title 2"
self.navbarView.addSubview(navTitleLabel2)
//Views for the scrolling view
//This is where the content of your views goes (or you can subclass these and add them to ScrollView)
feedViewController = storyboard?.instantiateViewControllerWithIdentifier("FeedController") as FeedViewController
view1 = feedViewController.view
addChildViewController(feedViewController)
feedViewController.didMoveToParentViewController(self)
view1.frame = CGRectMake(0, 0, wBounds, hBounds)
self.scrollView.addSubview(view1)
self.scrollView.bringSubviewToFront(view1)
//Notice the x position increases per number of views
secondViewController = storyboard?.instantiateViewControllerWithIdentifier("SecondController") as SecondViewController
view2 = secondViewController.view
addChildViewController(secondViewController)
secondViewController.didMoveToParentViewController(self)
view2.frame = CGRectMake(wBounds, 0, wBounds, hBounds)
self.scrollView.addSubview(view2)
self.scrollView.bringSubviewToFront(view2)
navBar.addSubview(navbarView)
self.view.addSubview(navBar)
}
I've looked at my storyboard and both ViewControllers seem identical in regards to their constraints.
I know this is an issue because both ViewControllers are populated by UITableViews. When I scroll through the SecondViewController, it works perfectly. When I scroll through the FeedViewController, there is a small white space at the top that I can't seem to get rid of and it shows that the text cuts off there. I've been stuck on this for a long time and if there is any other information needed, I'll gladly provide it.
Edit: Included video of the issue. If I could, I would bounty this question right now. I don't understand the cause
Update: After swapping both ViewController positions, I have noticed that the problem does not lie with either ViewController. The problem lies with page 1 being set lower. When swapped, the original SecondViewController also experienced the same behavior
So, I think everyone who implements this runs into this issue at some point. The issue isn't with the first ViewController. Simply adjust the constraint to be 44 from the top. The issue is with the second ViewController and it isn't so much an issue when you understand how they work. Technically, it is off to the side and hence its top constraint does not adhere to the Navigation Bar, so what you have is a constraint - 20. Which, depending on how you originally placed your constraints, can give you this seeming issue.
But basically, anyone and everyone will run into this issue when implementing this.
TL;DR: To make everything seamless, your second, third, fourth, fifth, etc. page View Controllers need a constraint + 20 of your first View Controller. With my set-up, I use a constraint of 44 for my first View Controller and hence 64 for the second

Resources