├── .gitignore ├── .travis.yml ├── Classes ├── YLRefreshHeaderView │ ├── YLRefreshHeaderView.h │ ├── YLRefreshHeaderView.m │ ├── YLRefreshHeaderViewPrivate.h │ └── YLRefreshHeaderViewSubclass.h ├── YLTableView.h ├── YLTableView.m ├── YLTableViewCell.h ├── YLTableViewChildViewControllerCell.h ├── YLTableViewDataSource.h ├── YLTableViewDataSource.m ├── YLTableViewDataSourceSubclass.h ├── YLTableViewFramework.h ├── YLTableViewHeaderFooterView.h ├── YLTableViewHeaderFooterView.m ├── YLTableViewPrivate.h └── YLTableViewSectionHeaderFooterView │ ├── YLTableViewSectionHeaderFooterLabelView.h │ ├── YLTableViewSectionHeaderFooterLabelView.m │ ├── YLTableViewSectionHeaderFooterView.h │ ├── YLTableViewSectionHeaderFooterView.m │ ├── YLTableViewSectionHeaderFooterViewPrivate.h │ └── YLTableViewSectionHeaderFooterViewSubclass.h ├── LICENSE.txt ├── README.md ├── Resources └── Info.plist ├── YLTableView.podspec ├── YLTableView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── YLTableView.xcscheme ├── YLTableView.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── YLTableViewExample ├── Classes │ ├── ImagesView │ │ ├── YLExampleImageControl.h │ │ ├── YLExampleImageControl.m │ │ ├── YLExampleImagesCell.h │ │ ├── YLExampleImagesCell.m │ │ ├── YLExampleImagesCellModel.h │ │ ├── YLExampleImagesCellModel.m │ │ ├── YLExampleImagesView.h │ │ ├── YLExampleImagesView.m │ │ ├── YLExampleImagesViewController.h │ │ ├── YLExampleImagesViewController.m │ │ ├── YLExampleLargeImageViewController.h │ │ └── YLExampleLargeImageViewController.m │ ├── LaunchScreen.xib │ ├── YLExampleAppDelegate.h │ ├── YLExampleAppDelegate.m │ ├── YLExampleRefreshHeader.h │ ├── YLExampleRefreshHeader.m │ ├── YLExampleTableViewDataSource.h │ ├── YLExampleTableViewDataSource.m │ ├── YLExampleTextCell.h │ ├── YLExampleTextCell.m │ ├── YLExampleViewController.h │ ├── YLExampleViewController.m │ └── main.m ├── Gemfile ├── Gemfile.lock ├── OtherSources │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── LaunchImage.launchimage │ │ │ ├── Contents.json │ │ │ ├── Default-568h@2x.png │ │ │ └── Default@2x.png │ │ ├── ninja_hammy.imageset │ │ │ ├── Contents.json │ │ │ ├── ninja_hammy.png │ │ │ └── ninja_hammy@2x.png │ │ ├── star.imageset │ │ │ ├── Contents.json │ │ │ ├── star.png │ │ │ └── star@2x.png │ │ └── yelp_logo.imageset │ │ │ ├── Contents.json │ │ │ ├── yelp_logo.png │ │ │ └── yelp_logo@2x.png │ └── Info.plist ├── Podfile ├── Podfile.lock ├── YLTableViewExample.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── YLTableViewExample.xcscheme └── YLTableViewExample.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── YLTableViewTests ├── Info.plist ├── YLTableViewCellTestStub.h ├── YLTableViewCellTestStub.m ├── YLTableViewDataSourceTestStub.h ├── YLTableViewDataSourceTestStub.m └── YLTableViewDataSourceTests.m ├── module.modulemap └── readme_images └── photo_swipe_cell.png /.gitignore: -------------------------------------------------------------------------------- 1 | *xcuserdata* 2 | *Pods/ 3 | *.DS_Store 4 | project.xcworkspace 5 | .bundle 6 | vendor 7 | Carthage -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9 3 | install: gem install xcpretty 4 | script: 5 | - xcodebuild clean build test -project "YLTableView.xcodeproj" -scheme "YLTableView" -destination "platform=iOS Simulator,name=iPhone 8" | xcpretty 6 | -------------------------------------------------------------------------------- /Classes/YLRefreshHeaderView/YLRefreshHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLRefreshHeaderView.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 7/31/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Enum to represent the current state of the YLRefreshHeaderView 13 | */ 14 | typedef NS_ENUM(NSInteger, YLRefreshHeaderViewState) { 15 | /** 16 | No refresh is currently happening. The user might have pulled the header down a bit, but not enough to trigger a refresh. 17 | */ 18 | YLRefreshHeaderViewStateNormal = 0, 19 | /** 20 | The user has pulled down the header far enough to trigger a refresh, but has not released yet. 21 | */ 22 | YLRefreshHeaderViewStateReadyToRefresh, 23 | /** 24 | Refreshing, either after the user pulled to refresh or a refresh was started programmatically. 25 | */ 26 | YLRefreshHeaderViewStateRefreshing, 27 | /** 28 | The refresh has just finished and the refresh header is in the process of closing. 29 | */ 30 | YLRefreshHeaderViewStateClosing, 31 | }; 32 | 33 | /** 34 | Abstract class used for refresh header views. See YLRefreshHeaderViewSubclass for the abstract methods you'll need to implement. YLTableView will call those methods automatically. Add a target for the UIControlEventValueChanged event to refresh the table view. 35 | */ 36 | @interface YLRefreshHeaderView : UIControl 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Classes/YLRefreshHeaderView/YLRefreshHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLRefreshHeaderView.m 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 7/31/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLRefreshHeaderView.h" 10 | #import "YLRefreshHeaderViewPrivate.h" 11 | #import "YLRefreshHeaderViewSubclass.h" 12 | 13 | @implementation YLRefreshHeaderView 14 | 15 | - (instancetype)initWithFrame:(CGRect)frame { 16 | if ((self = [super initWithFrame:frame])) { 17 | self.autoresizingMask = UIViewAutoresizingFlexibleWidth; 18 | self.clipsToBounds = YES; 19 | self.refreshState = YLRefreshHeaderViewStateNormal; 20 | } 21 | return self; 22 | } 23 | 24 | - (CGSize)sizeThatFits:(CGSize)size { 25 | static const CGFloat bottomPadding = 2; 26 | return CGSizeMake(size.width, self.pullAmountToRefresh + bottomPadding); 27 | } 28 | 29 | - (void)setRefreshState:(YLRefreshHeaderViewState)refreshState { 30 | [self setRefreshState:refreshState animated:YES]; 31 | } 32 | 33 | - (void)setRefreshState:(YLRefreshHeaderViewState)refreshState animated:(BOOL)animated { 34 | _refreshState = refreshState; 35 | 36 | void (^animationBlock)(void) = ^{ 37 | UIScrollView *strongScrollView = self.scrollView; 38 | UIEdgeInsets contentInset = strongScrollView.contentInset; 39 | // TODO(mglidden): make this support view controllers that have UIRectEdgeTop 40 | contentInset.top = (refreshState == YLRefreshHeaderViewStateRefreshing) ? self.pullAmountToRefresh : 0; 41 | strongScrollView.contentInset = contentInset; 42 | }; 43 | void (^completionBlock)(BOOL) = ^(BOOL finished) { 44 | if (self.refreshState == YLRefreshHeaderViewStateClosing) { 45 | self.refreshState = YLRefreshHeaderViewStateNormal; 46 | } 47 | }; 48 | 49 | if (animated) { 50 | [UIView animateWithDuration:0.2 animations:animationBlock completion:completionBlock]; 51 | } else { 52 | animationBlock(); 53 | completionBlock(YES); 54 | } 55 | } 56 | 57 | - (CGFloat)pullAmountToRefresh { 58 | NSAssert(NO, @"Abstract method – subclasses must implement this."); 59 | return 0; 60 | } 61 | 62 | #pragma mark UIScrollViewDelegate 63 | 64 | - (void)containingScrollViewDidScroll:(UIScrollView *)scrollView { 65 | if (scrollView.isDragging) { 66 | // If we were ready to refresh but then dragged back up, cancel the Ready to Refresh state. 67 | if (self.refreshState == YLRefreshHeaderViewStateReadyToRefresh && scrollView.contentOffset.y > -self.pullAmountToRefresh && scrollView.contentOffset.y < 0) { 68 | self.refreshState = YLRefreshHeaderViewStateNormal; 69 | // If we've dragged far enough, put us in the Ready to Refresh state 70 | } else if (self.refreshState == YLRefreshHeaderViewStateNormal && scrollView.contentOffset.y <= -self.pullAmountToRefresh) { 71 | self.refreshState = YLRefreshHeaderViewStateReadyToRefresh; 72 | } 73 | } 74 | self.currentPullAmount = MAX(0, -scrollView.contentOffset.y); 75 | } 76 | 77 | - (void)containingScrollViewDidEndDragging:(UIScrollView *)scrollView { 78 | // Trigger the action if it was pulled far enough. 79 | if (scrollView.contentOffset.y <= -self.pullAmountToRefresh && self.refreshState != YLRefreshHeaderViewStateRefreshing) { 80 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 81 | } else { 82 | self.currentPullAmount = 0; 83 | } 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /Classes/YLRefreshHeaderView/YLRefreshHeaderViewPrivate.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLRefreshHeaderViewPrivate.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 8/19/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | // NOTE: This header is not actually private. See PR#41: https://github.com/Yelp/YLTableView/pull/41 10 | 11 | #import "YLRefreshHeaderView.h" 12 | #import "YLRefreshHeaderViewSubclass.h" 13 | 14 | /** 15 | Methods used by YLTableViewDataSource and YLTableView to make the refresh header work. 16 | */ 17 | NS_ASSUME_NONNULL_BEGIN 18 | @interface YLRefreshHeaderView () 19 | 20 | /** 21 | The scroll view that this refresh header is at the top of. 22 | */ 23 | @property (weak, nonatomic) UIScrollView *scrollView; 24 | 25 | /** 26 | Called from scrollViewDidScroll: to update the refresh header 27 | */ 28 | - (void)containingScrollViewDidScroll:(UIScrollView *)scrollView; 29 | 30 | /** 31 | Called from scrollViewDidEndDragging: to potentially start the refresh 32 | */ 33 | - (void)containingScrollViewDidEndDragging:(UIScrollView *)scrollView; 34 | 35 | @end 36 | NS_ASSUME_NONNULL_END 37 | -------------------------------------------------------------------------------- /Classes/YLRefreshHeaderView/YLRefreshHeaderViewSubclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLRefreshHeaderViewSubclass.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 8/20/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLRefreshHeaderView.h" 10 | 11 | @interface YLRefreshHeaderView () 12 | 13 | /** 14 | The current state of the refresh header. The header should update its view as needed for the different states. If you override this method, you must call super. 15 | */ 16 | @property (assign, nonatomic) YLRefreshHeaderViewState refreshState; 17 | 18 | /** 19 | Start / stop the refreshing animation in the given scroll view. 20 | */ 21 | - (void)setRefreshState:(YLRefreshHeaderViewState)refreshState animated:(BOOL)animated NS_REQUIRES_SUPER; 22 | 23 | /** 24 | The current distance that the refresh header is being pulled. Will be updated as the user pulls the page down. Animations should be updated as this number changes. 25 | */ 26 | @property (assign, nonatomic) CGFloat currentPullAmount; 27 | 28 | /** 29 | The distance the user will have to pull the header to trigger a refresh. Abstract method. 30 | */ 31 | @property (readonly, assign, nonatomic) CGFloat pullAmountToRefresh; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Classes/YLTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableView.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 6/10/14. 6 | // Copyright (c) 2014 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class YLRefreshHeaderView; 12 | 13 | /** 14 | Enum to represent the current data loading state of the YLTableView 15 | */ 16 | typedef NS_ENUM(NSInteger, YLTableViewState) { 17 | /** 18 | Initial loading state. Pull to refresh header will not show. 19 | */ 20 | YLTableViewStateLoading = 0, 21 | /** 22 | Normal state. Nothing is currently loading. 23 | */ 24 | YLTableViewStateLoaded, 25 | /** 26 | Refreshing after a pull-to-refresh. The refreshHeaderView will be showing. 27 | */ 28 | YLTableViewStateRefreshing, 29 | /** 30 | Error state, eg network request errored. 31 | */ 32 | YLTableViewStateErrored, 33 | }; 34 | 35 | /** 36 | UITableView subclass that encapsulates some boilerplate code for working with a UITableView full of self-sizing cells 37 | */ 38 | @interface YLTableView : UITableView 39 | 40 | /** 41 | If you want to use a refresh header, set this to a subclass of YLRefreshHeaderView 42 | */ 43 | @property (strong, nonatomic, nullable) YLRefreshHeaderView *refreshHeaderView; 44 | 45 | /** 46 | The current state of the table view. Will update the state of the refresh header as needed. 47 | */ 48 | @property (assign, nonatomic) YLTableViewState state; 49 | 50 | @end -------------------------------------------------------------------------------- /Classes/YLTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableView.m 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 6/10/14. 6 | // Copyright (c) 2014 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableView.h" 10 | #import "YLTableViewPrivate.h" 11 | 12 | #import "YLRefreshHeaderView.h" 13 | #import "YLRefreshHeaderViewPrivate.h" 14 | #import "YLTableViewCell.h" 15 | #import "YLTableViewDataSource.h" 16 | #import "YLTableViewSectionHeaderFooterView.h" 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | @interface YLTableView () 20 | 21 | //! Maps reuse identifiers to cell classes 22 | @property (strong, nonatomic) NSMutableDictionary *cellClassForReuseIdentifier; 23 | 24 | //! Maps reuse identifiers to header/footer view classes 25 | @property (strong, nonatomic) NSMutableDictionary *headerFooterViewClassForReuseIdentifier; 26 | //! Maps reuse identifiers to sizing header/footer views 27 | @property (strong, nonatomic) NSMutableDictionary *sizingHeaderFooterViewsForReuseIdentifier; 28 | 29 | @end 30 | NS_ASSUME_NONNULL_END 31 | 32 | @implementation YLTableView 33 | 34 | - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { 35 | if ((self = [super initWithFrame:frame style:style])) { 36 | _cellClassForReuseIdentifier = [NSMutableDictionary dictionary]; 37 | 38 | _headerFooterViewClassForReuseIdentifier = [NSMutableDictionary dictionary]; 39 | _sizingHeaderFooterViewsForReuseIdentifier = [NSMutableDictionary dictionary]; 40 | 41 | self.estimatedSectionHeaderHeight = 0; 42 | self.estimatedSectionFooterHeight = 0; 43 | } 44 | return self; 45 | } 46 | 47 | #pragma mark Sizing Views 48 | 49 | - (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier { 50 | NSAssert(identifier, @"Must have a reuse identifier."); 51 | NSAssert([cellClass conformsToProtocol:@protocol(YLTableViewCell)], @"You can only use cells conforming to YLTableViewCell."); 52 | 53 | [super registerClass:cellClass forCellReuseIdentifier:identifier]; 54 | 55 | if (cellClass) { 56 | self.cellClassForReuseIdentifier[identifier] = cellClass; 57 | } else { 58 | [self.cellClassForReuseIdentifier removeObjectForKey:identifier]; 59 | } 60 | } 61 | 62 | - (void)registerClass:(Class)headerFooterViewClass forHeaderFooterViewReuseIdentifier:(NSString *)identifier { 63 | NSAssert(identifier, @"Must have a reuse identifier."); 64 | NSAssert([headerFooterViewClass isSubclassOfClass:[YLTableViewSectionHeaderFooterView class]], @"You can only use subclasses of YLTableViewSectionHeaderFooterView."); 65 | 66 | [super registerClass:headerFooterViewClass forHeaderFooterViewReuseIdentifier:identifier]; 67 | 68 | if (headerFooterViewClass) { 69 | self.headerFooterViewClassForReuseIdentifier[identifier] = headerFooterViewClass; 70 | } else { 71 | [self.headerFooterViewClassForReuseIdentifier removeObjectForKey:identifier]; 72 | } 73 | } 74 | 75 | - (Class)cellClassForReuseIdentifier:(NSString *)reuseIdentifier { 76 | return self.cellClassForReuseIdentifier[reuseIdentifier]; 77 | } 78 | 79 | - (YLTableViewSectionHeaderFooterView *)sizingHeaderFooterViewForReuseIdentifier:(NSString *)reuseIdentifier { 80 | NSAssert(reuseIdentifier, @"Must have a reuse identifier."); 81 | NSAssert(self.headerFooterViewClassForReuseIdentifier[reuseIdentifier], @"You must register a class for this reuse identifier."); 82 | 83 | if (!self.sizingHeaderFooterViewsForReuseIdentifier[reuseIdentifier]) { 84 | Class headerFooterViewClass = self.headerFooterViewClassForReuseIdentifier[reuseIdentifier]; 85 | YLTableViewSectionHeaderFooterView *const sizingHeaderFooterView = [(YLTableViewSectionHeaderFooterView *)[headerFooterViewClass alloc] initWithReuseIdentifier:reuseIdentifier]; 86 | self.sizingHeaderFooterViewsForReuseIdentifier[reuseIdentifier] = sizingHeaderFooterView; 87 | } 88 | return self.sizingHeaderFooterViewsForReuseIdentifier[reuseIdentifier]; 89 | } 90 | 91 | #pragma mark YLRefreshHeaderView 92 | 93 | - (void)layoutSubviews { 94 | [super layoutSubviews]; 95 | 96 | // Put self.refreshHeaderView above the origin of self.frame. We set self.refreshHeaderView.frame.size to be equal to self.frame.size to gurantee that you won't be able to see beyond the top of the header view. 97 | // self.refreshHeaderView should draw it's content at the bottom of its frame. 98 | self.refreshHeaderView.frame = CGRectMake(0, -self.frame.size.height, self.frame.size.width, self.frame.size.height); 99 | } 100 | 101 | - (void)setRefreshHeaderView:(YLRefreshHeaderView *)refreshHeaderView { 102 | if (refreshHeaderView) { 103 | [self addSubview:refreshHeaderView]; 104 | [self sendSubviewToBack:refreshHeaderView]; 105 | self.showsVerticalScrollIndicator = YES; 106 | refreshHeaderView.scrollView = self; 107 | } else { 108 | [self.refreshHeaderView removeFromSuperview]; 109 | } 110 | _refreshHeaderView = refreshHeaderView; 111 | } 112 | 113 | #pragma mark State 114 | 115 | - (void)setState:(YLTableViewState)state { 116 | YLRefreshHeaderViewState newState = YLRefreshHeaderViewStateNormal; 117 | if (state == YLTableViewStateRefreshing) { 118 | newState = YLRefreshHeaderViewStateRefreshing; 119 | } else if (self.refreshHeaderView.refreshState == YLRefreshHeaderViewStateRefreshing) { 120 | newState = YLRefreshHeaderViewStateClosing; 121 | } 122 | self.refreshHeaderView.refreshState = newState; 123 | 124 | _state = state; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /Classes/YLTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewCell.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 6/10/14. 6 | // Copyright (c) 2014 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Defines required methods that a UITableViewCell subclass must implement to work with a YLTableViewDataSource 13 | */ 14 | @protocol YLTableViewCell 15 | 16 | /** 17 | Apply a cell model to a cell. Subclasses should implement this to decide how they display a model's content. 18 | */ 19 | - (void)setModel:(nullable id)model; 20 | 21 | /** 22 | Estimated height for a cell, called within tableView:estimatedHeightForRowAtIndexPath 23 | */ 24 | + (CGFloat)estimatedRowHeight; 25 | 26 | @end -------------------------------------------------------------------------------- /Classes/YLTableViewChildViewControllerCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewChildViewControllerCell.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 3/30/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | NS_ASSUME_NONNULL_BEGIN 10 | /** 11 | Implement this protocol in a YLTableViewCell subclass to use a child view controller inside of a table view cell. The YLTableViewDataSource subclass must have something set as the parentViewController property. 12 | */ 13 | @protocol YLTableViewChildViewControllerCell 14 | 15 | /** 16 | This method should return the view controller managed by the cell so that it can be properly connected with its parent. 17 | */ 18 | @property (readonly, nonatomic) UIViewController *childViewController; 19 | 20 | @end 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /Classes/YLTableViewDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewDataSource.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 6/10/14. 6 | // Copyright (c) 2014 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class YLRefreshHeaderView; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | /** 15 | Class that conforms to UITableViewDataSource and UITableViewDelegate to be subclassed by clients of YLTableView. This class contains the boilerplate for mapping cell models to cells, along with other boilerplate code that makes YLTableView easy to use. See YLTableViewDataSourceSubclass for more info. 16 | */ 17 | @interface YLTableViewDataSource : NSObject 18 | 19 | /** 20 | If one of model's properties has changed (but it's still the same object), this will reload the visible cell for that model. Useful whenever something mutates the state of a model (i.e. image loading) 21 | */ 22 | - (void)reloadVisibleCellForModel:(id)model inTableView:(UITableView *)tableView; 23 | 24 | /** 25 | Set as the parent view controller of any cells implementing YLTableViewChildViewControllerCell. 26 | */ 27 | @property (weak, nonatomic, nullable) UIViewController *parentViewController; 28 | 29 | /** 30 | Defaults to NO. If you would like to cache the estimated heights for rows, set this to YES. 31 | 32 | @note This is only really necessary for iOS 8. If you are using estimated row height + UITableViewAutomaticDimension in iOS 8, there's a bug 33 | when reloadData is called: if the estimated height is different from the real height for the cells, the table view will jump when you scroll 34 | after calling reloadData. To fix this, we can record the actual height as it comes on screen (willDisplayCell), and return this for the 35 | estimated row height. This bug is fixed in iOS 9. 36 | */ 37 | @property (assign, nonatomic) BOOL shouldCacheEstimatedRowHeights; 38 | 39 | @end 40 | NS_ASSUME_NONNULL_END 41 | -------------------------------------------------------------------------------- /Classes/YLTableViewDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewDataSource.m 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 6/10/14. 6 | // Copyright (c) 2014 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewDataSource.h" 10 | #import "YLTableViewDataSourceSubclass.h" 11 | 12 | #import "YLRefreshHeaderViewPrivate.h" 13 | #import "YLTableView.h" 14 | #import "YLTableViewPrivate.h" 15 | #import "YLTableViewCell.h" 16 | #import "YLTableViewChildViewControllerCell.h" 17 | #import "YLTableViewSectionHeaderFooterView.h" 18 | #import "YLTableViewSectionHeaderFooterViewPrivate.h" 19 | 20 | @interface YLTableViewDataSource () 21 | 22 | //! This is used to cache the estimated row heights. 23 | @property (strong, nonatomic) NSMutableDictionary * indexPathToEstimatedRowHeight; 24 | 25 | @end 26 | 27 | @implementation YLTableViewDataSource 28 | 29 | 30 | #pragma mark indexPathToEstimatedRowHeight property methods 31 | 32 | - (NSMutableDictionary *)indexPathToEstimatedRowHeight { 33 | if (!_indexPathToEstimatedRowHeight) { 34 | _indexPathToEstimatedRowHeight = [[NSMutableDictionary alloc] init]; 35 | } 36 | 37 | return _indexPathToEstimatedRowHeight; 38 | } 39 | 40 | #pragma mark Public Helpers 41 | 42 | - (void)reloadVisibleCellForModel:(id)model inTableView:(UITableView *)tableView { 43 | for (NSIndexPath *indexPath in [tableView indexPathsForVisibleRows]) { 44 | if ([self tableView:tableView modelForCellAtIndexPath:indexPath] == model) { 45 | [(UITableViewCell *)[tableView cellForRowAtIndexPath:indexPath] setModel:model]; 46 | return; 47 | } 48 | } 49 | } 50 | 51 | #pragma mark Private Helpers 52 | 53 | //! Constructs a cache key for the given index path. UITableView sometimes returns a different subclass, NSMutableIndexPath, so we can't use the index path as the key by itself. 54 | + (NSString *)_keyForIndexPath:(NSIndexPath *)indexPath { 55 | return [NSString stringWithFormat:@"%ld,%ld", (long)indexPath.section, (long)indexPath.row]; 56 | } 57 | 58 | #pragma mark Configuration 59 | 60 | - (void)tableView:(UITableView *)tableView configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath { 61 | [cell setModel:[self tableView:tableView modelForCellAtIndexPath:indexPath]]; 62 | } 63 | 64 | - (void)tableView:(UITableView *)tableView configureHeader:(YLTableViewSectionHeaderFooterView *)headerView forSection:(NSUInteger)section { 65 | headerView.position = section == 0 ? YLTableViewSectionHeaderFooterPositionFirstHeader : YLTableViewSectionHeaderFooterPositionHeader; 66 | } 67 | 68 | - (void)tableView:(UITableView *)tableView configureFooter:(YLTableViewSectionHeaderFooterView *)footerView forSection:(NSUInteger)section { 69 | footerView.position = (section == [tableView numberOfSections] - 1) ? YLTableViewSectionHeaderFooterPositionLastFooter : YLTableViewSectionHeaderFooterPositionFooter; 70 | } 71 | 72 | #pragma mark Reuse Identifiers 73 | 74 | - (NSString *)tableView:(UITableView *)tableView reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath { 75 | NSAssert(NO, @"Must have a reuse identifier for all rows."); 76 | return nil; 77 | } 78 | 79 | - (NSString *)tableView:(UITableView *)tableView reuseIdentifierForHeaderInSection:(NSUInteger)section { 80 | return nil; 81 | } 82 | 83 | - (NSString *)tableView:(UITableView *)tableView reuseIdentifierForFooterInSection:(NSUInteger)section { 84 | return nil; 85 | } 86 | 87 | #pragma mark Models 88 | 89 | - (id)tableView:(UITableView *)tableView modelForCellAtIndexPath:(NSIndexPath *)indexPath { 90 | NSAssert(NO, @"Must have a model for all rows."); 91 | return nil; 92 | } 93 | 94 | #pragma mark UITableViewDataSource 95 | 96 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 97 | NSAssert(NO, @"This is abstract."); 98 | return -1; 99 | } 100 | 101 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 102 | NSString *const reuseID = [self tableView:tableView reuseIdentifierForCellAtIndexPath:indexPath]; 103 | NSAssert(reuseID != nil, @"Must have a reuse identifier."); 104 | UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:reuseID forIndexPath:indexPath]; 105 | NSAssert([[cell class] conformsToProtocol:@protocol(YLTableViewCell)], @"You can only use cells conforming to YLTableViewCell."); 106 | [self tableView:tableView configureCell:cell forIndexPath:indexPath]; 107 | return cell; 108 | } 109 | 110 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { 111 | NSString *const reuseID = [self tableView:tableView reuseIdentifierForHeaderInSection:section]; 112 | 113 | if (reuseID == nil) { 114 | return nil; 115 | } 116 | 117 | YLTableViewSectionHeaderFooterView *headerView = (YLTableViewSectionHeaderFooterView *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:reuseID]; 118 | headerView.sizingView = NO; 119 | [self tableView:tableView configureHeader:headerView forSection:section]; 120 | return headerView; 121 | } 122 | 123 | - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { 124 | NSString *const reuseID = [self tableView:tableView reuseIdentifierForFooterInSection:section]; 125 | 126 | if (reuseID == nil) { 127 | return nil; 128 | } 129 | 130 | YLTableViewSectionHeaderFooterView *footerView = (YLTableViewSectionHeaderFooterView *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:reuseID]; 131 | footerView.sizingView = NO; 132 | [self tableView:tableView configureFooter:footerView forSection:section]; 133 | return footerView; 134 | } 135 | 136 | #pragma mark UITableViewDelegate 137 | 138 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 139 | NSAssert([tableView isKindOfClass:[YLTableView class]], @"This can only be the delegate of a YLTableView."); 140 | return UITableViewAutomaticDimension; 141 | } 142 | 143 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 144 | NSAssert([tableView isKindOfClass:[YLTableView class]], @"This can only be the delegate of a YLTableView."); 145 | 146 | NSString *const reuseID = [self tableView:tableView reuseIdentifierForHeaderInSection:section]; 147 | if (reuseID) { 148 | YLTableViewSectionHeaderFooterView *headerView = [(YLTableView *)tableView sizingHeaderFooterViewForReuseIdentifier:reuseID]; 149 | headerView.sizingView = YES; 150 | [self tableView:tableView configureHeader:headerView forSection:section]; 151 | return [headerView heightForWidth:CGRectGetWidth(tableView.bounds)]; 152 | } else if ([self respondsToSelector:@selector(tableView:titleForHeaderInSection:)] && 153 | [self tableView:tableView titleForHeaderInSection:section]) { 154 | return UITableViewAutomaticDimension; 155 | } else { 156 | return 0; 157 | } 158 | } 159 | 160 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { 161 | NSAssert([tableView isKindOfClass:[YLTableView class]], @"This can only be the delegate of a YLTableView."); 162 | 163 | NSString *const reuseID = [self tableView:tableView reuseIdentifierForFooterInSection:section]; 164 | if (reuseID) { 165 | YLTableViewSectionHeaderFooterView *footerView = [(YLTableView *)tableView sizingHeaderFooterViewForReuseIdentifier:reuseID]; 166 | footerView.sizingView = YES; 167 | [self tableView:tableView configureFooter:footerView forSection:section]; 168 | return [footerView heightForWidth:CGRectGetWidth(tableView.bounds)]; 169 | } else if ([self respondsToSelector:@selector(tableView:titleForFooterInSection:)] && 170 | [self tableView:tableView titleForFooterInSection:section]) { 171 | return UITableViewAutomaticDimension; 172 | } else { 173 | return 0; 174 | } 175 | } 176 | 177 | - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { 178 | NSAssert([tableView isKindOfClass:[YLTableView class]], @"This can only be the delegate of a YLTableView."); 179 | 180 | if (self.shouldCacheEstimatedRowHeights && self.indexPathToEstimatedRowHeight[[[self class] _keyForIndexPath:indexPath]]) { 181 | return [self.indexPathToEstimatedRowHeight[[[self class] _keyForIndexPath:indexPath]] floatValue]; 182 | } 183 | id model = [self tableView:tableView modelForCellAtIndexPath:indexPath]; 184 | return [self tableView:tableView estimatedHeightForRowAtIndexPath:indexPath withModel:model]; 185 | } 186 | 187 | - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath withModel:(id)model { 188 | Class cellClass = [(YLTableView *)tableView cellClassForReuseIdentifier:[self tableView:tableView reuseIdentifierForCellAtIndexPath:indexPath]]; 189 | NSAssert([cellClass conformsToProtocol:@protocol(YLTableViewCell)], @"You can only use cells conforming to YLTableViewCell."); 190 | return [(id)cellClass estimatedRowHeight]; 191 | } 192 | 193 | #pragma mark ChildViewController support 194 | 195 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { 196 | 197 | if (self.shouldCacheEstimatedRowHeights) { 198 | self.indexPathToEstimatedRowHeight[[[self class] _keyForIndexPath:indexPath]] = @(cell.frame.size.height); 199 | } 200 | 201 | UIViewController *parentViewController = self.parentViewController; 202 | if ([cell conformsToProtocol:@protocol(YLTableViewChildViewControllerCell)]) { 203 | NSAssert(parentViewController != nil, @"Must have a parent view controller to support cell %@", cell); 204 | UITableViewCell *viewControllerCell = (UITableViewCell *)cell; 205 | UIViewController *childViewController = [viewControllerCell childViewController]; 206 | [parentViewController addChildViewController:childViewController]; 207 | [childViewController didMoveToParentViewController:parentViewController]; 208 | } 209 | } 210 | 211 | - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { 212 | if ([cell conformsToProtocol:@protocol(YLTableViewChildViewControllerCell)]) { 213 | NSAssert(self.parentViewController != nil, @"Must have a parent view controller to support cell %@", cell); 214 | UITableViewCell *viewControllerCell = (UITableViewCell *)cell; 215 | UIViewController *childViewController = [viewControllerCell childViewController]; 216 | [childViewController willMoveToParentViewController:nil]; 217 | [childViewController removeFromParentViewController]; 218 | } 219 | } 220 | 221 | #pragma mark UIScrollViewDelegate 222 | 223 | // NOTE: The following methods inform a refresh header view of the scrolling behavior of the tableview. This makes our refresh headers work. 224 | 225 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 226 | NSAssert([scrollView isKindOfClass:[YLTableView class]], @"This can only be the delegate of a YLTableView."); 227 | YLRefreshHeaderView *refreshHeaderView = [(YLTableView *)scrollView refreshHeaderView]; 228 | if (refreshHeaderView) { 229 | NSAssert([refreshHeaderView isKindOfClass:[YLRefreshHeaderView class]], @"The refresh header view must be a subclass of YLRefreshHeaderView."); 230 | [refreshHeaderView containingScrollViewDidScroll:scrollView]; 231 | } 232 | } 233 | 234 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 235 | NSAssert([scrollView isKindOfClass:[YLTableView class]], @"This can only be the delegate of a YLTableView."); 236 | YLRefreshHeaderView *refreshHeaderView = [(YLTableView *)scrollView refreshHeaderView]; 237 | if (refreshHeaderView) { 238 | NSAssert([refreshHeaderView isKindOfClass:[YLRefreshHeaderView class]], @"The refresh header view must be a subclass of YLRefreshHeaderView."); 239 | [refreshHeaderView containingScrollViewDidEndDragging:scrollView]; 240 | } 241 | } 242 | 243 | @end -------------------------------------------------------------------------------- /Classes/YLTableViewDataSourceSubclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewDataSourceSubclass.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 6/10/14. 6 | // Copyright (c) 2014 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewDataSource.h" 10 | 11 | @protocol YLTableViewCell; 12 | @class YLTableViewSectionHeaderFooterView; 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | @interface YLTableViewDataSource () 16 | 17 | #pragma mark Abstract methods 18 | 19 | /** 20 | Abstract method – must be handled by subclass. Return the reuse identifier for the given indexPath. 21 | */ 22 | - (NSString *)tableView:(UITableView *)tableView reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath; 23 | 24 | /** 25 | Abstract method – must be handled by subclass. The returned model will be passed to the cell for indexPath. 26 | */ 27 | - (id)tableView:(UITableView *)tableView modelForCellAtIndexPath:(NSIndexPath *)indexPath; 28 | 29 | 30 | #pragma mark Optional Methods 31 | 32 | /** 33 | * Return the reuse identifier for the given section header. By default, returns nil, in which case, no header is created. 34 | * 35 | * If nil is returned, tableView:heightForHeaderInSection: returns UITableViewAutomaticDimension which results in 17.5pt empty space on OS7. 36 | */ 37 | - (nullable NSString *)tableView:(UITableView *)tableView reuseIdentifierForHeaderInSection:(NSUInteger)section; 38 | 39 | /** 40 | * 41 | * Return the reuse identifier for the given section footer. By default, returns nil, in which case, no footer is created. 42 | * 43 | * If nil is returned, tableView:heightForFooterInSection: returns UITableViewAutomaticDimension which results in 17.5pt empty space on OS7. 44 | */ 45 | - (nullable NSString *)tableView:(UITableView *)tableView reuseIdentifierForFooterInSection:(NSUInteger)section; 46 | 47 | /** 48 | * Configure cell for display of the content in indexPath – gets the model for indexPath and then calls setModel on cell. 49 | * 50 | * Override this method if you need to do cell-specific configuration (for example, alternating background colors) but call super implementation first. 51 | */ 52 | - (void)tableView:(UITableView *)tableView configureCell:(UITableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath NS_REQUIRES_SUPER; 53 | 54 | /** 55 | Override this method to configure headerView to display content in section but call super implementation first. 56 | */ 57 | - (void)tableView:(UITableView *)tableView configureHeader:(YLTableViewSectionHeaderFooterView *)headerView forSection:(NSUInteger)section NS_REQUIRES_SUPER; 58 | 59 | /** 60 | Override this method to configure footerView to display content in section but call super implementation first. 61 | */ 62 | - (void)tableView:(UITableView *)tableView configureFooter:(YLTableViewSectionHeaderFooterView *)footerView forSection:(NSUInteger)section NS_REQUIRES_SUPER; 63 | 64 | /** 65 | Override this method to override YLTableViewDataSource's default estimated row height behavior (using the YLTableView class method) and provide your own estimated row height based on the cell's model and/or row 66 | */ 67 | - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath withModel:(nullable id)model; 68 | 69 | 70 | #pragma mark Pull To Refresh support 71 | 72 | /** 73 | You don't need to implement this, but make sure to call super if you do. 74 | */ 75 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView NS_REQUIRES_SUPER; 76 | 77 | /** 78 | You don't need to implement this, but make sure to call super if you do. 79 | */ 80 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate NS_REQUIRES_SUPER; 81 | 82 | 83 | #pragma mark ChildViewController support 84 | 85 | /** 86 | You don't need to implement this, but make sure to call super if you do. 87 | */ 88 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath NS_REQUIRES_SUPER; 89 | 90 | /** 91 | You don't need to implement this, but make sure to call super if you do. 92 | */ 93 | - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath NS_REQUIRES_SUPER; 94 | 95 | @end 96 | NS_ASSUME_NONNULL_END 97 | -------------------------------------------------------------------------------- /Classes/YLTableViewFramework.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableView.h 3 | // YLTableView 4 | // 5 | // Created by Steven Brooks on 5/16/19. 6 | // Copyright © 2019 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for YLTableView. 12 | FOUNDATION_EXPORT double YLTableViewVersionNumber; 13 | 14 | //! Project version string for YLTableView. 15 | FOUNDATION_EXPORT const unsigned char YLTableViewVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | -------------------------------------------------------------------------------- /Classes/YLTableViewHeaderFooterView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewHeaderFooterView.h 3 | // YLTableView 4 | // 5 | // Created by Tom Abraham on 5/13/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | /** YLTableViewHeaderFooterView is wrapper view that is to be used as the table{Header/Footer}View of 13 | * a UITableView. The one piece of functionality it offers is for UIViews with Auto Layout constraints 14 | * to be used as table{Header/Footer}Views in such a way that if their content changes, the height 15 | * of the table{Header/Footer}View will be updated to reflect the change in the height of the content. 16 | * 17 | * They can be used as such: 18 | * 19 | * tableView.tableHeaderView = [[YLTableViewHeaderFooterView alloc] initWithView:actualHeaderView forTableView:tableView]; 20 | * 21 | * When UITableView starts to support UIViews with Auto Layout constraints (iOS 8?), the above line can 22 | * be replaced simply by: 23 | * 24 | * tableView.tableHeaderView = actualHeaderView; 25 | */ 26 | @interface YLTableViewHeaderFooterView : UIView 27 | 28 | - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; 29 | - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; 30 | 31 | /** @param view A UIView instance that *must* be sized using Auto Layout constraints. It should 32 | * *not* have its width (unambiguously) defined. Its width will be decided by self. 33 | * On the other hand, its height *should* be unambiguously defined. 34 | * @param tableView The UITableView for which self is going to be a table{Header,Footer}View for i.e. 35 | * tableView.table{Header/Footer}View == self must be true. 36 | */ 37 | - (nullable instancetype)initWithView:(UIView *)view forTableView:(UITableView *)tableView NS_DESIGNATED_INITIALIZER; 38 | 39 | @end 40 | NS_ASSUME_NONNULL_END 41 | -------------------------------------------------------------------------------- /Classes/YLTableViewHeaderFooterView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewHeaderFooterView.m 3 | // YLTableView 4 | // 5 | // Created by Tom Abraham on 5/13/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewHeaderFooterView.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | @interface YLTableViewHeaderFooterView () 13 | 14 | @property (strong, nonatomic) UIView *view; 15 | @property (weak, nonatomic, nullable) UITableView *tableView; 16 | @property (assign, nonatomic) CGFloat previousHeight; 17 | 18 | @end 19 | NS_ASSUME_NONNULL_END 20 | 21 | @implementation YLTableViewHeaderFooterView 22 | 23 | - (instancetype)initWithView:(UIView *)view forTableView:(UITableView *)tableView { 24 | if (self = [super initWithFrame:CGRectZero]) { 25 | _view = view; 26 | [self addSubview:view]; 27 | 28 | _tableView = tableView; 29 | 30 | [self _installConstraints]; 31 | } 32 | 33 | return self; 34 | } 35 | 36 | #pragma mark Layout 37 | 38 | - (void)_installConstraints { 39 | NSDictionary *views = NSDictionaryOfVariableBindings(_view, self); 40 | 41 | // note how we don't pin self.view to the bottom or right of self 42 | // this is so that the self.view doesn't have self's autoresizing mask constraints constraining its height or width 43 | self.view.translatesAutoresizingMaskIntoConstraints = NO; 44 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_view]" options:0 metrics:nil views:views]]; 45 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_view]" options:0 metrics:nil views:views]]; 46 | 47 | // If we constrained the view to be the same width as us, it would first be sized as 0px wide, which would break a bunch of constraints. We'll put the priority of the width constraint at 999, so our other required constraints override it. 48 | NSLayoutConstraint *viewWidthConstraint = [[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_view(==self)]" options:0 metrics:nil views:views] firstObject]; 49 | viewWidthConstraint.priority = UILayoutPriorityRequired - 1; 50 | [self addConstraint:viewWidthConstraint]; 51 | } 52 | 53 | - (void)layoutSubviews { 54 | // layout (and thus, size) our one subview, self.view 55 | [super layoutSubviews]; 56 | 57 | // update our height to match view's height 58 | // on OS7 and before, UITableViews do *not* like fractional heights for their table{Header/Footer}View. 59 | // they respond by increasing the space available for the table{Header/Footer}View by the fraction to the 60 | // nearest whole number on *each* layout pass. thus, we round to the nearest whole number. filed rdar://16909794 61 | CGFloat newHeight = round(CGRectGetHeight(self.view.bounds)); 62 | CGRect bounds = self.bounds; 63 | bounds.size.height = newHeight; 64 | self.bounds = bounds; 65 | 66 | // re-setting the header/footer tells the table view that its height changed 67 | UITableView *tableView = self.tableView; 68 | if (tableView.tableHeaderView == self && newHeight != self.previousHeight) tableView.tableHeaderView = self; 69 | else if (tableView.tableFooterView == self && newHeight != self.previousHeight) tableView.tableFooterView = self; 70 | 71 | // we updated our height so we need to relayout our subviews 72 | self.previousHeight = newHeight; 73 | [super layoutSubviews]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /Classes/YLTableViewPrivate.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewPrivate.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 6/3/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | // NOTE: This header is not actually private. See PR#41: https://github.com/Yelp/YLTableView/pull/41 10 | 11 | #import "YLTableView.h" 12 | 13 | @class YLTableViewSectionHeaderFooterView; 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | @interface YLTableView () 17 | 18 | /** 19 | Returns the cell class registered for this reuse identifier, or Nil if none was registered. 20 | */ 21 | - (nullable Class)cellClassForReuseIdentifier:(NSString *)reuseIdentifier; 22 | 23 | /** 24 | Returns a cached section header/footer view for this reuse identifier. The view should only be used for sizing purposes, not for display. 25 | */ 26 | - (YLTableViewSectionHeaderFooterView *)sizingHeaderFooterViewForReuseIdentifier:(NSString *)reuseIdentifier; 27 | 28 | @end 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /Classes/YLTableViewSectionHeaderFooterView/YLTableViewSectionHeaderFooterLabelView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewSectionHeaderFooterLabelView.h 3 | // YLTableView 4 | // 5 | // Created by Tom Abraham on 7/2/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewSectionHeaderFooterView.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | /** 13 | A section header / footer view that supports a single, multi-line label 14 | */ 15 | @interface YLTableViewSectionHeaderFooterLabelView : YLTableViewSectionHeaderFooterView 16 | 17 | /** 18 | We don't use the default textLabel because then this header/footer would be sized assuming the system default header/footer font. 19 | */ 20 | @property (readonly, strong, nonatomic) UILabel *label; 21 | 22 | /** 23 | Setting this property will set the label's text. 24 | */ 25 | @property (copy, nonatomic, nullable) NSString *text; 26 | 27 | @end 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /Classes/YLTableViewSectionHeaderFooterView/YLTableViewSectionHeaderFooterLabelView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewSectionHeaderFooterLabelView.m 3 | // YLTableView 4 | // 5 | // Created by Tom Abraham on 7/2/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewSectionHeaderFooterLabelView.h" 10 | 11 | #import "YLTableViewSectionHeaderFooterViewSubclass.h" 12 | 13 | @implementation YLTableViewSectionHeaderFooterLabelView 14 | 15 | - (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { 16 | if (self = [super initWithReuseIdentifier:reuseIdentifier]) { 17 | _label = [[UILabel alloc] init]; 18 | _label.numberOfLines = 0; 19 | _label.translatesAutoresizingMaskIntoConstraints = NO; 20 | [self.contentLayoutGuideView addSubview:_label]; 21 | 22 | [self _installLabelConstraints]; 23 | } 24 | return self; 25 | } 26 | 27 | #pragma mark Layout 28 | 29 | - (void)_installLabelConstraints { 30 | NSDictionary *views = NSDictionaryOfVariableBindings(_label); 31 | 32 | [self.contentLayoutGuideView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_label]|" options:0 metrics:nil views:views]]; 33 | [self.contentLayoutGuideView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_label]|" options:0 metrics:nil views:views]]; 34 | } 35 | 36 | #pragma mark Text property 37 | 38 | - (void)setText:(NSString *)text { 39 | self.label.text = text; 40 | } 41 | 42 | - (NSString *)text { 43 | return self.label.text; 44 | } 45 | 46 | @end -------------------------------------------------------------------------------- /Classes/YLTableViewSectionHeaderFooterView/YLTableViewSectionHeaderFooterView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewSectionHeaderFooterView.h 3 | // YLTableView 4 | // 5 | // Created by Tom Abraham on 11/11/13. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Enum to represent a header/footer view's position relative to sections in the table view 13 | */ 14 | typedef NS_ENUM(NSInteger, YLTableViewSectionHeaderFooterPosition) { 15 | /** 16 | Set when the position had not yet been defined 17 | */ 18 | YLTableViewSectionHeaderFooterPositionUndefined, 19 | /** 20 | Any header but the first one in the table view 21 | */ 22 | YLTableViewSectionHeaderFooterPositionHeader, 23 | /** 24 | The first header in the table view 25 | */ 26 | YLTableViewSectionHeaderFooterPositionFirstHeader, 27 | /** 28 | Any footer but the last on one in the table view 29 | */ 30 | YLTableViewSectionHeaderFooterPositionFooter, 31 | /** 32 | The last footer in the table view 33 | */ 34 | YLTableViewSectionHeaderFooterPositionLastFooter, 35 | }; 36 | 37 | /** 38 | UITableViewHeaderFooterView subclass that encapsulates the boilerplate code to make self-sizing headers and footers work correctly in a YLTableView 39 | */ 40 | @interface YLTableViewSectionHeaderFooterView : UITableViewHeaderFooterView 41 | 42 | /** 43 | Position of this header/footer in the table view 44 | */ 45 | @property (readonly, assign, nonatomic) YLTableViewSectionHeaderFooterPosition position; 46 | 47 | /** 48 | If YES, this instance is being used as a sizing header/footer and won't actually be displayed on screen. 49 | */ 50 | @property (readonly, assign, nonatomic, getter=isSizingView) BOOL sizingView; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Classes/YLTableViewSectionHeaderFooterView/YLTableViewSectionHeaderFooterView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewSectionHeaderFooterView.m 3 | // YLTableView 4 | // 5 | // Created by Tom Abraham on 11/11/13. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewSectionHeaderFooterView.h" 10 | #import "YLTableViewSectionHeaderFooterViewSubclass.h" 11 | #import "YLTableViewSectionHeaderFooterViewPrivate.h" 12 | 13 | #import "YLTableView.h" 14 | #import "YLTableViewCell.h" 15 | 16 | static const CGFloat kSmallVerticalPadding = 7; 17 | static const CGFloat kLargeVerticalPadding = 12; 18 | static const CGFloat kDefaultHorizontalPadding = 15; 19 | static const CGFloat kFirstLastSectionExtraVerticalPadding = 17.5; 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | @interface YLTableViewSectionHeaderFooterView () 23 | @property (copy, nonatomic) NSArray *contentLayoutGuideConstraints; 24 | @property (strong, nonatomic) NSLayoutConstraint *contentLayoutGuideWidthConstraint; 25 | @end 26 | NS_ASSUME_NONNULL_END 27 | 28 | @implementation YLTableViewSectionHeaderFooterView 29 | 30 | - (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { 31 | if (self = [super initWithReuseIdentifier:reuseIdentifier]) { 32 | _contentLayoutGuideView = [[UIView alloc] init]; 33 | _contentLayoutGuideView.translatesAutoresizingMaskIntoConstraints = NO; 34 | [self.contentView addSubview:_contentLayoutGuideView]; 35 | 36 | [self _installContentLayoutGuideWidthConstraint]; 37 | } 38 | return self; 39 | } 40 | 41 | #pragma mark Layout 42 | 43 | - (void)_installContentLayoutGuideWidthConstraint { 44 | // For some reason, doing |-left-[_contentLayoutGuideView]-(right@999)-| doesn't work when the header's label is more than one line long, so we have to do this as a width. 45 | self.contentLayoutGuideWidthConstraint = [NSLayoutConstraint constraintWithItem:self.contentLayoutGuideView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0]; 46 | // Allow the width constraint to be broken by subviews that are constrained with additional non-zero padding 47 | self.contentLayoutGuideWidthConstraint.priority = UILayoutPriorityRequired - 1; 48 | [self.contentView addConstraint:self.contentLayoutGuideWidthConstraint]; 49 | } 50 | 51 | - (void)layoutSubviews { 52 | UIEdgeInsets edgeInsets = [self _totalInsets]; 53 | // The layout guide doesn't like being told to have a negative width, so we have to make sure it's at least 0. 54 | self.contentLayoutGuideWidthConstraint.constant = MAX(CGRectGetWidth(self.bounds) - edgeInsets.left - edgeInsets.right, 0); 55 | 56 | [super layoutSubviews]; 57 | } 58 | 59 | - (void)updateConstraints { 60 | if (self.contentLayoutGuideConstraints) { 61 | [self.contentView removeConstraints:self.contentLayoutGuideConstraints]; 62 | } 63 | 64 | // Before this view has been fully configured, UITableView adds a constraint forcing this to be CGSizeZero. To prevent unsatisfiable constraints, we have to let a constraint collapse. 65 | 66 | NSString *vfl = nil; 67 | switch (self.position) { 68 | case YLTableViewSectionHeaderFooterPositionUndefined: 69 | NSAssert(NO, @"undefined YLTableViewSectionHeaderFooterPosition"); 70 | case YLTableViewSectionHeaderFooterPositionHeader: 71 | case YLTableViewSectionHeaderFooterPositionFirstHeader: 72 | vfl = @"V:|-(>=top@priorityNotRequired)-[_contentLayoutGuideView]-bottom-|"; 73 | break; 74 | case YLTableViewSectionHeaderFooterPositionFooter: 75 | case YLTableViewSectionHeaderFooterPositionLastFooter: 76 | vfl = @"V:|-top-[_contentLayoutGuideView]-(>=bottom@priorityNotRequired)-|"; 77 | break; 78 | } 79 | 80 | NSMutableArray *constraints = [NSMutableArray arrayWithArray:[NSLayoutConstraint constraintsWithVisualFormat:vfl options:0 metrics:[self _metrics] views:[self _views]]]; 81 | 82 | [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-left-[_contentLayoutGuideView]" options:0 metrics:[self _metrics] views:[self _views]]]; 83 | 84 | self.contentLayoutGuideConstraints = constraints; 85 | [self.contentView addConstraints:self.contentLayoutGuideConstraints]; 86 | [super updateConstraints]; 87 | } 88 | 89 | - (NSDictionary *)_views { 90 | return NSDictionaryOfVariableBindings(_contentLayoutGuideView); 91 | } 92 | 93 | - (NSDictionary *)_metrics { 94 | UIEdgeInsets edgeInsets = [self _totalInsets]; 95 | return @{ @"left": @(edgeInsets.left), 96 | @"right": @(edgeInsets.right), 97 | @"top": @(edgeInsets.top), 98 | @"bottom": @(edgeInsets.bottom), 99 | @"priorityNotRequired": @(UILayoutPriorityRequired - 1) }; 100 | } 101 | 102 | - (UIEdgeInsets)_totalInsets { 103 | // When you don't specify section footers, they are 17.5pt tall by default. This makes up the 104 | // inter-section padding in grouped table views. Since the first section header doesn't have a 105 | // (previous section's) footer before it, the first section header's height is made 106 | // 17.5pts taller by the system when tableView:heightForHeaderInSection: isn't implemented. 107 | // This code emulates the system behavior. A similar thing happens for footers. 108 | CGFloat extraTopPadding = self.position == YLTableViewSectionHeaderFooterPositionFirstHeader ? kFirstLastSectionExtraVerticalPadding : 0; 109 | CGFloat extraBottomPadding = self.position == YLTableViewSectionHeaderFooterPositionLastFooter ? kFirstLastSectionExtraVerticalPadding : 0; 110 | UIEdgeInsets edgeInsets = self.contentLayoutGuideInsets; 111 | edgeInsets.top += extraTopPadding; 112 | edgeInsets.bottom += extraBottomPadding; 113 | return edgeInsets; 114 | } 115 | 116 | #pragma mark Public methods 117 | 118 | - (CGFloat)heightForWidth:(CGFloat)width { 119 | // set our width 120 | CGRect bounds = self.bounds; 121 | bounds.size.width = width; 122 | self.bounds = bounds; 123 | 124 | [self layoutIfNeeded]; 125 | 126 | // calculate minimum required height 127 | const CGFloat contentViewHeight = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; 128 | return contentViewHeight == 0 ? UITableViewAutomaticDimension : contentViewHeight; 129 | } 130 | 131 | - (void)setPosition:(YLTableViewSectionHeaderFooterPosition)position { 132 | if (position != _position) { 133 | _position = position; 134 | [self setNeedsUpdateConstraints]; 135 | } 136 | } 137 | 138 | #pragma mark Protected helpers 139 | 140 | - (UIEdgeInsets)contentLayoutGuideInsets { 141 | CGFloat top = 0; 142 | CGFloat bottom = 0; 143 | 144 | switch (self.position) { 145 | case YLTableViewSectionHeaderFooterPositionUndefined: 146 | NSAssert(NO, @"undefined YLTableViewSectionHeaderFooterPosition"); 147 | 148 | case YLTableViewSectionHeaderFooterPositionHeader: 149 | case YLTableViewSectionHeaderFooterPositionFirstHeader: 150 | top = kLargeVerticalPadding; 151 | bottom = kSmallVerticalPadding; 152 | break; 153 | 154 | case YLTableViewSectionHeaderFooterPositionFooter: 155 | case YLTableViewSectionHeaderFooterPositionLastFooter: 156 | top = kSmallVerticalPadding; 157 | bottom = kLargeVerticalPadding; 158 | break; 159 | } 160 | 161 | return UIEdgeInsetsMake(top, kDefaultHorizontalPadding, bottom, kDefaultHorizontalPadding); 162 | } 163 | 164 | @end -------------------------------------------------------------------------------- /Classes/YLTableViewSectionHeaderFooterView/YLTableViewSectionHeaderFooterViewPrivate.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewSectionHeaderFooterViewPrivate.h 3 | // YLTableView 4 | // 5 | // Created by Mason Glidden on 6/3/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | // NOTE: This header is not actually private. See PR#41: https://github.com/Yelp/YLTableView/pull/41 10 | 11 | #import "YLTableViewSectionHeaderFooterView.h" 12 | 13 | @interface YLTableViewSectionHeaderFooterView () 14 | 15 | /** 16 | Position of this header/footer in the table view 17 | */ 18 | @property (assign, nonatomic) YLTableViewSectionHeaderFooterPosition position; 19 | 20 | /** 21 | * Returns contentView's computed height based upon Auto Layout constraints if non-0. Otherwise, it 22 | * returns UITableViewAutomaticDimension (which, when returned from UITableViewDelegate's 23 | * heightForRowAtIndexPath:, will size the header/footer defaultEmptySectionHeaderFooterHeight tall) 24 | * 25 | * @param width Width of the contentView to assume when determining height 26 | * 27 | * @result Height of header/footer calculated assuming its contentView is of the specified width 28 | */ 29 | - (CGFloat)heightForWidth:(CGFloat)width; 30 | 31 | /** 32 | If YES, this instance is being used as a sizing header/footer and won't actually be displayed on screen. 33 | */ 34 | @property (assign, nonatomic, getter=isSizingView) BOOL sizingView; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /Classes/YLTableViewSectionHeaderFooterView/YLTableViewSectionHeaderFooterViewSubclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewSectionHeaderFooterViewSubclass.h 3 | // YLTableView 4 | // 5 | // Created by Tom Abraham on 7/2/14. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewSectionHeaderFooterView.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | @interface YLTableViewSectionHeaderFooterView () 13 | 14 | /** 15 | * You should add subviews to this view, and pin those views to it using Auto Layout constraints. 16 | * 17 | * This view is inset by contentLayoutGuideInsets + _systemContentLayoutGuideInsets. contentLayoutGuideInsets can be overridden by subclasses. 18 | */ 19 | @property (readonly, strong, nonatomic) UIView *contentLayoutGuideView; 20 | 21 | @property (readonly, assign, nonatomic) UIEdgeInsets contentLayoutGuideInsets; 22 | 23 | @end 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016 Yelp 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/Yelp/YLTableView.svg?branch=master)](https://travis-ci.org/Yelp/YLTableView) 2 | 3 | # YLTableView 4 | 5 | YLTableView is a framework for working with UITableView used by a number of the table views inside of the Yelp app. It makes it easy to work with cell models, allows your cells to have child view controllers, and provides a framework for building your own pull-to-refresh header. 6 | 7 | To learn more about the goals high-level architecture of YLTableView, check out our [blog post](http://engineeringblog.yelp.com/2015/06/advanced-uitableviews-made-simple-yltableview.html). 8 | 9 | ## Installation 10 | 11 | We recommend using CocoaPods to install YLTableView. To do so, add the following to your `Podfile`: 12 | 13 | ``` ruby 14 | pod 'YLTableView', '~> 2.2.0' 15 | ``` 16 | 17 | # Example usage 18 | You can clone the project and check out the example app to see YLTableView in use. 19 | 20 | ## Cells and Models 21 | 22 | All cells should provide an estimated height and take a model. Cells generally expect one specific type of model, while a given model could work with any number of cells. Start by subclassing `UITableViewCell` and implementing the `YLTableViewCell` protocol, which requires implementing `setModel:` and `estimatedRowHeight:`: 23 | 24 | ``` objc 25 | @interface YLExampleTextCell : UITableViewCell 26 | @end 27 | 28 | @implementation YLExampleTextCell 29 | - (void)setModel:(NSObject *)model { 30 | NSAssert([model isKindOfClass:[NSString class]], @"Must use %@ with %@", NSStringFromClass([NSString class]), NSStringFromClass([self class])); 31 | self.mainTextLabel.text = (NSString *)model; 32 | } 33 | 34 | + (CGFloat)estimatedRowHeight { 35 | return 44.0; 36 | } 37 | @end 38 | ``` 39 | 40 | This cell expects an NSString as its model, but your cell can use any object you want. 41 | 42 | ## Data Source 43 | To use YLTableView, you should subclass `YLTableViewDataSource`. After implementing a few simple methods, `YLTableViewDataSource` takes care of connecting cells to models. 44 | 45 | ``` objc 46 | @interface YLExampleTableViewDataSource : YLTableViewDataSource 47 | @property (copy, nonatomic) NSArray *models; 48 | @end 49 | 50 | @implementation YLExampleTableViewDataSource 51 | - (instancetype)init { 52 | if (self = [super init]) { 53 | _models = @[@"cell one", @"cell two", @"cell three"]; 54 | } 55 | return self; 56 | } 57 | 58 | #pragma mark YLTableViewDataSource subclass 59 | 60 | - (NSString *)tableView:(UITableView *)tableView reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath { 61 | return NSStringFromClass([YLExampleTextCell class]); 62 | } 63 | 64 | - (NSObject *)tableView:(UITableView *)tableView modelForCellAtIndexPath:(NSIndexPath *)indexPath { 65 | // The model returned here must work with the cell specified above. 66 | return self.sectionModels[indexPath.row]; 67 | } 68 | 69 | #pragma mark UITableViewDataSource 70 | 71 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 72 | return self.sectionModels.count; 73 | } 74 | @end 75 | ``` 76 | 77 | ## Table View 78 | Finally, you can use a YLTableView to connect everything together. 79 | 80 | ``` objc 81 | @interface YLExampleViewController : UIViewController 82 | @end 83 | 84 | @implementation YLExampleViewController 85 | - (void)loadView { 86 | [super loadView]; 87 | YLExampleTableViewDataSource *dataSource = [[YLExampleTableViewDataSource alloc] init]; 88 | YLTableView *tableView = [[YLTableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; 89 | // YLTableViewDataSource has to be the delegate and the dataSource of the table view. 90 | tableView.dataSource = dataSource; 91 | tableView.delegate = dataSource; 92 | 93 | // Registering cells and reuse identifiers 94 | [tableView registerClass:[YLExampleTextCell class] forCellReuseIdentifier:NSStringFromClass([YLExampleTextCell class])]; 95 | 96 | self.view = tableView; 97 | } 98 | @end 99 | ``` 100 | 101 | # Child View Controllers 102 | 103 | In order to make your cells more modular, `YLTableViewDataSource` allows your cells to have child view controllers. Simply have your cell implement the `YLTableViewChildViewControllerCell` protocol and set the `parentViewController` property on your data source. `YLTableViewDataSource` will take care of the rest. 104 | 105 | This is great when your cells have multiple buttons or actions that can push on view controllers. For example, the cell below is a UICollectionView owned by a UIViewController, which pushes on larger image views when the user taps on an image. 106 | 107 | ![Images cell](readme_images/photo_swipe_cell.png) 108 | 109 | Check out [YLExampleImagesCell.m](YLTableViewExample/Classes/ImagesView/YLExampleImagesCell.m) for an example. 110 | 111 | # Pull to Refresh 112 | 113 | YLTableView doesn't include a pull-to-refresh header, but makes it really easy to build your own. Simply subclass `YLRefreshHeaderView` and implement `setRefreshState:animated:`. Then you can set the `refreshHeader` property on `YLTableView` and add a listener for the `UIControlEventValueChanged` event on your refresh header. 114 | 115 | Check out [YLExampleRefreshHeader.m](YLTableViewExample/Classes/YLExampleRefreshHeader.m) for an example. 116 | 117 | ## License 118 | 119 | Copyright 2016 Yelp, Inc. 120 | 121 | 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 122 | 123 | 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](LICENSE.txt) for the specific language governing permissions and limitations under the License. 124 | -------------------------------------------------------------------------------- /Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /YLTableView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'YLTableView' 3 | s.version = '2.3.1' 4 | s.license = {:type => 'Apache 2', :file => 'LICENSE.txt'} 5 | s.summary = 'Yelp iOS table view framework' 6 | s.homepage = 'https://github.com/Yelp/YLTableView' 7 | s.authors = { 'Yelp iOS Team' => 'iphone@yelp.com' } 8 | s.source = { :git => 'https://git@github.com/Yelp/YLTableView.git', :tag => 'v' + s.version.to_s } 9 | 10 | s.ios.deployment_target = '10.0' 11 | 12 | s.public_header_files = 'Classes/**/*.h' 13 | s.source_files = 'Classes/**/*.{h,m}' 14 | end 15 | -------------------------------------------------------------------------------- /YLTableView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 78B38720228DCA2A0037A5C2 /* YLTableViewFramework.h in Headers */ = {isa = PBXBuildFile; fileRef = 78B3871E228DCA2A0037A5C2 /* YLTableViewFramework.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 78B38724228DCA350037A5C2 /* YLTableView.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51741C18B40E00F8528A /* YLTableView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 12 | 78B38725228DCBB30037A5C2 /* YLRefreshHeaderView.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51701C18B40E00F8528A /* YLRefreshHeaderView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 78B38726228DCBBE0037A5C2 /* YLRefreshHeaderViewSubclass.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51731C18B40E00F8528A /* YLRefreshHeaderViewSubclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | 78B38727228DCBC90037A5C2 /* YLTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51761C18B40E00F8528A /* YLTableViewCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 78B38728228DCBCF0037A5C2 /* YLTableViewChildViewControllerCell.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51791C18B40E00F8528A /* YLTableViewChildViewControllerCell.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 78B38729228DCBD30037A5C2 /* YLTableViewDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB517A1C18B40E00F8528A /* YLTableViewDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 78B3872A228DCBD70037A5C2 /* YLTableViewDataSourceSubclass.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB517C1C18B40E00F8528A /* YLTableViewDataSourceSubclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 78B3872B228DCBDC0037A5C2 /* YLTableViewHeaderFooterView.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB517D1C18B40E00F8528A /* YLTableViewHeaderFooterView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | 78B3872C228DCBE20037A5C2 /* YLTableViewPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB517F1C18B40E00F8528A /* YLTableViewPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 | 78B3872D228DCBE90037A5C2 /* YLRefreshHeaderViewPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51721C18B40E00F8528A /* YLRefreshHeaderViewPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 21 | 78B3872E228DCBF20037A5C2 /* YLTableViewSectionHeaderFooterLabelView.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51811C18B40E00F8528A /* YLTableViewSectionHeaderFooterLabelView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 22 | 78B3872F228DCBF80037A5C2 /* YLTableViewSectionHeaderFooterView.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51831C18B40E00F8528A /* YLTableViewSectionHeaderFooterView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | 78B38730228DCBFC0037A5C2 /* YLTableViewSectionHeaderFooterViewPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51851C18B40E00F8528A /* YLTableViewSectionHeaderFooterViewPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 24 | 78B38731228DCBFF0037A5C2 /* YLTableViewSectionHeaderFooterViewSubclass.h in Headers */ = {isa = PBXBuildFile; fileRef = A8CB51861C18B40E00F8528A /* YLTableViewSectionHeaderFooterViewSubclass.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25 | 78B38732228DCC3E0037A5C2 /* YLRefreshHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = A8CB51711C18B40E00F8528A /* YLRefreshHeaderView.m */; }; 26 | 78B38733228DCC4D0037A5C2 /* YLTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = A8CB51751C18B40E00F8528A /* YLTableView.m */; }; 27 | 78B38734228DCC4D0037A5C2 /* YLTableViewDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A8CB517B1C18B40E00F8528A /* YLTableViewDataSource.m */; }; 28 | 78B38735228DCC4D0037A5C2 /* YLTableViewHeaderFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = A8CB517E1C18B40E00F8528A /* YLTableViewHeaderFooterView.m */; }; 29 | 78B38736228DCC4D0037A5C2 /* YLTableViewSectionHeaderFooterLabelView.m in Sources */ = {isa = PBXBuildFile; fileRef = A8CB51821C18B40E00F8528A /* YLTableViewSectionHeaderFooterLabelView.m */; }; 30 | 78B38737228DCC4D0037A5C2 /* YLTableViewSectionHeaderFooterView.m in Sources */ = {isa = PBXBuildFile; fileRef = A8CB51841C18B40E00F8528A /* YLTableViewSectionHeaderFooterView.m */; }; 31 | 78B38741228DCCF40037A5C2 /* YLTableView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78B3871C228DCA2A0037A5C2 /* YLTableView.framework */; }; 32 | 78B38747228DCD140037A5C2 /* YLTableViewDataSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A6BADBB21C1A01C400501E0C /* YLTableViewDataSourceTests.m */; }; 33 | 78B38748228DCD180037A5C2 /* YLTableViewCellTestStub.m in Sources */ = {isa = PBXBuildFile; fileRef = A6BADBAD1C1A01B900501E0C /* YLTableViewCellTestStub.m */; }; 34 | 78B38749228DCD1B0037A5C2 /* YLTableViewDataSourceTestStub.m in Sources */ = {isa = PBXBuildFile; fileRef = A6BADBAF1C1A01B900501E0C /* YLTableViewDataSourceTestStub.m */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXContainerItemProxy section */ 38 | 78B38742228DCCF40037A5C2 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = A8CB515A1C18B38700F8528A /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = 78B3871B228DCA2A0037A5C2; 43 | remoteInfo = YLTableView; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | 78B3871C228DCA2A0037A5C2 /* YLTableView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YLTableView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 78B3871E228DCA2A0037A5C2 /* YLTableViewFramework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = YLTableViewFramework.h; sourceTree = ""; }; 50 | 78B3871F228DCA2A0037A5C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 78B3873C228DCCF40037A5C2 /* YLTableViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YLTableViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | A6BADBAC1C1A01B900501E0C /* YLTableViewCellTestStub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewCellTestStub.h; sourceTree = ""; }; 53 | A6BADBAD1C1A01B900501E0C /* YLTableViewCellTestStub.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTableViewCellTestStub.m; sourceTree = ""; }; 54 | A6BADBAE1C1A01B900501E0C /* YLTableViewDataSourceTestStub.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewDataSourceTestStub.h; sourceTree = ""; }; 55 | A6BADBAF1C1A01B900501E0C /* YLTableViewDataSourceTestStub.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTableViewDataSourceTestStub.m; sourceTree = ""; }; 56 | A6BADBB21C1A01C400501E0C /* YLTableViewDataSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTableViewDataSourceTests.m; sourceTree = ""; }; 57 | A8CB51701C18B40E00F8528A /* YLRefreshHeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLRefreshHeaderView.h; sourceTree = ""; }; 58 | A8CB51711C18B40E00F8528A /* YLRefreshHeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLRefreshHeaderView.m; sourceTree = ""; }; 59 | A8CB51721C18B40E00F8528A /* YLRefreshHeaderViewPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLRefreshHeaderViewPrivate.h; sourceTree = ""; }; 60 | A8CB51731C18B40E00F8528A /* YLRefreshHeaderViewSubclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLRefreshHeaderViewSubclass.h; sourceTree = ""; }; 61 | A8CB51741C18B40E00F8528A /* YLTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableView.h; sourceTree = ""; }; 62 | A8CB51751C18B40E00F8528A /* YLTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTableView.m; sourceTree = ""; }; 63 | A8CB51761C18B40E00F8528A /* YLTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewCell.h; sourceTree = ""; }; 64 | A8CB51791C18B40E00F8528A /* YLTableViewChildViewControllerCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewChildViewControllerCell.h; sourceTree = ""; }; 65 | A8CB517A1C18B40E00F8528A /* YLTableViewDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewDataSource.h; sourceTree = ""; }; 66 | A8CB517B1C18B40E00F8528A /* YLTableViewDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTableViewDataSource.m; sourceTree = ""; }; 67 | A8CB517C1C18B40E00F8528A /* YLTableViewDataSourceSubclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewDataSourceSubclass.h; sourceTree = ""; }; 68 | A8CB517D1C18B40E00F8528A /* YLTableViewHeaderFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewHeaderFooterView.h; sourceTree = ""; }; 69 | A8CB517E1C18B40E00F8528A /* YLTableViewHeaderFooterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTableViewHeaderFooterView.m; sourceTree = ""; }; 70 | A8CB517F1C18B40E00F8528A /* YLTableViewPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewPrivate.h; sourceTree = ""; }; 71 | A8CB51811C18B40E00F8528A /* YLTableViewSectionHeaderFooterLabelView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewSectionHeaderFooterLabelView.h; sourceTree = ""; }; 72 | A8CB51821C18B40E00F8528A /* YLTableViewSectionHeaderFooterLabelView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTableViewSectionHeaderFooterLabelView.m; sourceTree = ""; }; 73 | A8CB51831C18B40E00F8528A /* YLTableViewSectionHeaderFooterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewSectionHeaderFooterView.h; sourceTree = ""; }; 74 | A8CB51841C18B40E00F8528A /* YLTableViewSectionHeaderFooterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTableViewSectionHeaderFooterView.m; sourceTree = ""; }; 75 | A8CB51851C18B40E00F8528A /* YLTableViewSectionHeaderFooterViewPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewSectionHeaderFooterViewPrivate.h; sourceTree = ""; }; 76 | A8CB51861C18B40E00F8528A /* YLTableViewSectionHeaderFooterViewSubclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTableViewSectionHeaderFooterViewSubclass.h; sourceTree = ""; }; 77 | A8CB51B41C19EDE200F8528A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 78 | /* End PBXFileReference section */ 79 | 80 | /* Begin PBXFrameworksBuildPhase section */ 81 | 78B38719228DCA2A0037A5C2 /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | 78B38739228DCCF40037A5C2 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | 78B38741228DCCF40037A5C2 /* YLTableView.framework in Frameworks */, 93 | ); 94 | runOnlyForDeploymentPostprocessing = 0; 95 | }; 96 | /* End PBXFrameworksBuildPhase section */ 97 | 98 | /* Begin PBXGroup section */ 99 | 78B3871D228DCA2A0037A5C2 /* Resources */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 78B3871F228DCA2A0037A5C2 /* Info.plist */, 103 | ); 104 | path = Resources; 105 | sourceTree = ""; 106 | }; 107 | A8CB51591C18B38700F8528A = { 108 | isa = PBXGroup; 109 | children = ( 110 | A8CB516E1C18B40E00F8528A /* Classes */, 111 | A8CB51B11C19EDE200F8528A /* YLTableViewTests */, 112 | 78B3871D228DCA2A0037A5C2 /* Resources */, 113 | A8CB51631C18B38700F8528A /* Products */, 114 | ); 115 | indentWidth = 2; 116 | sourceTree = ""; 117 | tabWidth = 2; 118 | }; 119 | A8CB51631C18B38700F8528A /* Products */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 78B3871C228DCA2A0037A5C2 /* YLTableView.framework */, 123 | 78B3873C228DCCF40037A5C2 /* YLTableViewTests.xctest */, 124 | ); 125 | name = Products; 126 | sourceTree = ""; 127 | }; 128 | A8CB516E1C18B40E00F8528A /* Classes */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | A8CB516F1C18B40E00F8528A /* YLRefreshHeaderView */, 132 | A8CB51741C18B40E00F8528A /* YLTableView.h */, 133 | A8CB51751C18B40E00F8528A /* YLTableView.m */, 134 | A8CB51761C18B40E00F8528A /* YLTableViewCell.h */, 135 | A8CB51791C18B40E00F8528A /* YLTableViewChildViewControllerCell.h */, 136 | A8CB517A1C18B40E00F8528A /* YLTableViewDataSource.h */, 137 | A8CB517B1C18B40E00F8528A /* YLTableViewDataSource.m */, 138 | A8CB517C1C18B40E00F8528A /* YLTableViewDataSourceSubclass.h */, 139 | 78B3871E228DCA2A0037A5C2 /* YLTableViewFramework.h */, 140 | A8CB517D1C18B40E00F8528A /* YLTableViewHeaderFooterView.h */, 141 | A8CB517E1C18B40E00F8528A /* YLTableViewHeaderFooterView.m */, 142 | A8CB517F1C18B40E00F8528A /* YLTableViewPrivate.h */, 143 | A8CB51801C18B40E00F8528A /* YLTableViewSectionHeaderFooterView */, 144 | ); 145 | path = Classes; 146 | sourceTree = ""; 147 | }; 148 | A8CB516F1C18B40E00F8528A /* YLRefreshHeaderView */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | A8CB51701C18B40E00F8528A /* YLRefreshHeaderView.h */, 152 | A8CB51711C18B40E00F8528A /* YLRefreshHeaderView.m */, 153 | A8CB51721C18B40E00F8528A /* YLRefreshHeaderViewPrivate.h */, 154 | A8CB51731C18B40E00F8528A /* YLRefreshHeaderViewSubclass.h */, 155 | ); 156 | path = YLRefreshHeaderView; 157 | sourceTree = ""; 158 | }; 159 | A8CB51801C18B40E00F8528A /* YLTableViewSectionHeaderFooterView */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | A8CB51811C18B40E00F8528A /* YLTableViewSectionHeaderFooterLabelView.h */, 163 | A8CB51821C18B40E00F8528A /* YLTableViewSectionHeaderFooterLabelView.m */, 164 | A8CB51831C18B40E00F8528A /* YLTableViewSectionHeaderFooterView.h */, 165 | A8CB51841C18B40E00F8528A /* YLTableViewSectionHeaderFooterView.m */, 166 | A8CB51851C18B40E00F8528A /* YLTableViewSectionHeaderFooterViewPrivate.h */, 167 | A8CB51861C18B40E00F8528A /* YLTableViewSectionHeaderFooterViewSubclass.h */, 168 | ); 169 | path = YLTableViewSectionHeaderFooterView; 170 | sourceTree = ""; 171 | }; 172 | A8CB51B11C19EDE200F8528A /* YLTableViewTests */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | A6BADBB21C1A01C400501E0C /* YLTableViewDataSourceTests.m */, 176 | A6BADBAC1C1A01B900501E0C /* YLTableViewCellTestStub.h */, 177 | A6BADBAD1C1A01B900501E0C /* YLTableViewCellTestStub.m */, 178 | A6BADBAE1C1A01B900501E0C /* YLTableViewDataSourceTestStub.h */, 179 | A6BADBAF1C1A01B900501E0C /* YLTableViewDataSourceTestStub.m */, 180 | A8CB51B41C19EDE200F8528A /* Info.plist */, 181 | ); 182 | path = YLTableViewTests; 183 | sourceTree = ""; 184 | }; 185 | /* End PBXGroup section */ 186 | 187 | /* Begin PBXHeadersBuildPhase section */ 188 | 78B38717228DCA2A0037A5C2 /* Headers */ = { 189 | isa = PBXHeadersBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | 78B3872F228DCBF80037A5C2 /* YLTableViewSectionHeaderFooterView.h in Headers */, 193 | 78B3872A228DCBD70037A5C2 /* YLTableViewDataSourceSubclass.h in Headers */, 194 | 78B38727228DCBC90037A5C2 /* YLTableViewCell.h in Headers */, 195 | 78B38720228DCA2A0037A5C2 /* YLTableViewFramework.h in Headers */, 196 | 78B3872D228DCBE90037A5C2 /* YLRefreshHeaderViewPrivate.h in Headers */, 197 | 78B38724228DCA350037A5C2 /* YLTableView.h in Headers */, 198 | 78B38725228DCBB30037A5C2 /* YLRefreshHeaderView.h in Headers */, 199 | 78B38726228DCBBE0037A5C2 /* YLRefreshHeaderViewSubclass.h in Headers */, 200 | 78B3872E228DCBF20037A5C2 /* YLTableViewSectionHeaderFooterLabelView.h in Headers */, 201 | 78B38728228DCBCF0037A5C2 /* YLTableViewChildViewControllerCell.h in Headers */, 202 | 78B3872C228DCBE20037A5C2 /* YLTableViewPrivate.h in Headers */, 203 | 78B38731228DCBFF0037A5C2 /* YLTableViewSectionHeaderFooterViewSubclass.h in Headers */, 204 | 78B3872B228DCBDC0037A5C2 /* YLTableViewHeaderFooterView.h in Headers */, 205 | 78B38730228DCBFC0037A5C2 /* YLTableViewSectionHeaderFooterViewPrivate.h in Headers */, 206 | 78B38729228DCBD30037A5C2 /* YLTableViewDataSource.h in Headers */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | /* End PBXHeadersBuildPhase section */ 211 | 212 | /* Begin PBXNativeTarget section */ 213 | 78B3871B228DCA2A0037A5C2 /* YLTableView */ = { 214 | isa = PBXNativeTarget; 215 | buildConfigurationList = 78B38721228DCA2A0037A5C2 /* Build configuration list for PBXNativeTarget "YLTableView" */; 216 | buildPhases = ( 217 | 78B38717228DCA2A0037A5C2 /* Headers */, 218 | 78B38718228DCA2A0037A5C2 /* Sources */, 219 | 78B38719228DCA2A0037A5C2 /* Frameworks */, 220 | 78B3871A228DCA2A0037A5C2 /* Resources */, 221 | ); 222 | buildRules = ( 223 | ); 224 | dependencies = ( 225 | ); 226 | name = YLTableView; 227 | productName = YLTableView; 228 | productReference = 78B3871C228DCA2A0037A5C2 /* YLTableView.framework */; 229 | productType = "com.apple.product-type.framework"; 230 | }; 231 | 78B3873B228DCCF40037A5C2 /* YLTableViewTests */ = { 232 | isa = PBXNativeTarget; 233 | buildConfigurationList = 78B38744228DCCF40037A5C2 /* Build configuration list for PBXNativeTarget "YLTableViewTests" */; 234 | buildPhases = ( 235 | 78B38738228DCCF40037A5C2 /* Sources */, 236 | 78B38739228DCCF40037A5C2 /* Frameworks */, 237 | 78B3873A228DCCF40037A5C2 /* Resources */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | 78B38743228DCCF40037A5C2 /* PBXTargetDependency */, 243 | ); 244 | name = YLTableViewTests; 245 | productName = YLTableViewTests; 246 | productReference = 78B3873C228DCCF40037A5C2 /* YLTableViewTests.xctest */; 247 | productType = "com.apple.product-type.bundle.unit-test"; 248 | }; 249 | /* End PBXNativeTarget section */ 250 | 251 | /* Begin PBXProject section */ 252 | A8CB515A1C18B38700F8528A /* Project object */ = { 253 | isa = PBXProject; 254 | attributes = { 255 | LastUpgradeCheck = 0930; 256 | ORGANIZATIONNAME = Yelp; 257 | TargetAttributes = { 258 | 78B3871B228DCA2A0037A5C2 = { 259 | CreatedOnToolsVersion = 10.1; 260 | ProvisioningStyle = Automatic; 261 | }; 262 | 78B3873B228DCCF40037A5C2 = { 263 | CreatedOnToolsVersion = 10.1; 264 | ProvisioningStyle = Automatic; 265 | }; 266 | }; 267 | }; 268 | buildConfigurationList = A8CB515D1C18B38700F8528A /* Build configuration list for PBXProject "YLTableView" */; 269 | compatibilityVersion = "Xcode 3.2"; 270 | developmentRegion = English; 271 | hasScannedForEncodings = 0; 272 | knownRegions = ( 273 | en, 274 | ); 275 | mainGroup = A8CB51591C18B38700F8528A; 276 | productRefGroup = A8CB51631C18B38700F8528A /* Products */; 277 | projectDirPath = ""; 278 | projectRoot = ""; 279 | targets = ( 280 | 78B3871B228DCA2A0037A5C2 /* YLTableView */, 281 | 78B3873B228DCCF40037A5C2 /* YLTableViewTests */, 282 | ); 283 | }; 284 | /* End PBXProject section */ 285 | 286 | /* Begin PBXResourcesBuildPhase section */ 287 | 78B3871A228DCA2A0037A5C2 /* Resources */ = { 288 | isa = PBXResourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | 78B3873A228DCCF40037A5C2 /* Resources */ = { 295 | isa = PBXResourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | /* End PBXResourcesBuildPhase section */ 302 | 303 | /* Begin PBXSourcesBuildPhase section */ 304 | 78B38718228DCA2A0037A5C2 /* Sources */ = { 305 | isa = PBXSourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | 78B38733228DCC4D0037A5C2 /* YLTableView.m in Sources */, 309 | 78B38734228DCC4D0037A5C2 /* YLTableViewDataSource.m in Sources */, 310 | 78B38732228DCC3E0037A5C2 /* YLRefreshHeaderView.m in Sources */, 311 | 78B38735228DCC4D0037A5C2 /* YLTableViewHeaderFooterView.m in Sources */, 312 | 78B38736228DCC4D0037A5C2 /* YLTableViewSectionHeaderFooterLabelView.m in Sources */, 313 | 78B38737228DCC4D0037A5C2 /* YLTableViewSectionHeaderFooterView.m in Sources */, 314 | ); 315 | runOnlyForDeploymentPostprocessing = 0; 316 | }; 317 | 78B38738228DCCF40037A5C2 /* Sources */ = { 318 | isa = PBXSourcesBuildPhase; 319 | buildActionMask = 2147483647; 320 | files = ( 321 | 78B38748228DCD180037A5C2 /* YLTableViewCellTestStub.m in Sources */, 322 | 78B38749228DCD1B0037A5C2 /* YLTableViewDataSourceTestStub.m in Sources */, 323 | 78B38747228DCD140037A5C2 /* YLTableViewDataSourceTests.m in Sources */, 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | /* End PBXSourcesBuildPhase section */ 328 | 329 | /* Begin PBXTargetDependency section */ 330 | 78B38743228DCCF40037A5C2 /* PBXTargetDependency */ = { 331 | isa = PBXTargetDependency; 332 | target = 78B3871B228DCA2A0037A5C2 /* YLTableView */; 333 | targetProxy = 78B38742228DCCF40037A5C2 /* PBXContainerItemProxy */; 334 | }; 335 | /* End PBXTargetDependency section */ 336 | 337 | /* Begin XCBuildConfiguration section */ 338 | 78B38722228DCA2A0037A5C2 /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | CLANG_ANALYZER_NONNULL = YES; 342 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 343 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 344 | CLANG_ENABLE_OBJC_WEAK = YES; 345 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 346 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 347 | CODE_SIGN_IDENTITY = "iPhone Developer"; 348 | CODE_SIGN_STYLE = Automatic; 349 | CURRENT_PROJECT_VERSION = 1; 350 | DEFINES_MODULE = YES; 351 | DYLIB_COMPATIBILITY_VERSION = 1; 352 | DYLIB_CURRENT_VERSION = 1; 353 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 354 | GCC_C_LANGUAGE_STANDARD = gnu11; 355 | INFOPLIST_FILE = Resources/Info.plist; 356 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 357 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 358 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 359 | MODULEMAP_FILE = "$(PROJECT_DIR)/module.modulemap"; 360 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 361 | MTL_FAST_MATH = YES; 362 | PRODUCT_BUNDLE_IDENTIFIER = com.yelp.YLTableView; 363 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 364 | SKIP_INSTALL = YES; 365 | TARGETED_DEVICE_FAMILY = "1,2"; 366 | VERSIONING_SYSTEM = "apple-generic"; 367 | VERSION_INFO_PREFIX = ""; 368 | }; 369 | name = Debug; 370 | }; 371 | 78B38723228DCA2A0037A5C2 /* Release */ = { 372 | isa = XCBuildConfiguration; 373 | buildSettings = { 374 | CLANG_ANALYZER_NONNULL = YES; 375 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 376 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 377 | CLANG_ENABLE_OBJC_WEAK = YES; 378 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 379 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 380 | CODE_SIGN_IDENTITY = "iPhone Developer"; 381 | CODE_SIGN_STYLE = Automatic; 382 | CURRENT_PROJECT_VERSION = 1; 383 | DEFINES_MODULE = YES; 384 | DYLIB_COMPATIBILITY_VERSION = 1; 385 | DYLIB_CURRENT_VERSION = 1; 386 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 387 | GCC_C_LANGUAGE_STANDARD = gnu11; 388 | INFOPLIST_FILE = Resources/Info.plist; 389 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 390 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 391 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 392 | MODULEMAP_FILE = "$(PROJECT_DIR)/module.modulemap"; 393 | MTL_FAST_MATH = YES; 394 | PRODUCT_BUNDLE_IDENTIFIER = com.yelp.YLTableView; 395 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 396 | SKIP_INSTALL = YES; 397 | TARGETED_DEVICE_FAMILY = "1,2"; 398 | VERSIONING_SYSTEM = "apple-generic"; 399 | VERSION_INFO_PREFIX = ""; 400 | }; 401 | name = Release; 402 | }; 403 | 78B38745228DCCF40037A5C2 /* Debug */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | CLANG_ANALYZER_NONNULL = YES; 407 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 408 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 409 | CLANG_ENABLE_OBJC_WEAK = YES; 410 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 411 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 412 | CODE_SIGN_IDENTITY = "iPhone Developer"; 413 | CODE_SIGN_STYLE = Automatic; 414 | GCC_C_LANGUAGE_STANDARD = gnu11; 415 | INFOPLIST_FILE = YLTableViewTests/Info.plist; 416 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 418 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 419 | MTL_FAST_MATH = YES; 420 | PRODUCT_BUNDLE_IDENTIFIER = com.yelp.YLTableViewTests; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | TARGETED_DEVICE_FAMILY = "1,2"; 423 | }; 424 | name = Debug; 425 | }; 426 | 78B38746228DCCF40037A5C2 /* Release */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | CLANG_ANALYZER_NONNULL = YES; 430 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 431 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 432 | CLANG_ENABLE_OBJC_WEAK = YES; 433 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 434 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 435 | CODE_SIGN_IDENTITY = "iPhone Developer"; 436 | CODE_SIGN_STYLE = Automatic; 437 | GCC_C_LANGUAGE_STANDARD = gnu11; 438 | INFOPLIST_FILE = YLTableViewTests/Info.plist; 439 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 440 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 441 | MTL_FAST_MATH = YES; 442 | PRODUCT_BUNDLE_IDENTIFIER = com.yelp.YLTableViewTests; 443 | PRODUCT_NAME = "$(TARGET_NAME)"; 444 | TARGETED_DEVICE_FAMILY = "1,2"; 445 | }; 446 | name = Release; 447 | }; 448 | A8CB51691C18B38700F8528A /* Debug */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | ALWAYS_SEARCH_USER_PATHS = NO; 452 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 453 | CLANG_CXX_LIBRARY = "libc++"; 454 | CLANG_ENABLE_MODULES = YES; 455 | CLANG_ENABLE_OBJC_ARC = YES; 456 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 457 | CLANG_WARN_BOOL_CONVERSION = YES; 458 | CLANG_WARN_COMMA = YES; 459 | CLANG_WARN_CONSTANT_CONVERSION = YES; 460 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 461 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 462 | CLANG_WARN_EMPTY_BODY = YES; 463 | CLANG_WARN_ENUM_CONVERSION = YES; 464 | CLANG_WARN_INFINITE_RECURSION = YES; 465 | CLANG_WARN_INT_CONVERSION = YES; 466 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 467 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 468 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 469 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 470 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 471 | CLANG_WARN_STRICT_PROTOTYPES = YES; 472 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 473 | CLANG_WARN_UNREACHABLE_CODE = YES; 474 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 475 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 476 | COPY_PHASE_STRIP = NO; 477 | DEBUG_INFORMATION_FORMAT = dwarf; 478 | ENABLE_STRICT_OBJC_MSGSEND = YES; 479 | ENABLE_TESTABILITY = YES; 480 | GCC_C_LANGUAGE_STANDARD = gnu99; 481 | GCC_DYNAMIC_NO_PIC = NO; 482 | GCC_NO_COMMON_BLOCKS = YES; 483 | GCC_OPTIMIZATION_LEVEL = 0; 484 | GCC_PREPROCESSOR_DEFINITIONS = ( 485 | "DEBUG=1", 486 | "$(inherited)", 487 | ); 488 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 489 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 490 | GCC_WARN_UNDECLARED_SELECTOR = YES; 491 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 492 | GCC_WARN_UNUSED_FUNCTION = YES; 493 | GCC_WARN_UNUSED_VARIABLE = YES; 494 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 495 | MTL_ENABLE_DEBUG_INFO = YES; 496 | ONLY_ACTIVE_ARCH = YES; 497 | SDKROOT = iphoneos; 498 | }; 499 | name = Debug; 500 | }; 501 | A8CB516A1C18B38700F8528A /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | ALWAYS_SEARCH_USER_PATHS = NO; 505 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 506 | CLANG_CXX_LIBRARY = "libc++"; 507 | CLANG_ENABLE_MODULES = YES; 508 | CLANG_ENABLE_OBJC_ARC = YES; 509 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 510 | CLANG_WARN_BOOL_CONVERSION = YES; 511 | CLANG_WARN_COMMA = YES; 512 | CLANG_WARN_CONSTANT_CONVERSION = YES; 513 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 514 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 515 | CLANG_WARN_EMPTY_BODY = YES; 516 | CLANG_WARN_ENUM_CONVERSION = YES; 517 | CLANG_WARN_INFINITE_RECURSION = YES; 518 | CLANG_WARN_INT_CONVERSION = YES; 519 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 520 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 521 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 522 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 523 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 524 | CLANG_WARN_STRICT_PROTOTYPES = YES; 525 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 526 | CLANG_WARN_UNREACHABLE_CODE = YES; 527 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 528 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 529 | COPY_PHASE_STRIP = NO; 530 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 531 | ENABLE_NS_ASSERTIONS = NO; 532 | ENABLE_STRICT_OBJC_MSGSEND = YES; 533 | GCC_C_LANGUAGE_STANDARD = gnu99; 534 | GCC_NO_COMMON_BLOCKS = YES; 535 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 536 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 537 | GCC_WARN_UNDECLARED_SELECTOR = YES; 538 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 539 | GCC_WARN_UNUSED_FUNCTION = YES; 540 | GCC_WARN_UNUSED_VARIABLE = YES; 541 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 542 | MTL_ENABLE_DEBUG_INFO = NO; 543 | SDKROOT = iphoneos; 544 | VALIDATE_PRODUCT = YES; 545 | }; 546 | name = Release; 547 | }; 548 | /* End XCBuildConfiguration section */ 549 | 550 | /* Begin XCConfigurationList section */ 551 | 78B38721228DCA2A0037A5C2 /* Build configuration list for PBXNativeTarget "YLTableView" */ = { 552 | isa = XCConfigurationList; 553 | buildConfigurations = ( 554 | 78B38722228DCA2A0037A5C2 /* Debug */, 555 | 78B38723228DCA2A0037A5C2 /* Release */, 556 | ); 557 | defaultConfigurationIsVisible = 0; 558 | defaultConfigurationName = Release; 559 | }; 560 | 78B38744228DCCF40037A5C2 /* Build configuration list for PBXNativeTarget "YLTableViewTests" */ = { 561 | isa = XCConfigurationList; 562 | buildConfigurations = ( 563 | 78B38745228DCCF40037A5C2 /* Debug */, 564 | 78B38746228DCCF40037A5C2 /* Release */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | A8CB515D1C18B38700F8528A /* Build configuration list for PBXProject "YLTableView" */ = { 570 | isa = XCConfigurationList; 571 | buildConfigurations = ( 572 | A8CB51691C18B38700F8528A /* Debug */, 573 | A8CB516A1C18B38700F8528A /* Release */, 574 | ); 575 | defaultConfigurationIsVisible = 0; 576 | defaultConfigurationName = Release; 577 | }; 578 | /* End XCConfigurationList section */ 579 | }; 580 | rootObject = A8CB515A1C18B38700F8528A /* Project object */; 581 | } 582 | -------------------------------------------------------------------------------- /YLTableView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YLTableView.xcodeproj/xcshareddata/xcschemes/YLTableView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /YLTableView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YLTableView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImageControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImageControl.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /*! 12 | Simple UIControl subclass that displays an image at its full size. 13 | */ 14 | @interface YLExampleImageControl : UIControl 15 | 16 | @property (strong, nonatomic) UIImage *image; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImageControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImageControl.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleImageControl.h" 10 | 11 | @interface YLExampleImageControl () 12 | @property (strong, nonatomic) UIImageView *imageView; 13 | @end 14 | 15 | @implementation YLExampleImageControl 16 | 17 | - (instancetype)initWithFrame:(CGRect)frame { 18 | if (self = [super initWithFrame:frame]) { 19 | _imageView = [[UIImageView alloc] init]; 20 | _imageView.contentMode = UIViewContentModeScaleAspectFit; 21 | _imageView.translatesAutoresizingMaskIntoConstraints = NO; 22 | [self addSubview:_imageView]; 23 | 24 | [self _installConstraints]; 25 | } 26 | return self; 27 | } 28 | 29 | - (void)_installConstraints { 30 | NSDictionary *views = NSDictionaryOfVariableBindings(_imageView); 31 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_imageView]|" options:0 metrics:nil views:views]]; 32 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageView]|" options:0 metrics:nil views:views]]; 33 | } 34 | 35 | - (void)setImage:(UIImage *)image { 36 | self.imageView.image = image; 37 | } 38 | 39 | - (UIImage *)image { 40 | return self.imageView.image; 41 | } 42 | 43 | - (void)setHighlighted:(BOOL)highlighted { 44 | [super setHighlighted:highlighted]; 45 | self.backgroundColor = highlighted ? [UIColor grayColor] : [UIColor clearColor]; 46 | } 47 | 48 | - (void)setSelected:(BOOL)selected { 49 | [super setSelected:selected]; 50 | self.backgroundColor = selected ? [UIColor grayColor] : [UIColor clearColor]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImagesCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImagesCell.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YLExampleImagesCell : UITableViewCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImagesCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImagesCell.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleImagesCell.h" 10 | 11 | #import "YLExampleImagesCellModel.h" 12 | #import "YLExampleImagesViewController.h" 13 | 14 | #import 15 | 16 | @interface YLExampleImagesCell () 17 | @property (strong, nonatomic) YLExampleImagesViewController *imagesViewController; 18 | @end 19 | 20 | @implementation YLExampleImagesCell 21 | 22 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 23 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 24 | _imagesViewController = [[YLExampleImagesViewController alloc] init]; 25 | _imagesViewController.view.translatesAutoresizingMaskIntoConstraints = NO; 26 | [self.contentView addSubview:_imagesViewController.view]; 27 | 28 | [self _installConstraints]; 29 | } 30 | return self; 31 | } 32 | 33 | #pragma mark Layout 34 | 35 | - (void)_installConstraints { 36 | // Make sure we display the controller's view 37 | NSDictionary *views = @{ @"view": self.imagesViewController.view }; 38 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:views]]; 39 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:views]]; 40 | } 41 | 42 | #pragma mark YLTableViewCell protocol 43 | 44 | - (void)setModel:(id)model { 45 | NSAssert([model isKindOfClass:[YLExampleImagesCellModel class]], @"Must use %@ with %@", NSStringFromClass([YLExampleImagesCellModel class]), NSStringFromClass([self class])); 46 | // Pass the images through to the controller 47 | YLExampleImagesCellModel *imagesModel = (YLExampleImagesCellModel *)model; 48 | self.imagesViewController.imageOne = imagesModel.imageOne; 49 | self.imagesViewController.imageTwo = imagesModel.imageTwo; 50 | self.imagesViewController.imageThree = imagesModel.imageThree; 51 | } 52 | 53 | + (CGFloat)estimatedRowHeight { 54 | return 44.0; 55 | } 56 | 57 | #pragma mark YLTableViewChildViewControllerCell 58 | 59 | - (YLExampleImagesViewController *)childViewController { 60 | // By returning a child view controller here, it'll be added as a child of YLExampleViewController and recieve view events + access to the navigation controller. 61 | return self.imagesViewController; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImagesCellModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImagesCellModel.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class UIImage; 12 | 13 | @interface YLExampleImagesCellModel : NSObject 14 | 15 | @property (readonly, strong, nonatomic) UIImage *imageOne; 16 | @property (readonly, strong, nonatomic) UIImage *imageTwo; 17 | @property (readonly, strong, nonatomic) UIImage *imageThree; 18 | 19 | + (instancetype)modelWithImageOne:(UIImage *)imageOne imageTwo:(UIImage *)imageTwo imageThree:(UIImage *)imageThree; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImagesCellModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImagesCellModel.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleImagesCellModel.h" 10 | 11 | @interface YLExampleImagesCellModel () 12 | @property (strong, nonatomic) UIImage *imageOne; 13 | @property (strong, nonatomic) UIImage *imageTwo; 14 | @property (strong, nonatomic) UIImage *imageThree; 15 | @end 16 | 17 | @implementation YLExampleImagesCellModel 18 | 19 | + (instancetype)modelWithImageOne:(UIImage *)imageOne imageTwo:(UIImage *)imageTwo imageThree:(UIImage *)imageThree { 20 | YLExampleImagesCellModel *model = [[self alloc] init]; 21 | model.imageOne = imageOne; 22 | model.imageTwo = imageTwo; 23 | model.imageThree = imageThree; 24 | return model; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImagesView.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImageCellView.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class YLExampleImagesView; 12 | 13 | @protocol YLExampleImageCellViewDelegate 14 | - (void)exampleImageCellView:(YLExampleImagesView *)exampleImageCellView didSelectImage:(UIImage *)image; 15 | @end 16 | 17 | @interface YLExampleImagesView : UIView 18 | 19 | @property (strong, nonatomic) UIImage *imageOne; 20 | @property (strong, nonatomic) UIImage *imageTwo; 21 | @property (strong, nonatomic) UIImage *imageThree; 22 | 23 | @property (weak, nonatomic) id delegate; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImagesView.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImageCellView.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleImagesView.h" 10 | 11 | #import "YLExampleImageControl.h" 12 | 13 | @interface YLExampleImagesView () 14 | @property (strong, nonatomic) YLExampleImageControl *imageControlOne; 15 | @property (strong, nonatomic) YLExampleImageControl *imageControlTwo; 16 | @property (strong, nonatomic) YLExampleImageControl *imageControlThree; 17 | @end 18 | 19 | @implementation YLExampleImagesView 20 | 21 | - (instancetype)initWithFrame:(CGRect)frame { 22 | if (self = [super initWithFrame:frame]) { 23 | self.backgroundColor = [UIColor whiteColor]; 24 | 25 | _imageControlOne = [[YLExampleImageControl alloc] init]; 26 | _imageControlOne.translatesAutoresizingMaskIntoConstraints = NO; 27 | [_imageControlOne addTarget:self action:@selector(_controlSelected:) forControlEvents:UIControlEventTouchUpInside]; 28 | [self addSubview:_imageControlOne]; 29 | 30 | _imageControlTwo = [[YLExampleImageControl alloc] init]; 31 | _imageControlTwo.translatesAutoresizingMaskIntoConstraints = NO; 32 | [_imageControlTwo addTarget:self action:@selector(_controlSelected:) forControlEvents:UIControlEventTouchUpInside]; 33 | [self addSubview:_imageControlTwo]; 34 | 35 | _imageControlThree = [[YLExampleImageControl alloc] init]; 36 | _imageControlThree.translatesAutoresizingMaskIntoConstraints = NO; 37 | [_imageControlThree addTarget:self action:@selector(_controlSelected:) forControlEvents:UIControlEventTouchUpInside]; 38 | [self addSubview:_imageControlThree]; 39 | 40 | [self _installConstraints]; 41 | } 42 | return self; 43 | } 44 | 45 | - (void)_installConstraints { 46 | NSDictionary *views = NSDictionaryOfVariableBindings(_imageControlOne, _imageControlTwo, _imageControlThree); 47 | NSDictionary *metrics = @{ @"margin": @10, @"imageDimension": @60, }; 48 | 49 | // Space outh the 3 images horizontally, centering the middle one. 50 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-margin-[_imageControlOne(imageDimension)]-(>=margin)-[_imageControlTwo(imageDimension)]-(>=margin)-[_imageControlThree(imageDimension)]-margin-|" options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views]]; 51 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.imageControlTwo attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]]; 52 | 53 | // Center the images vertically 54 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[_imageControlOne(imageDimension)]-(>=margin)-|" options:0 metrics:metrics views:views]]; 55 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[_imageControlTwo(imageDimension)]-(>=margin)-|" options:0 metrics:metrics views:views]]; 56 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[_imageControlThree(imageDimension)]-(>=margin)-|" options:0 metrics:metrics views:views]]; 57 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.imageControlOne attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]]; 58 | } 59 | 60 | - (void)_controlSelected:(YLExampleImageControl *)control { 61 | id delegate = self.delegate; 62 | [delegate exampleImageCellView:self didSelectImage:control.image]; 63 | } 64 | 65 | #pragma mark Public methods 66 | 67 | - (UIImage *)imageOne { 68 | return self.imageControlOne.image; 69 | } 70 | 71 | - (void)setImageOne:(UIImage *)imageOne { 72 | self.imageControlOne.image = imageOne; 73 | } 74 | 75 | - (UIImage *)imageTwo { 76 | return self.imageControlTwo.image; 77 | } 78 | 79 | - (void)setImageTwo:(UIImage *)imageTwo { 80 | self.imageControlTwo.image = imageTwo; 81 | } 82 | 83 | - (UIImage *)imageThree { 84 | return self.imageControlThree.image; 85 | } 86 | 87 | - (void)setImageThree:(UIImage *)imageThree { 88 | self.imageControlThree.image = imageThree; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImagesViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImageCellViewController.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! View controller that displays 3 images and opens a detail view when one is selected. In this project, it is used in a cell and pushed onto the navigation controller by itself. 12 | @interface YLExampleImagesViewController : UIViewController 13 | 14 | @property (strong, nonatomic) UIImage *imageOne; 15 | @property (strong, nonatomic) UIImage *imageTwo; 16 | @property (strong, nonatomic) UIImage *imageThree; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleImagesViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleImageCellViewController.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleImagesViewController.h" 10 | 11 | #import "YLExampleImagesView.h" 12 | #import "YLExampleLargeImageViewController.h" 13 | 14 | @interface YLExampleImagesViewController () 15 | @property (strong, nonatomic) YLExampleImagesView *imageCellView; 16 | @end 17 | 18 | @implementation YLExampleImagesViewController 19 | 20 | - (void)loadView { 21 | [super loadView]; 22 | self.view = self.imageCellView; 23 | } 24 | 25 | - (YLExampleImagesView *)imageCellView { 26 | if (!_imageCellView) { 27 | _imageCellView = [[YLExampleImagesView alloc] init]; 28 | _imageCellView.delegate = self; 29 | } 30 | return _imageCellView; 31 | } 32 | 33 | #pragma mark YLExampleImageCellViewDelegate 34 | 35 | - (void)exampleImageCellView:(YLExampleImagesView *)exampleImageCellView didSelectImage:(UIImage *)image { 36 | [self.navigationController pushViewController:[[YLExampleLargeImageViewController alloc] initWithImage:image] animated:YES]; 37 | } 38 | 39 | #pragma mark Public methods 40 | 41 | - (UIImage *)imageOne { 42 | return self.imageCellView.imageOne; 43 | } 44 | 45 | - (void)setImageOne:(UIImage *)imageOne { 46 | self.imageCellView.imageOne = imageOne; 47 | } 48 | 49 | - (UIImage *)imageTwo { 50 | return self.imageCellView.imageTwo; 51 | } 52 | 53 | - (void)setImageTwo:(UIImage *)imageTwo { 54 | self.imageCellView.imageTwo = imageTwo; 55 | } 56 | 57 | - (UIImage *)imageThree { 58 | return self.imageCellView.imageThree; 59 | } 60 | 61 | - (void)setImageThree:(UIImage *)imageThree { 62 | self.imageCellView.imageThree = imageThree; 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleLargeImageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleLargeImageViewController.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Simple view controller that will display a full-screen image. 12 | @interface YLExampleLargeImageViewController : UIViewController 13 | 14 | - (instancetype)initWithImage:(UIImage *)image; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/ImagesView/YLExampleLargeImageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleLargeImageViewController.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleLargeImageViewController.h" 10 | 11 | @interface YLExampleLargeImageViewController () 12 | @property (strong, nonatomic) UIImage *image; 13 | @end 14 | 15 | @implementation YLExampleLargeImageViewController 16 | 17 | - (instancetype)initWithImage:(UIImage *)image { 18 | if (self = [super init]) { 19 | _image = image; 20 | } 21 | return self; 22 | } 23 | 24 | - (void)loadView { 25 | [super loadView]; 26 | UIImageView *imageView = [[UIImageView alloc] init]; 27 | imageView.image = self.image; 28 | imageView.contentMode = UIViewContentModeScaleAspectFit; 29 | imageView.backgroundColor = [UIColor whiteColor]; 30 | self.view = imageView; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YLExampleAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleAppDelegate.h" 10 | #import "YLExampleViewController.h" 11 | 12 | @implementation YLExampleAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 15 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 16 | [self.window setRootViewController:[[UINavigationController alloc] initWithRootViewController:[[YLExampleViewController alloc] init]]]; 17 | [self.window makeKeyAndVisible]; 18 | return YES; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleRefreshHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleRefreshHEader.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/3/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLRefreshHeaderView.h" 10 | 11 | @interface YLExampleRefreshHeader : YLRefreshHeaderView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleRefreshHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleRefreshHEader.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/3/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleRefreshHeader.h" 10 | #import 11 | 12 | @interface YLExampleRefreshHeader () 13 | @property (strong, nonatomic) UILabel *label; 14 | @end 15 | 16 | @implementation YLExampleRefreshHeader 17 | 18 | - (instancetype)initWithFrame:(CGRect)frame { 19 | if (self = [super initWithFrame:frame]) { 20 | self.backgroundColor = [UIColor whiteColor]; 21 | 22 | _label = [[UILabel alloc] init]; 23 | _label.translatesAutoresizingMaskIntoConstraints = NO; 24 | _label.textAlignment = NSTextAlignmentCenter; 25 | [self addSubview:_label]; 26 | 27 | NSDictionary *views = NSDictionaryOfVariableBindings(_label); 28 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_label]|" options:0 metrics:nil views:views]]; 29 | [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_label]-10-|" options:0 metrics:nil views:views]]; 30 | 31 | [self setRefreshState:YLRefreshHeaderViewStateNormal]; 32 | } 33 | return self; 34 | } 35 | 36 | #pragma mark YLRefreshHeader subclass methods 37 | 38 | - (void)setRefreshState:(YLRefreshHeaderViewState)refreshState animated:(BOOL)animated { 39 | [super setRefreshState:refreshState animated:animated]; 40 | 41 | switch (refreshState) { 42 | case YLRefreshHeaderViewStateNormal: 43 | self.label.text = @"Pull to refresh..."; 44 | self.label.textColor = [UIColor redColor]; 45 | break; 46 | case YLRefreshHeaderViewStateClosing: 47 | case YLRefreshHeaderViewStateRefreshing: 48 | self.label.text = @"Refreshing..."; 49 | self.label.textColor = [UIColor blackColor]; 50 | break; 51 | case YLRefreshHeaderViewStateReadyToRefresh: 52 | self.label.text = @"Release to refresh"; 53 | self.label.textColor = [UIColor greenColor]; 54 | break; 55 | } 56 | } 57 | 58 | - (CGFloat)pullAmountToRefresh { 59 | return 40.0; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleTableViewDataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleTableViewDataSource.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewDataSource.h" 10 | 11 | @class YLExampleTableViewDataSource; 12 | @class YLExampleImagesCellModel; 13 | 14 | @protocol YLExampleTableViewDataSourceDelegate 15 | - (void)exampleTableViewDataSource:(YLExampleTableViewDataSource *)dataSource didSelectImagesModel:(YLExampleImagesCellModel *)imagesModel; 16 | @end 17 | 18 | @interface YLExampleTableViewDataSource : YLTableViewDataSource 19 | 20 | @property (weak, nonatomic) id delegate; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleTableViewDataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleTableViewDataSource.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleTableViewDataSource.h" 10 | 11 | #import "YLExampleTextCell.h" 12 | #import "YLExampleImagesCell.h" 13 | #import "YLExampleImagesCellModel.h" 14 | 15 | #import 16 | #import 17 | 18 | typedef NS_ENUM(NSInteger, YLExampleTableViewSection) { 19 | YLExampleTableViewSectionText = 0, 20 | YLExampleTableViewSectionImages, 21 | }; 22 | 23 | @interface YLExampleTableViewDataSource () 24 | @property (copy, nonatomic) NSArray *sectionModels; 25 | @end 26 | 27 | @implementation YLExampleTableViewDataSource 28 | 29 | - (instancetype)init { 30 | if (self = [super init]) { 31 | // We usually use a model factory to create the models, but this is a small enough example that it can be done inline. 32 | _sectionModels = @[ 33 | @[@"cell one", @"cell two", @"really long text cell that will stretch to multiple lines, even on a wide phone like the iPhone 6+."], 34 | @[[YLExampleImagesCellModel modelWithImageOne:[UIImage imageNamed:@"ninja_hammy.png"] imageTwo:[UIImage imageNamed:@"star.png"] imageThree:[UIImage imageNamed:@"yelp_logo.png"]]], 35 | ]; 36 | } 37 | return self; 38 | } 39 | 40 | #pragma mark YLTableViewDataSource subclass 41 | 42 | - (NSString *)tableView:(UITableView *)tableView reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath { 43 | id model = [self tableView:tableView modelForCellAtIndexPath:indexPath]; 44 | if ([model isKindOfClass:[NSString class]]) { 45 | return NSStringFromClass([YLExampleTextCell class]); 46 | } else if ([model isKindOfClass:[YLExampleImagesCellModel class]]) { 47 | return NSStringFromClass([YLExampleImagesCell class]); 48 | } 49 | NSAssert(NO, @"No reuse identifier for model %@", model); 50 | return nil; 51 | } 52 | 53 | - (id)tableView:(UITableView *)tableView modelForCellAtIndexPath:(NSIndexPath *)indexPath { 54 | // The model returned here must work with the cell specified above. 55 | return self.sectionModels[indexPath.section][indexPath.row]; 56 | } 57 | 58 | - (NSString *)tableView:(UITableView *)tableView reuseIdentifierForHeaderInSection:(NSUInteger)section { 59 | return NSStringFromClass([YLTableViewSectionHeaderFooterLabelView class]); 60 | } 61 | 62 | - (void)tableView:(UITableView *)tableView configureHeader:(YLTableViewSectionHeaderFooterView *)headerView forSection:(NSUInteger)section { 63 | [super tableView:tableView configureHeader:headerView forSection:section]; 64 | YLTableViewSectionHeaderFooterLabelView *labelHeaderView = (YLTableViewSectionHeaderFooterLabelView *)headerView; 65 | switch ((YLExampleTableViewSection)section) { 66 | case YLExampleTableViewSectionText: 67 | labelHeaderView.text = @"Text Cells"; 68 | break; 69 | case YLExampleTableViewSectionImages: 70 | labelHeaderView.text = @"Child View Controller Cells"; 71 | break; 72 | } 73 | } 74 | 75 | #pragma mark YLTableViewDataSource 76 | 77 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 78 | return self.sectionModels.count; 79 | } 80 | 81 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 82 | return [self.sectionModels[section] count]; 83 | } 84 | 85 | #pragma mark YLTableViewDelegate 86 | 87 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 88 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 89 | id model = [self tableView:tableView modelForCellAtIndexPath:indexPath]; 90 | if ([model isKindOfClass:[YLExampleImagesCellModel class]]) { 91 | id delegate = self.delegate; 92 | [delegate exampleTableViewDataSource:self didSelectImagesModel:(YLExampleImagesCellModel *)model]; 93 | } 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleTextCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleTextCell.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YLExampleTextCell : UITableViewCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleTextCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLExampleTextCell.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleTextCell.h" 10 | 11 | #import 12 | 13 | @interface YLExampleTextCell () 14 | @property (strong, nonatomic) UILabel *mainTextLabel; 15 | @end 16 | 17 | @implementation YLExampleTextCell 18 | 19 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 20 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 21 | // Apple's default label doesn't support multi-line content very well, so we'll add our own label 22 | _mainTextLabel = [[UILabel alloc] init]; 23 | _mainTextLabel.font = [UIFont systemFontOfSize:16.0]; 24 | _mainTextLabel.translatesAutoresizingMaskIntoConstraints = NO; 25 | _mainTextLabel.numberOfLines = 0; 26 | [self.contentView addSubview:_mainTextLabel]; 27 | 28 | [self _installConstraints]; 29 | } 30 | return self; 31 | } 32 | 33 | #pragma mark Layout 34 | 35 | - (void)_installConstraints { 36 | NSDictionary *views = NSDictionaryOfVariableBindings(_mainTextLabel); 37 | NSDictionary *metrics = @{ @"margin": @10, }; 38 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-margin-[_mainTextLabel]-margin-|" options:0 metrics:metrics views:views]]; 39 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-margin-[_mainTextLabel]-margin-|" options:0 metrics:metrics views:views]]; 40 | } 41 | 42 | #pragma mark YLTableViewCell protocol 43 | 44 | - (void)setModel:(id)model { 45 | NSAssert([model isKindOfClass:[NSString class]], @"Must use %@ with %@", NSStringFromClass([NSString class]), NSStringFromClass([self class])); 46 | self.mainTextLabel.text = (NSString *)model; 47 | } 48 | 49 | + (CGFloat)estimatedRowHeight { 50 | return 44.0; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YLExampleViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/YLExampleViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLExampleViewController.h" 10 | 11 | #import "YLExampleImagesCell.h" 12 | #import "YLExampleImagesCellModel.h" 13 | #import "YLExampleImagesViewController.h" 14 | #import "YLExampleTableViewDataSource.h" 15 | #import "YLExampleTextCell.h" 16 | #import "YLExampleRefreshHeader.h" 17 | 18 | #import 19 | #import 20 | 21 | @interface YLExampleViewController () 22 | @property (strong, nonatomic) YLTableView *tableView; 23 | @property (strong, nonatomic) YLExampleTableViewDataSource *dataSource; 24 | @end 25 | 26 | @implementation YLExampleViewController 27 | 28 | - (instancetype)init { 29 | if (self = [super init]) { 30 | // If using a pull to refresh header, you can't have extended layout on the top edge 31 | self.edgesForExtendedLayout = UIRectEdgeNone; 32 | 33 | _dataSource = [[YLExampleTableViewDataSource alloc] init]; 34 | _dataSource.parentViewController = self; 35 | _dataSource.delegate = self; 36 | } 37 | return self; 38 | } 39 | 40 | - (void)dealloc { 41 | _tableView.delegate = nil; 42 | _tableView.dataSource = nil; 43 | } 44 | 45 | - (void)loadView { 46 | [super loadView]; 47 | self.view = self.tableView; 48 | } 49 | 50 | - (YLTableView *)tableView { 51 | if (!_tableView) { 52 | _tableView = [[YLTableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; 53 | // YLTableViewDataSource has to be the delegate and the dataSource of the table view. 54 | _tableView.dataSource = _dataSource; 55 | _tableView.delegate = _dataSource; 56 | 57 | // Set up a refresh header 58 | _tableView.refreshHeaderView = [[YLExampleRefreshHeader alloc] init]; 59 | [_tableView.refreshHeaderView addTarget:self action:@selector(_shouldRefresh) forControlEvents:UIControlEventValueChanged]; 60 | 61 | // Registering cells and reuse identifiers 62 | [_tableView registerClass:[YLExampleTextCell class] forCellReuseIdentifier:NSStringFromClass([YLExampleTextCell class])]; 63 | [_tableView registerClass:[YLExampleImagesCell class] forCellReuseIdentifier:NSStringFromClass([YLExampleImagesCell class])]; 64 | [_tableView registerClass:[YLTableViewSectionHeaderFooterLabelView class] forHeaderFooterViewReuseIdentifier:NSStringFromClass([YLTableViewSectionHeaderFooterLabelView class])]; 65 | } 66 | return _tableView; 67 | } 68 | 69 | #pragma mark UIControlEvents 70 | 71 | - (void)_shouldRefresh { 72 | self.tableView.state = YLTableViewStateRefreshing; 73 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 74 | self.tableView.state = YLTableViewStateLoaded; 75 | }); 76 | } 77 | 78 | #pragma mark YLExampleTableViewDataSourceDelegate 79 | 80 | - (void)exampleTableViewDataSource:(YLExampleTableViewDataSource *)dataSource didSelectImagesModel:(YLExampleImagesCellModel *)imagesModel { 81 | // This is the same view controller as used in YLExampleImagesCell, just presented normall on the stack. 82 | YLExampleImagesViewController *viewController = [[YLExampleImagesViewController alloc] init]; 83 | viewController.imageOne = imagesModel.imageOne; 84 | viewController.imageTwo = imagesModel.imageTwo; 85 | viewController.imageThree = imagesModel.imageThree; 86 | [self.navigationController pushViewController:viewController animated:YES]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /YLTableViewExample/Classes/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // YLTableViewExample 4 | // 5 | // Created by Mason Glidden on 6/2/15. 6 | // Copyright (c) 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "YLExampleAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([YLExampleAppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /YLTableViewExample/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'cocoapods', '0.39.0' 4 | -------------------------------------------------------------------------------- /YLTableViewExample/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (4.2.5) 5 | i18n (~> 0.7) 6 | json (~> 1.7, >= 1.7.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | claide (0.9.1) 11 | cocoapods (0.39.0) 12 | activesupport (>= 4.0.2) 13 | claide (~> 0.9.1) 14 | cocoapods-core (= 0.39.0) 15 | cocoapods-downloader (~> 0.9.3) 16 | cocoapods-plugins (~> 0.4.2) 17 | cocoapods-search (~> 0.1.0) 18 | cocoapods-stats (~> 0.6.2) 19 | cocoapods-trunk (~> 0.6.4) 20 | cocoapods-try (~> 0.5.1) 21 | colored (~> 1.2) 22 | escape (~> 0.0.4) 23 | molinillo (~> 0.4.0) 24 | nap (~> 1.0) 25 | xcodeproj (~> 0.28.2) 26 | cocoapods-core (0.39.0) 27 | activesupport (>= 4.0.2) 28 | fuzzy_match (~> 2.0.4) 29 | nap (~> 1.0) 30 | cocoapods-downloader (0.9.3) 31 | cocoapods-plugins (0.4.2) 32 | nap 33 | cocoapods-search (0.1.0) 34 | cocoapods-stats (0.6.2) 35 | cocoapods-trunk (0.6.4) 36 | nap (>= 0.8, < 2.0) 37 | netrc (= 0.7.8) 38 | cocoapods-try (0.5.1) 39 | colored (1.2) 40 | escape (0.0.4) 41 | fuzzy_match (2.0.4) 42 | i18n (0.7.0) 43 | json (1.8.3) 44 | minitest (5.8.3) 45 | molinillo (0.4.1) 46 | nap (1.0.0) 47 | netrc (0.7.8) 48 | thread_safe (0.3.5) 49 | tzinfo (1.2.2) 50 | thread_safe (~> 0.1) 51 | xcodeproj (0.28.2) 52 | activesupport (>= 3) 53 | claide (~> 0.9.1) 54 | colored (~> 1.2) 55 | 56 | PLATFORMS 57 | ruby 58 | 59 | DEPENDENCIES 60 | cocoapods (= 0.39.0) 61 | 62 | BUNDLED WITH 63 | 1.10.6 64 | -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "Default@2x.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "Default-568h@2x.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "ipad", 23 | "extent" : "full-screen", 24 | "minimum-system-version" : "7.0", 25 | "scale" : "1x" 26 | }, 27 | { 28 | "orientation" : "landscape", 29 | "idiom" : "ipad", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "scale" : "1x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "ipad", 37 | "extent" : "full-screen", 38 | "minimum-system-version" : "7.0", 39 | "scale" : "2x" 40 | }, 41 | { 42 | "orientation" : "landscape", 43 | "idiom" : "ipad", 44 | "extent" : "full-screen", 45 | "minimum-system-version" : "7.0", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/YLTableViewExample/OtherSources/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/LaunchImage.launchimage/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/YLTableViewExample/OtherSources/Images.xcassets/LaunchImage.launchimage/Default@2x.png -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/ninja_hammy.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "ninja_hammy.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "ninja_hammy@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/ninja_hammy.imageset/ninja_hammy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/YLTableViewExample/OtherSources/Images.xcassets/ninja_hammy.imageset/ninja_hammy.png -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/ninja_hammy.imageset/ninja_hammy@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/YLTableViewExample/OtherSources/Images.xcassets/ninja_hammy.imageset/ninja_hammy@2x.png -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "star.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "star@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/star.imageset/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/YLTableViewExample/OtherSources/Images.xcassets/star.imageset/star.png -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/star.imageset/star@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/YLTableViewExample/OtherSources/Images.xcassets/star.imageset/star@2x.png -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/yelp_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x", 6 | "filename" : "yelp_logo.png" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x", 11 | "filename" : "yelp_logo@2x.png" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/yelp_logo.imageset/yelp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/YLTableViewExample/OtherSources/Images.xcassets/yelp_logo.imageset/yelp_logo.png -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Images.xcassets/yelp_logo.imageset/yelp_logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/YLTableViewExample/OtherSources/Images.xcassets/yelp_logo.imageset/yelp_logo@2x.png -------------------------------------------------------------------------------- /YLTableViewExample/OtherSources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /YLTableViewExample/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '11.0' 2 | 3 | source 'https://github.com/CocoaPods/Specs.git' 4 | 5 | target 'YLTableViewExample' do 6 | pod 'YLTableView', :path => '../' 7 | end 8 | -------------------------------------------------------------------------------- /YLTableViewExample/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - YLTableView (2.2.1) 3 | 4 | DEPENDENCIES: 5 | - YLTableView (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | YLTableView: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | YLTableView: 134983d82783d9b22249530bfc1b9c0574d9194c 13 | 14 | PODFILE CHECKSUM: 4ea5408dfddfc6e0ad62d2bf8b36dd2075083d18 15 | 16 | COCOAPODS: 1.5.3 17 | -------------------------------------------------------------------------------- /YLTableViewExample/YLTableViewExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 626A638E4001FFFBBBE6F8EC /* libPods-YLTableViewExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01F6D1E0D7808379FB183B79 /* libPods-YLTableViewExample.a */; }; 11 | BBB3E4141B2255AE00051EF1 /* YLExampleImageControl.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E3FA1B2255AE00051EF1 /* YLExampleImageControl.m */; }; 12 | BBB3E4151B2255AE00051EF1 /* YLExampleImagesCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E3FC1B2255AE00051EF1 /* YLExampleImagesCell.m */; }; 13 | BBB3E4161B2255AE00051EF1 /* YLExampleImagesCellModel.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E3FE1B2255AE00051EF1 /* YLExampleImagesCellModel.m */; }; 14 | BBB3E4171B2255AE00051EF1 /* YLExampleImagesView.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E4001B2255AE00051EF1 /* YLExampleImagesView.m */; }; 15 | BBB3E4181B2255AE00051EF1 /* YLExampleImagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E4021B2255AE00051EF1 /* YLExampleImagesViewController.m */; }; 16 | BBB3E4191B2255AE00051EF1 /* YLExampleLargeImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E4041B2255AE00051EF1 /* YLExampleLargeImageViewController.m */; }; 17 | BBB3E41A1B2255AE00051EF1 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = BBB3E4051B2255AE00051EF1 /* LaunchScreen.xib */; }; 18 | BBB3E41B1B2255AE00051EF1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E4061B2255AE00051EF1 /* main.m */; }; 19 | BBB3E41C1B2255AE00051EF1 /* YLExampleAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E4081B2255AE00051EF1 /* YLExampleAppDelegate.m */; }; 20 | BBB3E41D1B2255AE00051EF1 /* YLExampleRefreshHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E40A1B2255AE00051EF1 /* YLExampleRefreshHeader.m */; }; 21 | BBB3E41E1B2255AE00051EF1 /* YLExampleTableViewDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E40C1B2255AE00051EF1 /* YLExampleTableViewDataSource.m */; }; 22 | BBB3E41F1B2255AE00051EF1 /* YLExampleTextCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E40E1B2255AE00051EF1 /* YLExampleTextCell.m */; }; 23 | BBB3E4201B2255AE00051EF1 /* YLExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BBB3E4101B2255AE00051EF1 /* YLExampleViewController.m */; }; 24 | BBB3E4211B2255AE00051EF1 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BBB3E4121B2255AE00051EF1 /* Images.xcassets */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 01F6D1E0D7808379FB183B79 /* libPods-YLTableViewExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-YLTableViewExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 77E0294F6EFAD61F32AF8B13 /* Pods-YLTableViewExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YLTableViewExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-YLTableViewExample/Pods-YLTableViewExample.release.xcconfig"; sourceTree = ""; }; 30 | 9C73FE2D338ACBD9DAFD01E5 /* Pods-YLTableViewExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-YLTableViewExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-YLTableViewExample/Pods-YLTableViewExample.debug.xcconfig"; sourceTree = ""; }; 31 | BBB3E3CE1B22555700051EF1 /* YLTableViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YLTableViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | BBB3E3F91B2255AE00051EF1 /* YLExampleImageControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleImageControl.h; sourceTree = ""; }; 33 | BBB3E3FA1B2255AE00051EF1 /* YLExampleImageControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleImageControl.m; sourceTree = ""; }; 34 | BBB3E3FB1B2255AE00051EF1 /* YLExampleImagesCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleImagesCell.h; sourceTree = ""; }; 35 | BBB3E3FC1B2255AE00051EF1 /* YLExampleImagesCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleImagesCell.m; sourceTree = ""; }; 36 | BBB3E3FD1B2255AE00051EF1 /* YLExampleImagesCellModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleImagesCellModel.h; sourceTree = ""; }; 37 | BBB3E3FE1B2255AE00051EF1 /* YLExampleImagesCellModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleImagesCellModel.m; sourceTree = ""; }; 38 | BBB3E3FF1B2255AE00051EF1 /* YLExampleImagesView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleImagesView.h; sourceTree = ""; }; 39 | BBB3E4001B2255AE00051EF1 /* YLExampleImagesView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleImagesView.m; sourceTree = ""; }; 40 | BBB3E4011B2255AE00051EF1 /* YLExampleImagesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleImagesViewController.h; sourceTree = ""; }; 41 | BBB3E4021B2255AE00051EF1 /* YLExampleImagesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleImagesViewController.m; sourceTree = ""; }; 42 | BBB3E4031B2255AE00051EF1 /* YLExampleLargeImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleLargeImageViewController.h; sourceTree = ""; }; 43 | BBB3E4041B2255AE00051EF1 /* YLExampleLargeImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleLargeImageViewController.m; sourceTree = ""; }; 44 | BBB3E4051B2255AE00051EF1 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; 45 | BBB3E4061B2255AE00051EF1 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 46 | BBB3E4071B2255AE00051EF1 /* YLExampleAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleAppDelegate.h; sourceTree = ""; }; 47 | BBB3E4081B2255AE00051EF1 /* YLExampleAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleAppDelegate.m; sourceTree = ""; }; 48 | BBB3E4091B2255AE00051EF1 /* YLExampleRefreshHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleRefreshHeader.h; sourceTree = ""; }; 49 | BBB3E40A1B2255AE00051EF1 /* YLExampleRefreshHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleRefreshHeader.m; sourceTree = ""; }; 50 | BBB3E40B1B2255AE00051EF1 /* YLExampleTableViewDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleTableViewDataSource.h; sourceTree = ""; }; 51 | BBB3E40C1B2255AE00051EF1 /* YLExampleTableViewDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleTableViewDataSource.m; sourceTree = ""; }; 52 | BBB3E40D1B2255AE00051EF1 /* YLExampleTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleTextCell.h; sourceTree = ""; }; 53 | BBB3E40E1B2255AE00051EF1 /* YLExampleTextCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleTextCell.m; sourceTree = ""; }; 54 | BBB3E40F1B2255AE00051EF1 /* YLExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLExampleViewController.h; sourceTree = ""; }; 55 | BBB3E4101B2255AE00051EF1 /* YLExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLExampleViewController.m; sourceTree = ""; }; 56 | BBB3E4121B2255AE00051EF1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 57 | BBB3E4131B2255AE00051EF1 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | BBB3E3CB1B22555700051EF1 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 626A638E4001FFFBBBE6F8EC /* libPods-YLTableViewExample.a in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | 3321E9458CADC439D1A97443 /* Frameworks */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 01F6D1E0D7808379FB183B79 /* libPods-YLTableViewExample.a */, 76 | ); 77 | name = Frameworks; 78 | sourceTree = ""; 79 | }; 80 | B4D8F43C23D5DD1B55C87030 /* Pods */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 9C73FE2D338ACBD9DAFD01E5 /* Pods-YLTableViewExample.debug.xcconfig */, 84 | 77E0294F6EFAD61F32AF8B13 /* Pods-YLTableViewExample.release.xcconfig */, 85 | ); 86 | name = Pods; 87 | sourceTree = ""; 88 | }; 89 | BBB3E3C51B22555700051EF1 = { 90 | isa = PBXGroup; 91 | children = ( 92 | BBB3E3F71B2255AE00051EF1 /* Classes */, 93 | BBB3E4111B2255AE00051EF1 /* OtherSources */, 94 | BBB3E3CF1B22555700051EF1 /* Products */, 95 | B4D8F43C23D5DD1B55C87030 /* Pods */, 96 | 3321E9458CADC439D1A97443 /* Frameworks */, 97 | ); 98 | indentWidth = 2; 99 | sourceTree = ""; 100 | tabWidth = 2; 101 | }; 102 | BBB3E3CF1B22555700051EF1 /* Products */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | BBB3E3CE1B22555700051EF1 /* YLTableViewExample.app */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | BBB3E3F71B2255AE00051EF1 /* Classes */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | BBB3E3F81B2255AE00051EF1 /* ImagesView */, 114 | BBB3E4051B2255AE00051EF1 /* LaunchScreen.xib */, 115 | BBB3E4061B2255AE00051EF1 /* main.m */, 116 | BBB3E4071B2255AE00051EF1 /* YLExampleAppDelegate.h */, 117 | BBB3E4081B2255AE00051EF1 /* YLExampleAppDelegate.m */, 118 | BBB3E4091B2255AE00051EF1 /* YLExampleRefreshHeader.h */, 119 | BBB3E40A1B2255AE00051EF1 /* YLExampleRefreshHeader.m */, 120 | BBB3E40B1B2255AE00051EF1 /* YLExampleTableViewDataSource.h */, 121 | BBB3E40C1B2255AE00051EF1 /* YLExampleTableViewDataSource.m */, 122 | BBB3E40D1B2255AE00051EF1 /* YLExampleTextCell.h */, 123 | BBB3E40E1B2255AE00051EF1 /* YLExampleTextCell.m */, 124 | BBB3E40F1B2255AE00051EF1 /* YLExampleViewController.h */, 125 | BBB3E4101B2255AE00051EF1 /* YLExampleViewController.m */, 126 | ); 127 | path = Classes; 128 | sourceTree = ""; 129 | }; 130 | BBB3E3F81B2255AE00051EF1 /* ImagesView */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | BBB3E3F91B2255AE00051EF1 /* YLExampleImageControl.h */, 134 | BBB3E3FA1B2255AE00051EF1 /* YLExampleImageControl.m */, 135 | BBB3E3FB1B2255AE00051EF1 /* YLExampleImagesCell.h */, 136 | BBB3E3FC1B2255AE00051EF1 /* YLExampleImagesCell.m */, 137 | BBB3E3FD1B2255AE00051EF1 /* YLExampleImagesCellModel.h */, 138 | BBB3E3FE1B2255AE00051EF1 /* YLExampleImagesCellModel.m */, 139 | BBB3E3FF1B2255AE00051EF1 /* YLExampleImagesView.h */, 140 | BBB3E4001B2255AE00051EF1 /* YLExampleImagesView.m */, 141 | BBB3E4011B2255AE00051EF1 /* YLExampleImagesViewController.h */, 142 | BBB3E4021B2255AE00051EF1 /* YLExampleImagesViewController.m */, 143 | BBB3E4031B2255AE00051EF1 /* YLExampleLargeImageViewController.h */, 144 | BBB3E4041B2255AE00051EF1 /* YLExampleLargeImageViewController.m */, 145 | ); 146 | path = ImagesView; 147 | sourceTree = ""; 148 | }; 149 | BBB3E4111B2255AE00051EF1 /* OtherSources */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | BBB3E4121B2255AE00051EF1 /* Images.xcassets */, 153 | BBB3E4131B2255AE00051EF1 /* Info.plist */, 154 | ); 155 | path = OtherSources; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXNativeTarget section */ 161 | BBB3E3CD1B22555700051EF1 /* YLTableViewExample */ = { 162 | isa = PBXNativeTarget; 163 | buildConfigurationList = BBB3E3F11B22555700051EF1 /* Build configuration list for PBXNativeTarget "YLTableViewExample" */; 164 | buildPhases = ( 165 | F1C8C5C216B1BF00944DC866 /* [CP] Check Pods Manifest.lock */, 166 | BBB3E3CA1B22555700051EF1 /* Sources */, 167 | BBB3E3CB1B22555700051EF1 /* Frameworks */, 168 | BBB3E3CC1B22555700051EF1 /* Resources */, 169 | ); 170 | buildRules = ( 171 | ); 172 | dependencies = ( 173 | ); 174 | name = YLTableViewExample; 175 | productName = YLTableViewExample; 176 | productReference = BBB3E3CE1B22555700051EF1 /* YLTableViewExample.app */; 177 | productType = "com.apple.product-type.application"; 178 | }; 179 | /* End PBXNativeTarget section */ 180 | 181 | /* Begin PBXProject section */ 182 | BBB3E3C61B22555700051EF1 /* Project object */ = { 183 | isa = PBXProject; 184 | attributes = { 185 | LastUpgradeCheck = 0920; 186 | ORGANIZATIONNAME = Yelp; 187 | TargetAttributes = { 188 | BBB3E3CD1B22555700051EF1 = { 189 | CreatedOnToolsVersion = 6.3.2; 190 | }; 191 | }; 192 | }; 193 | buildConfigurationList = BBB3E3C91B22555700051EF1 /* Build configuration list for PBXProject "YLTableViewExample" */; 194 | compatibilityVersion = "Xcode 3.2"; 195 | developmentRegion = English; 196 | hasScannedForEncodings = 0; 197 | knownRegions = ( 198 | en, 199 | Base, 200 | ); 201 | mainGroup = BBB3E3C51B22555700051EF1; 202 | productRefGroup = BBB3E3CF1B22555700051EF1 /* Products */; 203 | projectDirPath = ""; 204 | projectRoot = ""; 205 | targets = ( 206 | BBB3E3CD1B22555700051EF1 /* YLTableViewExample */, 207 | ); 208 | }; 209 | /* End PBXProject section */ 210 | 211 | /* Begin PBXResourcesBuildPhase section */ 212 | BBB3E3CC1B22555700051EF1 /* Resources */ = { 213 | isa = PBXResourcesBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | BBB3E41A1B2255AE00051EF1 /* LaunchScreen.xib in Resources */, 217 | BBB3E4211B2255AE00051EF1 /* Images.xcassets in Resources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXResourcesBuildPhase section */ 222 | 223 | /* Begin PBXShellScriptBuildPhase section */ 224 | F1C8C5C216B1BF00944DC866 /* [CP] Check Pods Manifest.lock */ = { 225 | isa = PBXShellScriptBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | ); 229 | inputPaths = ( 230 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 231 | "${PODS_ROOT}/Manifest.lock", 232 | ); 233 | name = "[CP] Check Pods Manifest.lock"; 234 | outputPaths = ( 235 | "$(DERIVED_FILE_DIR)/Pods-YLTableViewExample-checkManifestLockResult.txt", 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 240 | showEnvVarsInLog = 0; 241 | }; 242 | /* End PBXShellScriptBuildPhase section */ 243 | 244 | /* Begin PBXSourcesBuildPhase section */ 245 | BBB3E3CA1B22555700051EF1 /* Sources */ = { 246 | isa = PBXSourcesBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | BBB3E4201B2255AE00051EF1 /* YLExampleViewController.m in Sources */, 250 | BBB3E4191B2255AE00051EF1 /* YLExampleLargeImageViewController.m in Sources */, 251 | BBB3E4141B2255AE00051EF1 /* YLExampleImageControl.m in Sources */, 252 | BBB3E41F1B2255AE00051EF1 /* YLExampleTextCell.m in Sources */, 253 | BBB3E4151B2255AE00051EF1 /* YLExampleImagesCell.m in Sources */, 254 | BBB3E4181B2255AE00051EF1 /* YLExampleImagesViewController.m in Sources */, 255 | BBB3E41E1B2255AE00051EF1 /* YLExampleTableViewDataSource.m in Sources */, 256 | BBB3E4171B2255AE00051EF1 /* YLExampleImagesView.m in Sources */, 257 | BBB3E41B1B2255AE00051EF1 /* main.m in Sources */, 258 | BBB3E41D1B2255AE00051EF1 /* YLExampleRefreshHeader.m in Sources */, 259 | BBB3E41C1B2255AE00051EF1 /* YLExampleAppDelegate.m in Sources */, 260 | BBB3E4161B2255AE00051EF1 /* YLExampleImagesCellModel.m in Sources */, 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXSourcesBuildPhase section */ 265 | 266 | /* Begin XCBuildConfiguration section */ 267 | BBB3E3EF1B22555700051EF1 /* Debug */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ALWAYS_SEARCH_USER_PATHS = NO; 271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 272 | CLANG_CXX_LIBRARY = "libc++"; 273 | CLANG_ENABLE_MODULES = YES; 274 | CLANG_ENABLE_OBJC_ARC = YES; 275 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 276 | CLANG_WARN_BOOL_CONVERSION = YES; 277 | CLANG_WARN_COMMA = YES; 278 | CLANG_WARN_CONSTANT_CONVERSION = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_EMPTY_BODY = YES; 281 | CLANG_WARN_ENUM_CONVERSION = YES; 282 | CLANG_WARN_INFINITE_RECURSION = YES; 283 | CLANG_WARN_INT_CONVERSION = YES; 284 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 287 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 288 | CLANG_WARN_STRICT_PROTOTYPES = YES; 289 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 290 | CLANG_WARN_UNREACHABLE_CODE = YES; 291 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 292 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 293 | COPY_PHASE_STRIP = NO; 294 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 295 | ENABLE_STRICT_OBJC_MSGSEND = YES; 296 | ENABLE_TESTABILITY = YES; 297 | GCC_C_LANGUAGE_STANDARD = gnu99; 298 | GCC_DYNAMIC_NO_PIC = NO; 299 | GCC_NO_COMMON_BLOCKS = YES; 300 | GCC_OPTIMIZATION_LEVEL = 0; 301 | GCC_PREPROCESSOR_DEFINITIONS = ( 302 | "DEBUG=1", 303 | "$(inherited)", 304 | ); 305 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 313 | MTL_ENABLE_DEBUG_INFO = YES; 314 | ONLY_ACTIVE_ARCH = YES; 315 | SDKROOT = iphoneos; 316 | TARGETED_DEVICE_FAMILY = "1,2"; 317 | }; 318 | name = Debug; 319 | }; 320 | BBB3E3F01B22555700051EF1 /* Release */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_COMMA = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 341 | CLANG_WARN_STRICT_PROTOTYPES = YES; 342 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 343 | CLANG_WARN_UNREACHABLE_CODE = YES; 344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 345 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 346 | COPY_PHASE_STRIP = NO; 347 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 348 | ENABLE_NS_ASSERTIONS = NO; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | GCC_C_LANGUAGE_STANDARD = gnu99; 351 | GCC_NO_COMMON_BLOCKS = YES; 352 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 353 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 354 | GCC_WARN_UNDECLARED_SELECTOR = YES; 355 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 356 | GCC_WARN_UNUSED_FUNCTION = YES; 357 | GCC_WARN_UNUSED_VARIABLE = YES; 358 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 359 | MTL_ENABLE_DEBUG_INFO = NO; 360 | SDKROOT = iphoneos; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | VALIDATE_PRODUCT = YES; 363 | }; 364 | name = Release; 365 | }; 366 | BBB3E3F21B22555700051EF1 /* Debug */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = 9C73FE2D338ACBD9DAFD01E5 /* Pods-YLTableViewExample.debug.xcconfig */; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | INFOPLIST_FILE = OtherSources/Info.plist; 372 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 373 | PRODUCT_BUNDLE_IDENTIFIER = "Yelp.$(PRODUCT_NAME:rfc1034identifier)"; 374 | PRODUCT_NAME = "$(TARGET_NAME)"; 375 | }; 376 | name = Debug; 377 | }; 378 | BBB3E3F31B22555700051EF1 /* Release */ = { 379 | isa = XCBuildConfiguration; 380 | baseConfigurationReference = 77E0294F6EFAD61F32AF8B13 /* Pods-YLTableViewExample.release.xcconfig */; 381 | buildSettings = { 382 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 383 | INFOPLIST_FILE = OtherSources/Info.plist; 384 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 385 | PRODUCT_BUNDLE_IDENTIFIER = "Yelp.$(PRODUCT_NAME:rfc1034identifier)"; 386 | PRODUCT_NAME = "$(TARGET_NAME)"; 387 | }; 388 | name = Release; 389 | }; 390 | /* End XCBuildConfiguration section */ 391 | 392 | /* Begin XCConfigurationList section */ 393 | BBB3E3C91B22555700051EF1 /* Build configuration list for PBXProject "YLTableViewExample" */ = { 394 | isa = XCConfigurationList; 395 | buildConfigurations = ( 396 | BBB3E3EF1B22555700051EF1 /* Debug */, 397 | BBB3E3F01B22555700051EF1 /* Release */, 398 | ); 399 | defaultConfigurationIsVisible = 0; 400 | defaultConfigurationName = Release; 401 | }; 402 | BBB3E3F11B22555700051EF1 /* Build configuration list for PBXNativeTarget "YLTableViewExample" */ = { 403 | isa = XCConfigurationList; 404 | buildConfigurations = ( 405 | BBB3E3F21B22555700051EF1 /* Debug */, 406 | BBB3E3F31B22555700051EF1 /* Release */, 407 | ); 408 | defaultConfigurationIsVisible = 0; 409 | defaultConfigurationName = Release; 410 | }; 411 | /* End XCConfigurationList section */ 412 | }; 413 | rootObject = BBB3E3C61B22555700051EF1 /* Project object */; 414 | } 415 | -------------------------------------------------------------------------------- /YLTableViewExample/YLTableViewExample.xcodeproj/xcshareddata/xcschemes/YLTableViewExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 89 | 91 | 97 | 98 | 99 | 100 | 102 | 103 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /YLTableViewExample/YLTableViewExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /YLTableViewExample/YLTableViewExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /YLTableViewTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /YLTableViewTests/YLTableViewCellTestStub.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewCellTestStub.h 3 | // YLTableView 4 | // 5 | // Created by Ushhud Khalid on 12/9/15. 6 | // Copyright © 2015 Yelp. All rights reserved. 7 | // 8 | #import 9 | #import 10 | 11 | #import 12 | 13 | /** 14 | The height returned by calling estimatedRowHeight on this cell 15 | */ 16 | extern const CGFloat kYLTableViewCellStubHeight; 17 | 18 | /** 19 | YLTableViewCell stub used for testing purposes 20 | */ 21 | @interface YLTableViewCellTestStub : UITableViewCell 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /YLTableViewTests/YLTableViewCellTestStub.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewCellTestStub.m 3 | // YLTableView 4 | // 5 | // Created by Ushhud Khalid on 12/9/15. 6 | // Copyright © 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableViewCellTestStub.h" 10 | 11 | const CGFloat kYLTableViewCellStubHeight = 100.0; 12 | 13 | @implementation YLTableViewCellTestStub 14 | 15 | -(void)setModel:(id)model { } 16 | 17 | + (CGFloat)estimatedRowHeight { 18 | return kYLTableViewCellStubHeight; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /YLTableViewTests/YLTableViewDataSourceTestStub.h: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewDataSourceTestStub.h 3 | // YLTableView 4 | // 5 | // Created by Ushhud Khalid on 12/9/15. 6 | // Copyright © 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | extern const CGFloat kYLTableViewDataSourceTestStubOverriddenHeight; 13 | 14 | /** 15 | YLTableViewDataSource stub used for testing purposes 16 | */ 17 | @interface YLTableViewDataSourceTestStub : YLTableViewDataSource 18 | 19 | /** 20 | This is used to populate the table with cells at their respective index paths 21 | */ 22 | @property (copy, nonatomic) NSDictionary *reuseIdentifiers; 23 | @property (assign, nonatomic) BOOL shouldOverrideEstimatedRowHeight; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /YLTableViewTests/YLTableViewDataSourceTestStub.m: -------------------------------------------------------------------------------- 1 | 2 | // YLTableViewDataSourceTestStub.m 3 | // YLTableView 4 | // 5 | // Created by Ushhud Khalid on 12/9/15. 6 | // Copyright © 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | #import 13 | 14 | #import "YLTableViewDataSourceTestStub.h" 15 | 16 | const CGFloat kYLTableViewDataSourceTestStubOverriddenHeight = 200.0; 17 | 18 | @implementation YLTableViewDataSourceTestStub 19 | 20 | - (NSString *)tableView:(UITableView *)tableView reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath { 21 | return self.reuseIdentifiers[indexPath]; 22 | } 23 | 24 | - (id)tableView:(UITableView *)tableView modelForCellAtIndexPath:(NSIndexPath *)indexPath { 25 | return [[NSObject alloc] init]; 26 | } 27 | 28 | - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath withModel:(id)model { 29 | if (self.shouldOverrideEstimatedRowHeight) { 30 | return kYLTableViewDataSourceTestStubOverriddenHeight; 31 | } else { 32 | return [super tableView:tableView estimatedHeightForRowAtIndexPath:indexPath withModel:model]; 33 | } 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /YLTableViewTests/YLTableViewDataSourceTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // YLTableViewDataSourceTests.m 3 | // YLTableView 4 | // 5 | // Created by Ushhud Khalid on 12/9/15. 6 | // Copyright © 2015 Yelp. All rights reserved. 7 | // 8 | 9 | #import "YLTableView.h" 10 | #import "YLTableViewCell.h" 11 | #import "YLTableViewCellTestStub.h" 12 | #import "YLTableViewDataSource.h" 13 | #import "YLTableViewDataSourceTestStub.h" 14 | 15 | #import 16 | 17 | static NSString *const kCustomReuseIdentifier = @"NotAClass-ReuseId"; 18 | 19 | @interface YLTableViewDataSourceTests : XCTestCase 20 | 21 | @property (strong, nonatomic) YLTableView *tableView; 22 | @property (strong, nonatomic) YLTableViewDataSourceTestStub *dataSource; 23 | 24 | @end 25 | 26 | @implementation YLTableViewDataSourceTests 27 | 28 | #pragma mark - Setup/Teardown 29 | 30 | - (void)setUp { 31 | [super setUp]; 32 | 33 | _tableView = [[YLTableView alloc] init]; 34 | [_tableView registerClass:[YLTableViewCellTestStub class] 35 | forCellReuseIdentifier:NSStringFromClass([YLTableViewCellTestStub class])]; 36 | [_tableView registerClass:[YLTableViewCellTestStub class] 37 | forCellReuseIdentifier:kCustomReuseIdentifier]; 38 | 39 | _dataSource = [[YLTableViewDataSourceTestStub alloc] init]; 40 | _dataSource.reuseIdentifiers = [self _reuseIdentifiersForTableView]; 41 | 42 | _tableView.dataSource = _dataSource; 43 | _tableView.delegate = _dataSource; 44 | } 45 | 46 | #pragma mark - Helpers 47 | 48 | - (NSDictionary *)_reuseIdentifiersForTableView { 49 | return @{ 50 | [self _indexPathForClassNameReuseIdentifier]: NSStringFromClass([YLTableViewCellTestStub class]), 51 | [self _indexPathForCustomReuseIdentifier]: kCustomReuseIdentifier, 52 | }; 53 | } 54 | 55 | - (NSIndexPath *)_indexPathForClassNameReuseIdentifier { 56 | return [NSIndexPath indexPathForRow:0 inSection:0]; 57 | } 58 | 59 | - (NSIndexPath *)_indexPathForCustomReuseIdentifier { 60 | return [NSIndexPath indexPathForRow:1 inSection:0]; 61 | } 62 | 63 | #pragma mark - Tests 64 | 65 | - (void)testEstimatedRowHeightForClassNameReuseIdentifier { 66 | CGFloat height = [self.dataSource tableView:self.tableView estimatedHeightForRowAtIndexPath:[self _indexPathForClassNameReuseIdentifier]]; 67 | XCTAssertEqual(height, kYLTableViewCellStubHeight); 68 | } 69 | 70 | - (void)testEstimatedRowHeightForCustomReuseIdentifier { 71 | CGFloat height = [self.dataSource tableView:self.tableView estimatedHeightForRowAtIndexPath:[self _indexPathForCustomReuseIdentifier]]; 72 | XCTAssertEqual(height, kYLTableViewCellStubHeight); 73 | } 74 | 75 | - (void)testNonConformingCell { 76 | XCTAssertThrows([self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"]); 77 | } 78 | 79 | - (void)testEstimatedRowHeightOverride { 80 | self.dataSource.shouldOverrideEstimatedRowHeight = YES; 81 | CGFloat height = [self.dataSource tableView:self.tableView estimatedHeightForRowAtIndexPath:[self _indexPathForCustomReuseIdentifier]]; 82 | XCTAssertEqual(height, kYLTableViewDataSourceTestStubOverriddenHeight); 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /module.modulemap: -------------------------------------------------------------------------------- 1 | framework module YLTableView { 2 | umbrella header "YLTableViewFramework.h" 3 | } -------------------------------------------------------------------------------- /readme_images/photo_swipe_cell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yelp/YLTableView/57efdb500f91dd447594d4e2823a0d900850da24/readme_images/photo_swipe_cell.png --------------------------------------------------------------------------------