material-components_materia.../components/AppBar/examples/AppBarWrappingUITableViewControllerExample.m
featherless a5594f3780
[FlexibleHeader] Add support for observing the tracking scroll view. (#4647)
This new opt-in behavior allows a client to more easily integrate the Flexible Header (and relatedly, the App Bar) when they do not require shifting behavior (which is most of the time).

The new behavior can be enabled by setting `observesTrackingScrollViewScrollEvents` to true on the flexible header view instance.

When this property is enabled, the flexible header will no longer allow tracking scroll view events to be manually forwarded, and it will also enforce the shift behavior being set to a disabled state. The intent of this enforcement is to encourage unambiguous usage of the flexible header (you're either allowing it to observe events, or you're forwarding events, but not both).

I've rolled this new behavior out to all of the App Bar examples to demonstrate how much code it removes. I expect this to be a large win for client teams that are integrating with the App Bar.

The underlying implementation of this new behavior relies on KVO of the scroll view's contentOffset property. We manage registration of KVO events through the fhv_startObservingContentOffset and fhv_stopObservingContentOffset APIs.

Prior to iOS 11, the KVO implementation of observers required that you deregister any observers from an object before it is deallocated. As such, if a client wishes to use this behavior and they also support iOS 10.* or below, they must also explicitly nil out the tracking scroll view before it is deallocated. This is required because we keep a weak reference to the tracking scroll view (for a variety of important reasons, namely: avoiding retain cycles and ensuring we don't keep the tracking scroll view alive longer than its owning controller). Because of the weak reference, we can't rely on our own dealloc method being invoked before the tracking scroll view is deallocated. As such, only the client who provided us with a tracking scroll view is capable of making an informed decision as to when to nil out the tracking scroll view. In essence, this behavior comes with a KVO contract (a cost) while providing a much easier integration experience (the benefit).

If a client supports iOS 11 and up, they can simply enable this behavior with no additional code.

Closes https://github.com/material-components/material-components-ios/issues/347
2018-07-26 23:36:42 -04:00

123 lines
4.2 KiB
Objective-C

/*
Copyright 2018-present the Material Components for iOS authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <UIKit/UIKit.h>
#import "MaterialAppBar.h"
#import "MaterialAppBar+ColorThemer.h"
#import "MaterialAppBar+TypographyThemer.h"
@interface AppBarWrappingUITableViewControllerExample : UIViewController <UITableViewDataSource>
@property(nonatomic, strong) MDCAppBarContainerViewController *appBarContainerViewController;
@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme;
@property(nonatomic, strong) MDCTypographyScheme *typographyScheme;
@end
@implementation AppBarWrappingUITableViewControllerExample
- (void)dealloc {
// Required for pre-iOS 11 devices because we've enabled observesTrackingScrollViewScrollEvents.
self.appBarContainerViewController.appBar.headerViewController.headerView.trackingScrollView
= nil;
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_colorScheme = [[MDCSemanticColorScheme alloc] init];
_typographyScheme = [[MDCTypographyScheme alloc] init];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
UITableViewController *tableViewController =
[[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
tableViewController.title = @"Wrapped table view";
self.appBarContainerViewController =
[[MDCAppBarContainerViewController alloc] initWithContentViewController:tableViewController];
self.appBarContainerViewController.topLayoutGuideAdjustmentEnabled = YES;
tableViewController.tableView.dataSource = self;
[tableViewController.tableView registerClass:[UITableViewCell class]
forCellReuseIdentifier:@"cell"];
MDCFlexibleHeaderView *headerView =
self.appBarContainerViewController.appBar.headerViewController.headerView;
// Allows us to avoid forwarding events, but means we can't enable shift behaviors.
headerView.observesTrackingScrollViewScrollEvents = YES;
headerView.trackingScrollView = tableViewController.tableView;
[MDCAppBarColorThemer applySemanticColorScheme:self.colorScheme
toAppBar:self.appBarContainerViewController.appBar];
[MDCAppBarTypographyThemer applyTypographyScheme:self.typographyScheme
toAppBar:self.appBarContainerViewController.appBar];
// Need to update the status bar style after applying the theme.
[self setNeedsStatusBarAppearanceUpdate];
[self addChildViewController:self.appBarContainerViewController];
self.appBarContainerViewController.view.frame = self.view.bounds;
[self.view addSubview:self.appBarContainerViewController.view];
[self.appBarContainerViewController didMoveToParentViewController:self];
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return self.appBarContainerViewController;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 100;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"
forIndexPath:indexPath];
cell.textLabel.text = [@(indexPath.row) description];
return cell;
}
@end
@implementation AppBarWrappingUITableViewControllerExample (CatalogByConvention)
+ (NSArray *)catalogBreadcrumbs {
return @[ @"App Bar", @"Wrapped table view" ];
}
+ (BOOL)catalogIsPrimaryDemo {
return NO;
}
- (BOOL)catalogShouldHideNavigation {
return YES;
}
+ (BOOL)catalogIsPresentable {
return YES;
}
@end