/* |
Copyright (C) 2015 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
|
Abstract: |
Demonstrates applying a custom background to a navigation bar. |
*/ |
|
#import "CustomAppearanceViewController.h" |
#import "NavigationController.h" |
|
@interface CustomAppearanceViewController () |
@property (nonatomic, weak) IBOutlet UISegmentedControl *backgroundSwitcher; |
//! An array of city names, populated from Cities.json. |
@property (nonatomic, strong) NSArray *cities; |
@end |
|
|
@implementation CustomAppearanceViewController |
|
//| ---------------------------------------------------------------------------- |
- (void)viewDidLoad |
{ |
[super viewDidLoad]; |
|
// Load some data to populate the table view with |
NSURL *citiesJSONURL = [[NSBundle mainBundle] URLForResource:@"Cities" withExtension:@"json"]; |
NSData *citiesJSONData = [NSData dataWithContentsOfURL:citiesJSONURL]; |
self.cities = [NSJSONSerialization JSONObjectWithData:citiesJSONData options:0 error:NULL]; |
|
// Place the background switcher in the toolbar. |
UIBarButtonItem *backgroundSwitcherItem = [[UIBarButtonItem alloc] initWithCustomView:self.backgroundSwitcher]; |
[self setToolbarItems:@[ |
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:NULL], |
backgroundSwitcherItem, |
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:NULL] |
]]; |
|
[self applyImageBackgroundToTheNavigationBar]; |
} |
|
|
//| ---------------------------------------------------------------------------- |
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator |
{ |
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; |
|
// This works around a bug in iOS 8.0 - 8.2 in which the navigation bar |
// will not display the correct background image after rotating the device. |
// This bug affects bars in navigation controllers that are presented |
// modally. A bar in the window's rootViewController would not be affected. |
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { |
// The workaround is to toggle some appearance related setting on the |
// navigation bar when we detect that the view controller has changed |
// interface orientations. In our example, we call the |
// -configureNewNavBarBackground: which reapplies our appearance |
// based on the current selection. In a real app, changing just the |
// barTintColor or barStyle would have the same effect. |
[self configureNewNavBarBackground:self.backgroundSwitcher]; |
} completion:NULL]; |
} |
|
|
//| ---------------------------------------------------------------------------- |
//! Configures the navigation bar to use an image as its background. |
// |
- (void)applyImageBackgroundToTheNavigationBar |
{ |
// These background images contain a small pattern which is displayed |
// in the lower right corner of the navigation bar. |
UIImage *backgroundImageForDefaultBarMetrics = [UIImage imageNamed:@"NavigationBarDefault"]; |
UIImage *backgroundImageForLandscapePhoneBarMetrics = [UIImage imageNamed:@"NavigationBarLandscapePhone"]; |
|
// Both of the above images are smaller than the navigation bar's |
// size. To enable the images to resize gracefully while keeping their |
// content pinned to the bottom right corner of the bar, the images are |
// converted into resizable images width edge insets extending from the |
// bottom up to the second row of pixels from the top, and from the |
// right over to the second column of pixels from the left. This results |
// in the topmost and leftmost pixels being stretched when the images |
// are resized. Not coincidentally, the pixels in these rows/columns |
// are empty. |
backgroundImageForDefaultBarMetrics = [backgroundImageForDefaultBarMetrics resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, backgroundImageForDefaultBarMetrics.size.height - 1, backgroundImageForDefaultBarMetrics.size.width - 1)]; |
backgroundImageForLandscapePhoneBarMetrics = [backgroundImageForLandscapePhoneBarMetrics resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, backgroundImageForLandscapePhoneBarMetrics.size.height - 1, backgroundImageForLandscapePhoneBarMetrics.size.width - 1)]; |
|
// You should use the appearance proxy to customize the appearance of |
// UIKit elements. However changes made to an element's appearance |
// proxy do not effect any existing instances of that element currently |
// in the view hierarchy. Normally this is not an issue because you |
// will likely be performing your appearance customizations in |
// -application:didFinishLaunchingWithOptions:. However, this example |
// allows you to toggle between appearances at runtime which necessitates |
// applying appearance customizations directly to the navigation bar. |
/* id navigationBarAppearance = [UINavigationBar appearanceWhenContainedIn:[NavigationController class], nil]; */ |
id navigationBarAppearance = self.navigationController.navigationBar; |
|
// The bar metrics associated with a background image determine when it |
// is used. The background image associated with the Default bar metrics |
// is used when a more suitable background image can not be found. |
[navigationBarAppearance setBackgroundImage:backgroundImageForDefaultBarMetrics forBarMetrics:UIBarMetricsDefault]; |
// The background image associated with the LandscapePhone bar metrics |
// is used by the shorter variant of the navigation bar that is used on |
// iPhone when in landscape. |
[navigationBarAppearance setBackgroundImage:backgroundImageForLandscapePhoneBarMetrics forBarMetrics:UIBarMetricsCompact]; |
} |
|
|
//| ---------------------------------------------------------------------------- |
//! Configures the navigation bar to use a transparent background (see-through |
//! but without any blur). |
// |
- (void)applyTransparentBackgroundToTheNavigationBar:(CGFloat)opacity |
{ |
UIImage *transparentBackground; |
|
// The background of a navigation bar switches from being translucent |
// to transparent when a background image is applied. The intensity of |
// the background image's alpha channel is inversely related to the |
// transparency of the bar. That is, a smaller alpha channel intensity |
// results in a more transparent bar and vis-versa. |
// |
// Below, a background image is dynamically generated with the desired |
// opacity. |
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), NO, self.navigationController.navigationBar.layer.contentsScale); |
CGContextRef context = UIGraphicsGetCurrentContext(); |
CGContextSetRGBFillColor(context, 1, 1, 1, opacity); |
UIRectFill(CGRectMake(0, 0, 1, 1)); |
transparentBackground = UIGraphicsGetImageFromCurrentImageContext(); |
UIGraphicsEndImageContext(); |
|
// You should use the appearance proxy to customize the appearance of |
// UIKit elements. However changes made to an element's appearance |
// proxy do not effect any existing instances of that element currently |
// in the view hierarchy. Normally this is not an issue because you |
// will likely be performing your appearance customizations in |
// -application:didFinishLaunchingWithOptions:. However, this example |
// allows you to toggle between appearances at runtime which necessitates |
// applying appearance customizations directly to the navigation bar. |
/* id navigationBarAppearance = [UINavigationBar appearanceWhenContainedIn:[NavigationController class], nil]; */ |
id navigationBarAppearance = self.navigationController.navigationBar; |
|
[navigationBarAppearance setBackgroundImage:transparentBackground forBarMetrics:UIBarMetricsDefault]; |
} |
|
//| ---------------------------------------------------------------------------- |
//! Configures the navigation bar to use a custom color as its background. |
//! The navigation bar remains translucent. |
// |
- (void)applyBarTintColorToTheNavigationBar |
{ |
// Be aware when selecting a barTintColor for a translucent bar that |
// the tint color will be blended with the content passing under |
// the translucent bar. See QA1808 for more information. |
// </RU/iOS/qa/qa1808/_index.html> |
UIColor *barTintColor = [UIColor colorWithRed:176/255.0f green:226/255.0f blue:172/255.0f alpha:1.0f]; |
UIColor *darkendBarTintColor = [UIColor colorWithRed:(176/255.0f - .05f) green:(226/255.0f - .02f) blue:(172/255.0f - .05f) alpha:1.0f]; |
|
// You should use the appearance proxy to customize the appearance of |
// UIKit elements. However changes made to an element's appearance |
// proxy do not effect any existing instances of that element currently |
// in the view hierarchy. Normally this is not an issue because you |
// will likely be performing your appearance customizations in |
// -application:didFinishLaunchingWithOptions:. However, this example |
// allows you to toggle between appearances at runtime which necessitates |
// applying appearance customizations directly to the navigation bar. |
/* id navigationBarAppearance = [UINavigationBar appearanceWhenContainedIn:[NavigationController class], nil]; */ |
id navigationBarAppearance = self.navigationController.navigationBar; |
|
[navigationBarAppearance setBarTintColor:darkendBarTintColor]; |
|
// For comparison, apply the same barTintColor to the toolbar, which |
// has been configured to be opaque. |
[self.navigationController.toolbar setBarTintColor:barTintColor]; |
self.navigationController.toolbar.translucent = NO; |
} |
|
#pragma mark - |
#pragma mark Background Switcher |
|
//| ---------------------------------------------------------------------------- |
- (IBAction)configureNewNavBarBackground:(UISegmentedControl*)sender |
{ |
// Reset everything. |
[self.navigationController.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault]; |
[self.navigationController.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsCompact]; |
[self.navigationController.navigationBar setBarTintColor:nil]; |
[self.navigationController.toolbar setBarTintColor:nil]; |
self.navigationController.toolbar.translucent = YES; |
|
switch (sender.selectedSegmentIndex) { |
case 0: /* Transparent Background */ { |
[self applyImageBackgroundToTheNavigationBar]; |
break; |
} |
case 1: /* Transpaent */ { |
[self applyTransparentBackgroundToTheNavigationBar:0.87]; |
break; |
} |
case 2: /* Color */ { |
[self applyBarTintColorToTheNavigationBar]; |
break; |
} |
default: |
break; |
} |
} |
|
#pragma mark - |
#pragma mark UITableViewDataSource |
|
//| ---------------------------------------------------------------------------- |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section |
{ |
return self.cities.count; |
} |
|
|
//| ---------------------------------------------------------------------------- |
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath |
{ |
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; |
cell.textLabel.text = self.cities[indexPath.row]; |
|
return cell; |
} |
|
#pragma mark - |
#pragma mark UITableViewDelegate |
|
//| ---------------------------------------------------------------------------- |
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath |
{ |
if ([self.navigationItem.prompt isEqualToString:self.cities[indexPath.row]]) { |
self.navigationItem.prompt = nil; |
[tableView deselectRowAtIndexPath:indexPath animated:YES]; |
} else |
self.navigationItem.prompt = self.cities[indexPath.row]; |
} |
|
@end |