View is behind the extended navigation bar - ios

In the viewDidLoad of the root view controller of the UINavigationController I add the subview to navigationBar and update its frame. Note that the new frame has a longer height than that of the original frame.
The problem is that the top of the subview in view of root view controller is hidden behind the "extended" nav bar even though, in the storyboard, the constraint of the subview is set to vertical spacing to top layout guide. If I remove my subview from from nav bar, the problem disappears.
So, question:
Is it considered normal to add subview to the default nav bar provided by navigation controller?
When is the best time to add subview and update the frame of nav bar?
How can I resolve the issue where part of the view is hidden behind the nav bar with my sub view in it?
Code snippet:
override func viewDidLoad() {
super.viewDidLoad()
title = "Tile"
if let frame = navigationController?.navigationBar.frame {
let label = UILabel(frame: CGRect(x: 0, y: frame.height - 25, width: 25, height: 25))
label.text = "1234"
navigationController?.navigationBar.frame = CGRect(origin: frame.origin, size: CGSize(width: frame.width, height: frame.height + 25))
navigationController?.navigationBar.addSubview(label)
}
}

Related

iOS SearchController Searchbar does not work after adding constraint programmatically

I have an iOS project whereby the SearchController's SearchBar is added to the view as a subview:
let subView = UIView(frame: CGRect(x: 0, y: 65.0, width: 350.0, height: 45.0))
subView.addSubview((self.searchController.searchBar))
self.view.addSubview(subView)
As shown in the screenshot below, the searchbar is partially obscured by the navigation bar:
As such, I added the following constraint:
subView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([subView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor)])
The searchbar is now positioned correctly below the navigation bar as shown:
However, I'm now not able to click onto the searchbar to input any text. How can I resolve this?
How I resolved this at the end.
I still made use of a subView for the searchbar. But this time, instead of giving a fixed value for y, I used the heights of the safeArea and the navigation bar as shown:
let window = UIApplication.shared.windows.first
let safeheight = window?.safeAreaInsets.top
let navHeight = self.navigationController?.navigationBar.frame.height
let subView = UIView(frame: CGRect(x: 0, y: safeheight! + navHeight!, width: 350.0, height: 45.0))
subView.addSubview((self.searchController.searchBar))
self.view.addSubview(subView)
This time, the searchbar was positioned correctly and worked perfectly.

How to show view in statusbar in iOS?

I want to show view in statusbar which should be visible to my all View Controller
Example: I need to show "No Internet Connection" in status bar and it will hide after we get connectivity of internet?
You can add a view directly to the status bar:
// get the status bar
let statusBar = UIApplication.shared.value(forKey: "statusBarWindow") as? UIWindow
// create a subview & add it to the status bar
let subview = UIView(frame: CGRect(x: 20, y: 0, width: 20, height: 10))
subview.backgroundColor = .red
statusBar?.addSubview(subview)
statusBar?.bringSubview(toFront: subview)
If you declare the statusBar and the noNetworkConnectionView globally, you can access it from anywhere to show or hide it dynamically.
Result:
Disclaimer: I am not sure whether Apple will approve apps that modify the status bar in this way.
Create a view
let statusView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 40))
statusView.tag = 1101 //used to get view whenever required
statusView.backgroundColor = UIColor.red.withAlphaComponent(0.6)
//set other properties
Show view on window
//show on window, will be visible in all view controller
UIApplication.shared.delegate?.window??.addSubview(statusView)
UIApplication.shared.delegate?.window??.bringSubview(toFront: statusView)
Hide View
//to hide that view
UIApplication.shared.delegate?.window??.viewWithTag(1101)?.removeFromSuperview()

Reproducing Music App's NavigationBar Animation

In my project I want to achieve a very similar animation like in the Artist-ViewController of the stock Music App.
When the UITableView scrolls to a specific offset the navigationBar's tintColor, the color of the title and the color of the statusbar changes.
To achieve that I added an UIView (I named it containerView) as a subView of the UINavigationBar and inside that containerView I added a UIBlurEffect. Now when the tableView scrolls I am listening to scrollViewDidScroll to change the earlier named attributes which works really good.
containerView.frame = CGRect(x: 0, y: -(statusBarHeight), width: UIScreen.main.bounds.width, height: frame.height + statusBarHeight)
containerView.clipsToBounds = true
insertSubview(containerView, at: 0)
let blurEffect = UIBlurEffect(style: .extraLight)
overlayView.effect = blurEffect
overlayView.backgroundColor = unOverlayColor
let height = frame.height + statusBarHeight
overlayView.frame = CGRect(x: 0, y: containerView.frame.height, width: containerView.frame.width, height: height)
containerView.addSubview(overlayView)
My only problem is that the containerView is placed above the UINavigationBar's navigationItems and therefore hides them.
So my question is how can I add the containerView behind the navigationItems to the UINavigationBar?
I figured out that it has to be something with the way how navigationBars are handled in iOS 11 because on iOS 10 everything works fine.

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.)

Add children view controller in a controller that pushed in a navigation controller

I had a simple view controller called ViewController, it is in a navigation stack, the structure is like following:
UINavigationControlelr
UIViewController
ViewController
The ViewController had a children view controller called a, I add a to ViewController, a's frame is ViewController's bounds. I have two another UIViewController called b and c, I add b and c to a. b's frame is
CGRect(x: 0, y: 0, width: 100, height: 100)
c's frame is
CGRect(x: 0, y: 100, width: 100, height: 100)
If I set the navigation bar translucent to true, the view shows right, like below:
But when I set the navigation bar translucent to false, something weird happened.
What's going on. How to fix that problem?
The project is simple, you can also download from here to test the problem: download
The problem cause is by default UIViewController's view has FlexibleWidth &
FlexibleHeight
bController and cController's view has extra gap 64 pixel (navBar(44) + status bar(20)). Now when you resize it has those 'FlexibleWidth&FlexibleHeight` to play with.
Solution could be making autoresizingMask to .None
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.greenColor()
bController = b()
cController = c()
bController.view.autoresizingMask = .None
cController.view.autoresizingMask = .None
displayContentController(bController, toFrame: CGRect(x: 0, y: 0, width: 100, height: 100))
displayContentController(cController, toFrame: CGRect(x: 0, y: 100, width: 100, height: 100))
}
You are setting view frames in viewDidLoad, please note that view layout is not done until much later. viewWillLayoutSubviews and viewDidLayoutSubviews, respectively, are called before and after the layout occurs).
Move your frame setting code to viewDidLayoutSubviews and it will work fine.
NOTE:: You could also get away with setting the view's frame in viewWillAppear but that's the the right place to set frames. viewWillAppear is called just before view is about to appear, view layout can change after the view has appeared (e.g. in response to device rotation).

Resources