I have already added support for iOS13 where there are two switches: first one for the automatic, system based theme and the other switch to turn on and off dark theme and am switching it with the given method:
func changeThemeTo(theme: String){
switch theme {
case "dark":
UIApplication.shared.windows.forEach { window in
if #available(iOS 13.0, *) {
window.overrideUserInterfaceStyle = .dark
} else {
// Fallback on earlier versions
}
}
case "light":
UIApplication.shared.windows.forEach { window in
if #available(iOS 13.0, *) {
window.overrideUserInterfaceStyle = .light
} else {
// Fallback on earlier versions
}
}
case "auto":
UIApplication.shared.windows.forEach { window in
if #available(iOS 13.0, *) {
window.overrideUserInterfaceStyle = .unspecified
} else {
// Fallback on earlier versions
}
}
default: break
}
}
And everything is working fine for iOS 13, but now I need to enable this to work for iOS lower than 13. Is there something I can do similar to window.overrideUserInterfaceStyle = .dark or should I completely change the switching logic as did on https://www.onswiftwings.com/posts/dark-mode/
I introduced SideMenu in CocoaPods, but an error occurs in the ios13 part of the status bar.But I don't know the correct code.
Would you please teach me?
var statusBarFrame: CGRect {
if #available(iOS 13.0, *) {
// How to do for iOS 13??
} else {
return UIApplication.shared.statusBarFrame
}
}
statusBarFrame has been deprecated in iOS 13,
for iOS 13 you can use this
var statusBarFrame: CGRect {
if #available(iOS 13.0, *) {
return UIApplication.shared.keyWindow!.windowScene!.statusBarManager!.statusBarFrame
} else {
return UIApplication.shared.statusBarFrame
}
}
How can I change the map to dark mode from iOS 13?
I have opt-out from UserInterfaceStyle so system-wide colors will not apply to me, so I'll do it manually.
I've seen this video from apple WWDC2019 - Session 236, at 8:19s but that's for snapshots and I didn't get it.
Actually I was trying something like:
private var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.backgroundColor = .black
}
but it doesn't change the theme or appearance or the traitCollection to dark.
Any suggestion?
This is what you need on the viewDidLoad
if #available(iOS 13.0, *) {
self.overrideUserInterfaceStyle = .dark
}
Setting overrideUserInterfaceStyle on viewDidLoad method will force that view controller to use the desired mode you specify.
if #available(iOS 13.0, *) {
overrideUserInterfaceStyle = .light
}
MapSnapShot
if you are trying to take a snapshot and confused with output image colour Use
if #available(iOS 13.0, *) {
mapSnapshotOptions.traitCollection = traitCollection
}
Full code for a MKMapSnapshotter
func mapCamera(location : CLLocationCoordinate2D )-> MKMapSnapshotter {
let mapSnapshotOptions = MKMapSnapshotter.Options()
let region = MKCoordinateRegion(center: location, latitudinalMeters: 500, longitudinalMeters: 500)
mapSnapshotOptions.region = region
mapSnapshotOptions.scale = UIScreen.main.scale
mapSnapshotOptions.size = AppConstants.Size.mapDetailView
mapSnapshotOptions.showsBuildings = true
mapSnapshotOptions.showsPointsOfInterest = true
if #available(iOS 13.0, *) {
mapSnapshotOptions.traitCollection = traitCollection
}
return MKMapSnapshotter(options: mapSnapshotOptions)
}
In case you got here searching for a macOS solution (like me), simply update the appearance property on MKMapView:
if #available(OSX 10.14, *) {
mapView.appearance = NSAppearance(named: .darkAqua) // .aqua for default mode
}
I am having a horizontal UIScrollview in my xib file. Scrollview looks great in all devices except iPhone X, XS and XR.
It looks good in iPhone 8
iPhone X
I have tried all the possible solutions, Unchecked the under top bar, under bottom bar, auto resize subviews,nothing works for me. iPhone X always looks the same. Top constraint of the scrollview is set to safe area. I am using xib file here.
if #available(iOS 11.0, *) {
scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.never
} else {
self.automaticallyAdjustsScrollViewInsets = false
edgesForExtendedLayout = []
// Fallback on earlier versions
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.scrollView.contentSize = CGSize(width: self.lectureTable.frame.size.width , height: self.scrollView.frame.size.height)
self.automaticallyAdjustsScrollViewInsets = false
}
Please shed some light. Thanks a ton.
I have solved it by myself. Inside my scroll view, I have used UIView.
var hasTopNotch: Bool {
if #available(iOS 11.0, *) {
return UIApplication.shared.delegate?.window??.safeAreaInsets.top ?? 0 > 20
}
return false
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
var topbarHeight: CGFloat {
return UIApplication.shared.statusBarFrame.size.height + (self.navigationController?.navigationBar.frame.height ?? 0.0)
}
if hasTopNotch{
let window = UIApplication.shared.keyWindow
if #available(iOS 11.0, *) {
let topPadding = window?.safeAreaInsets.top
self.customView.frame.size.height = UIScreen.main.bounds.height - (topbarHeight + topPadding!)
} else {
// Fallback on earlier versions
}
}else{
let NavBarHeight = self.navigationController!.navigationBar.frame.height + UIApplication.shared.statusBarFrame.height
self.customView.frame.size.height = UIScreen.main.bounds.height - NavBarHeight
}
}
This works great in all the devices.
I want to change background color of UITabBarItem badge but can't find any resource on how to make it.
UITabBarItem has this available since iOS 10.
var badgeColor: UIColor? { get set }
It's also available via appearence.
if #available(iOS 10, *) {
UITabBarItem.appearance().badgeColor = .green
}
reference docs:
https://developer.apple.com/reference/uikit/uitabbaritem/1648567-badgecolor
Changing the badge-color is now natively supported in iOS 10 and later using the badgeColor property inside your UITabBarItem. See the apple docs for more infos on the property.
Example:
Swift 3: myTab.badgeColor = UIColor.blue
Objective-C: [myTab setBadgeColor:[UIColor blueColor]];
I wrote this piece of code for my app, but I have only tested it in iOS 7.
for (UIView* tabBarButton in self.tabBar.subviews) {
for (UIView* badgeView in tabBarButton.subviews) {
NSString* className = NSStringFromClass([badgeView class]);
// looking for _UIBadgeView
if ([className rangeOfString:#"BadgeView"].location != NSNotFound) {
for (UIView* badgeSubview in badgeView.subviews) {
NSString* className = NSStringFromClass([badgeSubview class]);
// looking for _UIBadgeBackground
if ([className rangeOfString:#"BadgeBackground"].location != NSNotFound) {
#try {
[badgeSubview setValue:[UIImage imageNamed:#"YourCustomImage.png"] forKey:#"image"];
}
#catch (NSException *exception) {}
}
if ([badgeSubview isKindOfClass:[UILabel class]]) {
((UILabel *)badgeSubview).textColor = [UIColor greenColor];
}
}
}
}
}
You're only able to update the badge background with an image, not a color. I have also exposed the badge label if you wanted to update that in some way.
Its important to note that this code must be called after setting the tabBarItem.badgeValue!
EDIT: 4/14/14
The above code will work in iOS 7 when called anywhere. To get it working in iOS 7.1 call it in the view controllers -viewWillLayoutSubviews.
EDIT: 12/22/14
Here's an updated snippet which I'm currently using. I put the code in a category extension for simplicity.
- (void)badgeViews:(void (^)(UIView* badgeView, UILabel* badgeLabel, UIView* badgeBackground))block {
if (block) {
for (UIView* tabBarButton in self.subviews) {
for (UIView* badgeView in tabBarButton.subviews) {
NSString* className = NSStringFromClass([badgeView class]);
if ([className rangeOfString:#"BadgeView"].location != NSNotFound) {
UILabel* badgeLabel;
UIView* badgeBackground;
for (UIView* badgeSubview in badgeView.subviews) {
NSString* className = NSStringFromClass([badgeSubview class]);
if ([badgeSubview isKindOfClass:[UILabel class]]) {
badgeLabel = (UILabel *)badgeSubview;
} else if ([className rangeOfString:#"BadgeBackground"].location != NSNotFound) {
badgeBackground = badgeSubview;
}
}
block(badgeView, badgeLabel, badgeBackground);
}
}
}
}
}
Then when you're ready to call it, it'll look like this.
[self.tabBar badgeViews:^(UIView *badgeView, UILabel *badgeLabel, UIView *badgeBackground) {
}];
EDIT: 11/16/15
It's been brought to my attention that some people need a little more clarity on what's happening in this code. The for loops are searching for a few views which are not publicly accessible. By checking if the views class name contains a part of the expected name, it's ensuring to reach the intended view while not setting off any possible red flags by Apple. Once everything has been located, a block is executed with easy access to these views.
It's noteworthy that the possibility exists for this code to stop working in a future iOS update. For example these internal views could one day acquire different class names. However the chances of that are next to none since even internally Apple rarely refactors classes to this nature. But even if they were to, it would be something along the title of UITabBarBadgeView, which would still reach the expected point in code. Being that iOS9 is well out the door and this code is still working as intended, you can expect this problem to never arise.
I have the same problem and solved it by creating a little category that replace the BadgeView with an UILabel that you can customize easily.
https://github.com/enryold/UITabBarItem-CustomBadge/
For people using Swift, I managed to improve on TimWhiting answer in order to have the badge view working on any screen size and any orientation.
extension UITabBarController {
func setBadges(badgeValues: [Int]) {
for view in self.tabBar.subviews {
if view is CustomTabBadge {
view.removeFromSuperview()
}
}
for index in 0...badgeValues.count-1 {
if badgeValues[index] != 0 {
addBadge(index, value: badgeValues[index], color:UIColor(paletteItem: .Accent), font: UIFont(name: Constants.ThemeApp.regularFontName, size: 11)!)
}
}
}
func addBadge(index: Int, value: Int, color: UIColor, font: UIFont) {
let badgeView = CustomTabBadge()
badgeView.clipsToBounds = true
badgeView.textColor = UIColor.whiteColor()
badgeView.textAlignment = .Center
badgeView.font = font
badgeView.text = String(value)
badgeView.backgroundColor = color
badgeView.tag = index
tabBar.addSubview(badgeView)
self.positionBadges()
}
override public func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.positionBadges()
}
// Positioning
func positionBadges() {
var tabbarButtons = self.tabBar.subviews.filter { (view: UIView) -> Bool in
return view.userInteractionEnabled // only UITabBarButton are userInteractionEnabled
}
tabbarButtons = tabbarButtons.sort({ $0.frame.origin.x < $1.frame.origin.x })
for view in self.tabBar.subviews {
if view is CustomTabBadge {
let badgeView = view as! CustomTabBadge
self.positionBadge(badgeView, items:tabbarButtons, index: badgeView.tag)
}
}
}
func positionBadge(badgeView: UIView, items: [UIView], index: Int) {
let itemView = items[index]
let center = itemView.center
let xOffset: CGFloat = 12
let yOffset: CGFloat = -14
badgeView.frame.size = CGSizeMake(17, 17)
badgeView.center = CGPointMake(center.x + xOffset, center.y + yOffset)
badgeView.layer.cornerRadius = badgeView.bounds.width/2
tabBar.bringSubviewToFront(badgeView)
}
}
class CustomTabBadge: UILabel {}
No you can't change the color but you can use your own badges instead. Add this extension at the file scope and you can customise the badges however you like. Just call self.tabBarController!.setBadges([1,0,2]) in any of your root view controllers.
To be clear that is for a tab bar with three items, with the badge values going from left to right.
extension UITabBarController {
func setBadges(badgeValues:[Int]){
var labelExistsForIndex = [Bool]()
for value in badgeValues {
labelExistsForIndex.append(false)
}
for view in self.tabBar.subviews {
if view.isKindOfClass(PGTabBadge) {
let badgeView = view as! PGTabBadge
let index = badgeView.tag
if badgeValues[index]==0 {
badgeView.removeFromSuperview()
}
labelExistsForIndex[index]=true
badgeView.text = String(badgeValues[index])
}
}
for var i=0;i<labelExistsForIndex.count;i++ {
if labelExistsForIndex[i] == false {
if badgeValues[i] > 0 {
addBadge(i, value: badgeValues[i], color:UIColor(red: 4/255, green: 110/255, blue: 188/255, alpha: 1), font: UIFont(name: "Helvetica-Light", size: 11)!)
}
}
}
}
func addBadge(index:Int,value:Int, color:UIColor, font:UIFont){
let itemPosition = CGFloat(index+1)
let itemWidth:CGFloat = tabBar.frame.width / CGFloat(tabBar.items!.count)
let bgColor = color
let xOffset:CGFloat = 12
let yOffset:CGFloat = -9
var badgeView = PGTabBadge()
badgeView.frame.size=CGSizeMake(17, 17)
badgeView.center=CGPointMake((itemWidth * itemPosition)-(itemWidth/2)+xOffset, 20+yOffset)
badgeView.layer.cornerRadius=badgeView.bounds.width/2
badgeView.clipsToBounds=true
badgeView.textColor=UIColor.whiteColor()
badgeView.textAlignment = .Center
badgeView.font = font
badgeView.text = String(value)
badgeView.backgroundColor = bgColor
badgeView.tag=index
tabBar.addSubview(badgeView)
}
}
class PGTabBadge: UILabel {
}
Swift 3 Here is an updated version of #Kirualex's answer (who improved on #TimWhiting's answer) for Swift 3.
extension UITabBarController {
func setBadges(badgeValues: [Int]) {
for view in self.tabBar.subviews {
if view is CustomTabBadge {
view.removeFromSuperview()
}
}
for index in 0...badgeValues.count-1 {
if badgeValues[index] != 0 {
addBadge(index: index, value: badgeValues[index], color: UIColor.blue, font: UIFont(name: "Helvetica-Light", size: 11)!)
}
}
}
func addBadge(index: Int, value: Int, color: UIColor, font: UIFont) {
let badgeView = CustomTabBadge()
badgeView.clipsToBounds = true
badgeView.textColor = UIColor.white
badgeView.textAlignment = .center
badgeView.font = font
badgeView.text = String(value)
badgeView.backgroundColor = color
badgeView.tag = index
tabBar.addSubview(badgeView)
self.positionBadges()
}
override open func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.positionBadges()
}
// Positioning
func positionBadges() {
var tabbarButtons = self.tabBar.subviews.filter { (view: UIView) -> Bool in
return view.isUserInteractionEnabled // only UITabBarButton are userInteractionEnabled
}
tabbarButtons = tabbarButtons.sorted(by: { $0.frame.origin.x < $1.frame.origin.x })
for view in self.tabBar.subviews {
if view is CustomTabBadge {
let badgeView = view as! CustomTabBadge
self.positionBadge(badgeView: badgeView, items:tabbarButtons, index: badgeView.tag)
}
}
}
func positionBadge(badgeView: UIView, items: [UIView], index: Int) {
let itemView = items[index]
let center = itemView.center
let xOffset: CGFloat = 12
let yOffset: CGFloat = -14
badgeView.frame.size = CGSize(width: 17, height: 17)
badgeView.center = CGPoint(x: center.x + xOffset, y: center.y + yOffset)
badgeView.layer.cornerRadius = badgeView.bounds.width/2
tabBar.bringSubview(toFront: badgeView)
}
}
class CustomTabBadge: UILabel {}
It appears that no. You may only set the value.
From Apple's documentation badge is:
Text that is displayed in the upper-right corner of the item with a
surrounding red oval.
You need to specify tab item at index to change badge color, #available in iOS 10 ,
if #available(iOS 10.0, *)
{
self.kAppTabBarController.tabBar.items![1].badgeColor = YOUR_COLOR
}
You can now do it in the storyboard too, by selecting your tab bar item and going to the attributes inspector.
Since iOS 15 has different approach, what worked in my case:
let appearance = UITabBarAppearance()
appearance.configureWithTransparentBackground()
let barAppearance = UITabBarItemAppearance()
barAppearance.normal.badgeBackgroundColor = .green
barAppearance.normal.badgeTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.red]
appearance.stackedLayoutAppearance = barAppearance
tabBar.standardAppearance = appearance
YES, But the only possible solution is to create a custom Tabbar and creating your custom tabbar badge icon. You will find many article/code for creating custom tabbar.
// change TabBar BadgeView background Color
-(void)changeTabBarBadgeViewBgColor:(UITabBar*)tabBar {
for (UIView* tabBarButton in tabBar.subviews) {
for (UIView* badgeView in tabBarButton.subviews) {
NSString* className = NSStringFromClass([badgeView class]);
// looking for _UIBadgeView
if ([className rangeOfString:#"BadgeView"].location != NSNotFound) {
for (UIView* badgeSubview in badgeView.subviews) {
NSString* className = NSStringFromClass([badgeSubview class]);
// looking for _UIBadgeBackground
if ([className rangeOfString:#"BadgeBackground"].location != NSNotFound) {
#try {
[badgeSubview setValue:nil forKey:#"image"];
[badgeSubview setBackgroundColor:[UIColor blueColor]];
badgeSubview.clipsToBounds = YES;
badgeSubview.layer.cornerRadius = badgeSubview.frame.size.height/2;
}
#catch (NSException *exception) {}
}
if ([badgeSubview isKindOfClass:[UILabel class]]) {
((UILabel *)badgeSubview).textColor = [UIColor greenColor];
}
}
}
}
}
}
Hm...it's very easy.
[[self tabBarItem] setBadgeColor:[UIColor greenColor]];
Add below lines of code in UITabBarController :
class RootTabBarViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
if #available(iOS 13.0, *) {
let appearance = tabBar.standardAppearance.copy()
setTabBarItemBadgeAppearance(appearance.stackedLayoutAppearance)
setTabBarItemBadgeAppearance(appearance.inlineLayoutAppearance)
setTabBarItemBadgeAppearance(appearance.compactInlineLayoutAppearance)
tabBar.standardAppearance = appearance
if #available(iOS 15.0, *) {
tabBar.scrollEdgeAppearance = appearance
}
}
// Do any additional setup after loading the view.
}
#available(iOS 13.0, *)
private func setTabBarItemBadgeAppearance(_ itemAppearance: UITabBarItemAppearance) {
itemAppearance.normal.badgeBackgroundColor = UIColor.colorBlue207DFF
}
}
Since iOS 15 / Xcode 13, you have to set stackedLayoutAppearance property to change badge color on UITabBarItem. Change just ".blue" with you own color:
if #available(iOS 15.0, *) {
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.stackedLayoutAppearance.normal.badgeBackgroundColor = .blue
UITabBar.appearance().standardAppearance = appearance
UITabBar.appearance().scrollEdgeAppearance = appearance
}
Tested on Xcode 14.1 / iOS 16.
Take a look here # UITabbarItem-CustomBadge.
A complete demonstration is following
it takes only two line of code, if you want to use the default implementation
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//supplying the animation parameter
[UITabBarItem setDefaultAnimationProvider:[[DefaultTabbarBadgeAnimation alloc] init]];
[UITabBarItem setDefaultConfigurationProvider:[[DefaultSystemLikeBadgeConfiguration alloc] init]];
//rest of your code goes following...
return YES;
}