How to customize the color of the navigation bar in qlpreviewcontroller

3.1k views Asked by At

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.

5

There are 5 answers

1
Ashish vani On

Please use below code in App delegate

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

1
ChikabuZ On

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
    }
}
0
Tum On

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()!
}
2
Aleksander Lashevich On

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)
0
Milutin Tomic On

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
    }
}