You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
// // FLEXScopeCarousel.m // FLEX // // Created by Tanner Bennett on 7/17/19. // Copyright © 2019 Flipboard. All rights reserved. //
#import "FLEXScopeCarousel.h" #import "FLEXCarouselCell.h" #import "FLEXColor.h" #import "UIView+FLEX_Layout.h"
const CGFloat kCarouselItemSpacing = 0; NSString * const kCarouselCellReuseIdentifier = @"kCarouselCellReuseIdentifier";
@interface FLEXScopeCarousel () <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> @property (nonatomic, readonly) UICollectionView *collectionView; @property (nonatomic, readonly) FLEXCarouselCell *sizingCell; @property (nonatomic, readonly) NSLayoutConstraint *heightConstraint;
@property (nonatomic, readonly) id dynamicTypeObserver; @property (nonatomic, readonly) NSMutableArray *dynamicTypeHandlers;
@property (nonatomic) BOOL constraintsInstalled; @end
@implementation FLEXScopeCarousel
- (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [FLEXColor primaryBackgroundColor]; self.autoresizingMask = UIViewAutoresizingFlexibleWidth; _dynamicTypeHandlers = [NSMutableArray new]; CGSize itemSize = CGSizeZero; if (@available(iOS 10.0, *)) { itemSize = UICollectionViewFlowLayoutAutomaticSize; }
// Collection view layout UICollectionViewFlowLayout *layout = ({ UICollectionViewFlowLayout *layout = [UICollectionViewFlowLayout new]; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; layout.sectionInset = UIEdgeInsetsZero; layout.minimumLineSpacing = kCarouselItemSpacing; layout.itemSize = itemSize; layout.estimatedItemSize = itemSize; layout; });
// Collection view _collectionView = ({ UICollectionView *cv = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout ]; cv.showsHorizontalScrollIndicator = NO; cv.backgroundColor = UIColor.clearColor; cv.delegate = self; cv.dataSource = self; [cv registerClass:[FLEXCarouselCell class] forCellWithReuseIdentifier:kCarouselCellReuseIdentifier];
[self addSubview:cv]; cv; });
// Sizing cell _sizingCell = [FLEXCarouselCell new]; self.sizingCell.title = @"NSObject";
// Dynamic type __weak __typeof(self) weakSelf = self; _dynamicTypeObserver = [NSNotificationCenter.defaultCenter addObserverForName:UIContentSizeCategoryDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) { [self.collectionView setNeedsLayout]; [self setNeedsUpdateConstraints];
// Notify observers __typeof(self) self = weakSelf; for (void (^block)(FLEXScopeCarousel *) in self.dynamicTypeHandlers) { block(self); } } ]; }
return self; }
- (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self.dynamicTypeObserver]; }
#pragma mark - Overrides
- (void)drawRect:(CGRect)rect { [super drawRect:rect];
CGFloat width = 1.f / UIScreen.mainScreen.scale;
// Draw hairline CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetStrokeColorWithColor(context, [FLEXColor hairlineColor].CGColor); CGContextSetLineWidth(context, width); CGContextMoveToPoint(context, 0, rect.size.height - width); CGContextAddLineToPoint(context, rect.size.width, rect.size.height - width); CGContextStrokePath(context); }
+ (BOOL)requiresConstraintBasedLayout { return YES; }
- (void)updateConstraints { if (!self.constraintsInstalled) { self.translatesAutoresizingMaskIntoConstraints = NO; self.collectionView.translatesAutoresizingMaskIntoConstraints = NO;
[self.centerXAnchor constraintEqualToAnchor:self.superview.centerXAnchor].active = YES; [self.widthAnchor constraintEqualToAnchor:self.superview.widthAnchor].active = YES; [self.topAnchor constraintEqualToAnchor:self.superview.topAnchor].active = YES;
[self.collectionView pinEdgesToSuperview]; _heightConstraint = [self.heightAnchor constraintEqualToConstant:100]; self.heightConstraint.active = YES;
self.constraintsInstalled = YES; }
self.heightConstraint.constant = [self.sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; [super updateConstraints]; }
#pragma mark - Public
- (void)setItems:(NSArray<NSString *> *)items { NSParameterAssert(items.count);
_items = items.copy;
// Refresh list, select first item initially [self.collectionView reloadData]; self.selectedIndex = 0; }
- (void)setSelectedIndex:(NSInteger)idx { NSParameterAssert(idx < self.items.count);
_selectedIndex = idx; NSIndexPath *path = [NSIndexPath indexPathForItem:idx inSection:0]; [self.collectionView selectItemAtIndexPath:path animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; [self collectionView:self.collectionView didSelectItemAtIndexPath:path]; }
- (void)registerBlockForDynamicTypeChanges:(void (^)(FLEXScopeCarousel *))handler { [self.dynamicTypeHandlers addObject:handler]; }
#pragma mark - UICollectionView
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { // if (@available(iOS 10.0, *)) { // return UICollectionViewFlowLayoutAutomaticSize; // } self.sizingCell.title = self.items[indexPath.item]; return [self.sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; }
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.items.count; }
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { FLEXCarouselCell *cell = (id)[collectionView dequeueReusableCellWithReuseIdentifier:kCarouselCellReuseIdentifier forIndexPath:indexPath]; cell.title = self.items[indexPath.row]; return cell; }
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { _selectedIndex = indexPath.item; // In case self.selectedIndex didn't trigger this call
if (self.selectedIndexChangedAction) { self.selectedIndexChangedAction(indexPath.row); }
// TODO: dynamically choose a scroll position. Very wide items should // get "Left" while smaller items should not scroll at all, unless // they are only partially on the screen, in which case they // should get "HorizontallyCentered" to bring them onto the screen. // For now, everything goes to the left, as this has a similar effect. [collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:YES]; [self sendActionsForControlEvents:UIControlEventValueChanged]; }
@end
|