How to customize the color of the navigation bar in qlpreviewcontroller
Asked Answered
K

5

11

Can I customize the color of the navigation bar in the QlPreviewController controller?

I have tried following

[[UINavigationBar appearanceWhenContainedIn: [QLPreviewController class], nil] setBarTintColor: [UIColor redColor]];

but it does not work.

Thanks.

Klong answered 15/9, 2017 at 12:31 Comment(1)
Please don't mark code like so: `code`, instead, add four space in front of your code. Also please explain what is 'does not work'. Does your system explodes upon running such code?Foolery
E
12

Yeah, there is a bug with barTintColor on QLPreviewController for iOS 11 if you are showing it via presentViewController: animated:

Here's my solution, use setBackgroundImage: with 1x1 image instead of setBarTintColor:

[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[QLPreviewController class]]] 
    setBackgroundImage:[UIImage imageWithColor:[UIColor redColor]]
 forBarMetrics:UIBarMetricsDefault];

And imageWithColor: is a method in my custom category of UIImage which is returning resizable 1x1 image of the desired color (red color in the example above):

+ (UIImage *)imageWithColor:(UIColor *)color {
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    const CGFloat alpha = CGColorGetAlpha(color.CGColor);
    const BOOL opaque = alpha == 1;
    UIGraphicsBeginImageContextWithOptions(rect.size, opaque, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);

    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return image;
}

I also suggest to wrap this with iOS version check like:

if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"11.0")) {
[[UINavigationBar appearance... 
setBackgroundImage:[UIImage imageWithColor:...]
     forBarMetrics:UIBarMetricsDefault];
    }

Where SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO is from:

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
Erythro answered 2/11, 2017 at 17:48 Comment(2)
Nice suggestion, worked for me by setting the image.Nettienetting
Thanks! I converted it to Swift 4 in a standalone answer.Gametophyte
D
3

If you guys don't want to use the appearance proxy for whatever reason there is another way to tackle this issue if you subclass the QLPreviewController. It turns out that if you present the QLPreviewController modally, it creates a UINavigationController which is then added as a child ViewController to the view hierarchy. This however does not happen on initialization or even before the viewDidLoad is called. It happens when the QLPreviewController is about to be presented. So overriding the viewWillAppear function in the QLPreviewController subclass is the way to go here:

public override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if let nc = self.children.first as? UINavigationController {
        // your customization code goes here
        nc.navigationBar.tintColor = .yellow
    }
}
Desiccate answered 24/6, 2019 at 7:54 Comment(0)
G
2

Thanks to Aleksander Lashevich for his answer! Works like a charm. I converted it to Swift 4 for convenience.

let navbar = UINavigationBar.appearance(whenContainedInInstancesOf: [QLPreviewController.self])
navbar.setBackgroundImage(self.imageWithColor(color: UIColor.red), for: UIBarMetrics.default)

And for the image generation:

func imageWithColor(color: UIColor) -> UIImage {
    let rect = CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)
    let alpha = color.cgColor.alpha
    let opaque = alpha == 1
    UIGraphicsBeginImageContextWithOptions(rect.size, opaque, 0)
    let context = UIGraphicsGetCurrentContext()
    context?.setFillColor(color.cgColor)
    context?.fill(rect)

    return UIGraphicsGetImageFromCurrentImageContext()!
}
Gametophyte answered 25/9, 2018 at 13:44 Comment(0)
C
-1

Please use below code in App delegate

[[UINavigationBar appearance] setBarTintColor:#your color#];

Crisis answered 15/9, 2017 at 13:26 Comment(1)
*in AppDelegate.mCrisis
B
-1

I use this workaround because all others don't work for me.

import QuickLook

class PreviewController: QLPreviewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        navigationController?.navigationBar.setBackgroundImage(nil, for: .any, barMetrics: .default)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        navigationController?.navigationBar.isTranslucent = false //optional
    }
}
Behindhand answered 1/10, 2020 at 11:52 Comment(1)
No effect. navigationController?.navigationBar is nil in this case, both in viewDidLoad and viewWillAppear.Juanajuanita

© 2022 - 2024 — McMap. All rights reserved.