I know this has been asked and answered quite a lot but I can't seem to make it work.
I have an UIViewController with a full size WKWebView. Everything works fine, except for the status bar that looks out of place on notch devices.
The problem is, nothing works, even my own code from other apps.
Things that I tried so far (this works just fine in another app...):
if #available(iOS 13, *)
{
let statusBar = UIView(frame: (UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.windowScene?.statusBarManager?.statusBarFrame)!)
//statusBar.backgroundColor = UIColor(red: 242/255, green: 242/255, blue: 242/255, alpha: 1.0)
statusBar.backgroundColor = UIColor.red
UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.addSubview(statusBar)
}
else
{
let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
if statusBar.responds(to:#selector(setter: UIView.backgroundColor))
{
statusBar.backgroundColor = UIColor(red: 242/255, green: 242/255, blue: 242/255, alpha: 1.0)
}
}
And this:
extension UIApplication {
var statusBarUIView: UIView? {
if #available(iOS 13.0, *) {
let tag = 3848245
let keyWindow: UIWindow? = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
if let statusBar = keyWindow?.viewWithTag(tag) {
return statusBar
} else {
let height = keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
let statusBarView = UIView(frame: height)
statusBarView.tag = tag
statusBarView.layer.zPosition = 999999
keyWindow?.addSubview(statusBarView)
return statusBarView
}
} else {
if responds(to: Selector(("statusBar"))) {
return value(forKey: "statusBar") as? UIView
}
}
return nil
}
}
And everything from here:
How to change the status bar background color and text color on iOS 13?
Change Status Bar Color in iOS 13?
How to set Status Bar Style in Swift 3
Even tried doing it from my webpage displayed inside the WKWebView:
https://itnext.io/make-your-pwas-look-handsome-on-ios-fd8fdfcd5777
The only workaround that kinda fixes the issue is to change the background color of the main view.
The problem is, this will also change the color of the bottom section.
I even tried messing around with the style in the info.plist file.
Any ideas on what I'm missing?
you can just add that in your controller and then you check the hasNotch
extension UIDevice {
/// Returns `true` if the device has a notch
var hasNotch: Bool {
guard #available(iOS 11.0, *),
let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }),
window.safeAreaInsets.bottom >= 10 else {
return false
}
return true
}
}
I'm trying to put a UIview() to cover the full screen so when i press a button i can put a dark mode theme on it. it works ok the only problem is that it does not cover the nav bar which is something i want to do i have looked up a few things i found this code it works on the tutorial i have seen but does not work now think swift have updated the language making that solution old
if let window = UIApplication.shared.keyWindow {
let blackView = UIView()
blackView.backgroundColor = .black
view.addSubview(blackView)
blackView.frame = window.frame
}
Swift 4.X
You can add extension in your project.
extension UIView {
func addToWindow() {
let window = UIApplication.shared.keyWindow!
self.frame = window.bounds
window.addSubview(self)
}
}
Use:
bgView = UIView(frame: UIScreen.main.bounds)
bgView.backgroundColor = UIColor(white: 0, alpha: 0.4)
bgView.addToWindow()
I have an overlay (UIImageView) which should have a transparent background and alpha. How can I set the imageview such that it covers the entire screen? Currently, it covers the screen but not the UIStatusBar. I am adding the view in AppDelegate's main window as a subview.
The code:
let overlay1 = UIImageView(image: UIImage(named: "overlay-image"))
overlay1.contentMode = .scaleAspectFit
overlay1.backgroundColor = UIColor.black
overlay1.alpha = 0.87
overlay1.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
overlay1.isUserInteractionEnabled = true
overlay1.layer.zPosition = 1
(UIApplication.shared.delegate as? AppDelegate).window.addSubview(overlay1)
After discussion in comments found that changing the backgroundColor of statusBar is the reason why your code is not working properly.
By printing the superView of statusBar I found that statusBar is not added on UIWindow instead it is on UIStatusBarWindow which is probably above the mainWindow.
Also please don't use force unwrapping it can be cause of crash. At last I put a guard to fetch the statusBar, to check if it responds to backgroundColor property, to fetch its superView and adding the overlay on this superView got it working.
Your check for respondToSelector is also wrong. See below code it works as per your requirement.
guard let statusBar = UIApplication.shared.value(forKey: "statusBar") as? UIView, statusBar.responds(to: NSSelectorFromString("backgroundColor")), let superView = statusBar.superview else {return}
statusBar.backgroundColor = UIColor.red
let overlay1 = UIImageView(image: UIImage(named: "overlay-image"))
overlay1.contentMode = .scaleAspectFit
overlay1.backgroundColor = UIColor.black
overlay1.alpha = 0.87
overlay1.frame = superView.bounds
overlay1.isUserInteractionEnabled = true
overlay1.layer.zPosition = 1
superView.addSubview(overlay1)
Note: Changing the statusBar color is not recommended. You can set its style to default or light.
Okay I just tried with something and it worked. Just use it in your ViewController like:
// You should be using viewDidAppear(_:)
override func viewDidAppear(_ animated: Bool) {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate, let window = appDelegate.window {
let windowFrame = window.frame
let imageView = UIImageView(frame: windowFrame)
imageView.image = #imageLiteral(resourceName: "TakeOff") // use your image
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = UIColor.green.withAlphaComponent(0.2) // replace green with the color you want
window.addSubview(imageView)
}
}
But, remember one thing. It's not a good idea to add an image view as
an overlay. You should always use a UIView as an overlay and add the
image view as a sub view to that overlay.
Screenshot:
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.
Can I change the background color of a specific UITabBarItem in a UITabBar?
I know how to change all the background of the selected background, using:
[[UITabBar appearance] setBarTintColor:[UIColor redColor]];
[[UITabBar appearance] setTintColor:[UIColor blueColor]];
[[UITabBar appearance] setSelectedImageTintColor:[UIColor yellowColor]];
But can it be done for only one item without subclassing?
Thanks
You can add a subview to the parent tabBar, and set a background color on the subview. You can use the tabBar frame dimensions to calculate the offset and width of your tabBarItem, and then insert the subview underneath.
Example (in Swift):
// Add background color to middle tabBarItem
let itemIndex = 2
let bgColor = UIColor(red: 0.08, green: 0.726, blue: 0.702, alpha: 1.0)
let itemWidth = tabBar.frame.width / CGFloat(tabBar.items!.count)
let bgView = UIView(frame: CGRectMake(itemWidth * itemIndex, 0, itemWidth, tabBar.frame.height))
bgView.backgroundColor = bgColor
tabBar.insertSubview(bgView, atIndex: 0)
in Swift:
This solution is for apps using Auto Layout . Main difference between other solution is that: tabBar.frame.width is not getting actual device's screen width. So, bgView is appearing wrong place.
You can fix it with this : UIScreen.main.bounds.width
let tabWidth: CGFloat = UIScreen.main.bounds.width / CGFloat(self.tabbar!.items!.count)
let tabIndex: CGFloat = 2
let bgColor: UIColor = .redColor
let bgView = UIView(frame: CGRectMake(tabWidth * tabIndex, 0, tabWidth, 49))
bgView.backgroundColor = bgColor
self.tabbar!.insertSubview(bgView, atIndex: 0)
49 is a default height of UITabbar
Objective C solution:
int itemIndex = 3;
UIColor* bgColor = [UIColor colorWithRed:(245/255.f) green:(192/255.f) blue:(47/255.f) alpha:1];
float itemWidth = self.tabBarController.tabBar.frame.size.width / 5.0f; //5 = tab bar items
UIView* bgView = [[UIView alloc]initWithFrame: CGRectMake(itemWidth*itemIndex, 0,itemWidth, self.tabBarController.tabBar.frame.size.height)];
bgView.backgroundColor = bgColor;
[self.tabBarController.tabBar insertSubview:bgView atIndex:1];
Updated for Swift 4:
let itemIndex: CGFloat = 2.0
let bgColor = UIColor.ownBlue
let itemWidth = tabBar.frame.width / CGFloat(tabBar.items!.count)
let bgView = UIView(frame: CGRect(x: itemWidth * itemIndex, y: 0, width: itemWidth, height: tabBar.frame.height))
bgView.backgroundColor = bgColor
tabBar.insertSubview(bgView, at: 0)
You cannot really do this with the tint color property. See this post about the fact that even the setSelectedImageTintColor does not really seem to be working (it did not last time I checked).
The solution is to change the tintColor on the fly for the item in question. You can do this ad hoc whenever the selected item changes by implementing the UITabBarDelegate method tabBar:didSelectItem: e.g. in the app delegate.
Late to the game, but this is how I did it. I made the tabs look like buttons, with rounded views. The answers above, have issues when the device is rotated, or the app is sent into a split window.
This solution marries the background directly to each tab, using auto-layout, so they naturally follow the tab bar changes.
This is done on the custom subclass of UITabBarController.
First, I set the colors (white for unselected, and black for selected), of the icons in the tab items, then, I iterate the tabs, and insert backgrounds to each, using auto layout to have them cleave to the tabs:
override func viewDidLoad() {
super.viewDidLoad()
let appearance = UITabBarAppearance()
appearance.stackedLayoutAppearance.normal.iconColor = .white
appearance.stackedLayoutAppearance.selected.iconColor = .black
appearance.inlineLayoutAppearance.normal.iconColor = .white
appearance.inlineLayoutAppearance.selected.iconColor = .black
appearance.compactInlineLayoutAppearance.normal.iconColor = .white
appearance.compactInlineLayoutAppearance.selected.iconColor = .black
tabBar.subviews.forEach {
if let item = $0 as? UIControl {
let wrapper = UIView()
wrapper.isUserInteractionEnabled = false
wrapper.clipsToBounds = true
wrapper.backgroundColor = tabBar.tintColor
wrapper.layer.cornerRadius = 8
item.insertSubview(wrapper, at: 0)
wrapper.translatesAutoresizingMaskIntoConstraints = false
wrapper.leadingAnchor.constraint(equalTo: item.leadingAnchor).isActive = true
wrapper.trailingAnchor.constraint(equalTo: item.trailingAnchor).isActive = true
wrapper.topAnchor.constraint(equalTo: item.topAnchor).isActive = true
wrapper.bottomAnchor.constraint(equalTo: item.bottomAnchor).isActive = true
}
}
}
The reason this works, is that the tab bar implements the tab items as an internal subclass of UIControl, which is a UIView.
I can add views underneath the control.
This ends up looking like this:
NB: Turning off user interaction is important, as is inserting at 0.
The drawback is that there's no way to tell which item is the selected one (at the time the background images are created).
ALSO NB: Apple REALLY doesn't want us mucking about in the tab bar, so I could see this method (as well as the ones above), breaking in some future OS upgrade.