├── ERPageController ├── Assets.xcassets │ ├── Contents.json │ ├── editButtonImage.imageset │ │ ├── editButtonImage@2x.png │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── PageOneViewController.h ├── PageTwoViewController.h ├── ViewController.h ├── PageFourViewController.h ├── PageThreeViewController.h ├── AppDelegate.h ├── ERPageController │ ├── EditMenuControllers │ │ ├── SectionHeader.h │ │ ├── EditMenuCollectionViewCell.h │ │ ├── EditMenuCollectionViewCell.m │ │ ├── SectionHeader.m │ │ ├── ERSegmentMenuController.h │ │ ├── LXReorderableCollectionViewFlowLayout.h │ │ ├── EditMenuCollectionViewCell.xib │ │ ├── ERSegmentMenuController.m │ │ └── LXReorderableCollectionViewFlowLayout.m │ └── PageViewControllers │ │ ├── ERSegmentCollectionViewCell.h │ │ ├── ERSegmentCollectionViewCell.m │ │ ├── ERPageViewController.h │ │ ├── ERSegmentController.h │ │ ├── ERSegmentCollectionViewCell.xib │ │ ├── ERPageViewController.m │ │ └── ERSegmentController.m ├── main.m ├── PageFourViewController.m ├── PageOneViewController.m ├── PageTwoViewController.m ├── PageThreeViewController.m ├── Info.plist ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard ├── AppDelegate.m └── ViewController.m ├── ERPageController.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── huguangyu.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcuserdata │ └── huguangyu.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── ERPageController.xcscheme └── project.pbxproj ├── ERPageControllerTests ├── Info.plist └── ERPageControllerTests.m ├── ERPageControllerUITests ├── Info.plist └── ERPageControllerUITests.m └── README.md /ERPageController/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ERPageController/Assets.xcassets/editButtonImage.imageset/editButtonImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErHu1993/ERPageController/HEAD/ERPageController/Assets.xcassets/editButtonImage.imageset/editButtonImage@2x.png -------------------------------------------------------------------------------- /ERPageController.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ERPageController.xcodeproj/project.xcworkspace/xcuserdata/huguangyu.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ErHu1993/ERPageController/HEAD/ERPageController.xcodeproj/project.xcworkspace/xcuserdata/huguangyu.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ERPageController/PageOneViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PageOneViewController.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PageOneViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ERPageController/PageTwoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PageTwoViewController.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PageTwoViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ERPageController/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ERPageController 4 | // 5 | // Created by 胡广宇 on 2017/7/7. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ERPageController/PageFourViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PageFourViewController.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PageFourViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ERPageController/PageThreeViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PageThreeViewController.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PageThreeViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ERPageController/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ERPageController 4 | // 5 | // Created by 胡广宇 on 2017/7/7. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/SectionHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // SectionHeader.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/7/6. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SectionHeader : UICollectionReusableView 12 | 13 | @property (nonatomic, strong) UILabel *titleLabel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ERPageController/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ERPageController 4 | // 5 | // Created by 胡广宇 on 2017/7/7. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/EditMenuCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // EditMenuCollectionViewCell.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/7/4. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EditMenuCollectionViewCell : UICollectionViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UIButton *channelButton; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/PageViewControllers/ERSegmentCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ERSegmentCollectionViewCell.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ERSegmentCollectionViewCell : UICollectionViewCell 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/PageViewControllers/ERSegmentCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ERSegmentCollectionViewCell.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "ERSegmentCollectionViewCell.h" 10 | 11 | @implementation ERSegmentCollectionViewCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ERPageController/Assets.xcassets/editButtonImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "editButtonImage@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/EditMenuCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // EditMenuCollectionViewCell.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/7/4. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "EditMenuCollectionViewCell.h" 10 | 11 | @interface EditMenuCollectionViewCell () 12 | 13 | @property (weak, nonatomic) IBOutlet UIButton *deleteButton; 14 | 15 | @end 16 | 17 | @implementation EditMenuCollectionViewCell 18 | 19 | - (void)awakeFromNib { 20 | [super awakeFromNib]; 21 | } 22 | 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ERPageController.xcodeproj/xcuserdata/huguangyu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ERPageControllerTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ERPageControllerUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/SectionHeader.m: -------------------------------------------------------------------------------- 1 | // 2 | // SectionHeader.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/7/6. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "SectionHeader.h" 10 | 11 | @implementation SectionHeader 12 | 13 | - (UILabel *)titleLabel{ 14 | if (!_titleLabel) { 15 | _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 16 | _titleLabel.font = [UIFont systemFontOfSize:12]; 17 | _titleLabel.textColor = [UIColor blackColor]; 18 | [self addSubview:_titleLabel]; 19 | } 20 | return _titleLabel; 21 | } 22 | 23 | - (void)layoutSubviews{ 24 | [self.titleLabel setFrame:CGRectMake(10, (CGRectGetHeight(self.frame) - 15), CGRectGetWidth(self.frame), 15)]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ERPageController.xcodeproj/xcuserdata/huguangyu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ERPageController.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | D8F7C80B1F0F5E5B000DB0E7 16 | 17 | primary 18 | 19 | 20 | D8F7C8241F0F5E5B000DB0E7 21 | 22 | primary 23 | 24 | 25 | D8F7C82F1F0F5E5B000DB0E7 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ERPageController/PageFourViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PageFourViewController.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "PageFourViewController.h" 10 | 11 | @interface PageFourViewController () 12 | 13 | @end 14 | 15 | @implementation PageFourViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | self.view.backgroundColor = [UIColor blueColor]; 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ERPageController/PageOneViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PageOneViewController.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "PageOneViewController.h" 10 | 11 | @interface PageOneViewController () 12 | 13 | @end 14 | 15 | @implementation PageOneViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | self.view.backgroundColor = [UIColor whiteColor]; 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ERPageController/PageTwoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PageTwoViewController.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "PageTwoViewController.h" 10 | 11 | @interface PageTwoViewController () 12 | 13 | @end 14 | 15 | @implementation PageTwoViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | self.view.backgroundColor = [UIColor yellowColor]; 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ERPageController/PageThreeViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PageThreeViewController.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "PageThreeViewController.h" 10 | 11 | @interface PageThreeViewController () 12 | 13 | @end 14 | 15 | @implementation PageThreeViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | self.view.backgroundColor = [UIColor blackColor]; 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ERPageControllerTests/ERPageControllerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ERPageControllerTests.m 3 | // ERPageControllerTests 4 | // 5 | // Created by 胡广宇 on 2017/7/7. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ERPageControllerTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ERPageControllerTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ERPageControllerUITests/ERPageControllerUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ERPageControllerUITests.m 3 | // ERPageControllerUITests 4 | // 5 | // Created by 胡广宇 on 2017/7/7. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ERPageControllerUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ERPageControllerUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ERPageController/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 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 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/ERSegmentMenuController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ERSegmentMenuController.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/29. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ERSegmentMenuController; 12 | 13 | @protocol ERSegmentMenuControllerDataSource 14 | 15 | @required; 16 | 17 | /** 18 | 已经选择的频道列表信息 19 | 20 | @param segmentMenuController self 21 | @return 必须为字典型数组(必须包含一个KEY为@"name"的字符串) 22 | */ 23 | - (NSMutableArray *)selectedChannelLisInSegmentMenuController:(ERSegmentMenuController *)segmentMenuController; 24 | 25 | @optional; 26 | 27 | /** 28 | 未选择的频道列表信息 29 | 30 | @param segmentMenuController self 31 | @return 可以为nil ,若不为nil则必须为字典型数组(必须包含一个KEY为@"name"的字符串) 32 | */ 33 | - (NSMutableArray *)unSelectChannelListInSegmentMenuController:(ERSegmentMenuController *)segmentMenuController; 34 | 35 | 36 | /** 37 | 每组标题 38 | 39 | @param segmentMenuController segmentMenuController 40 | @param sectionHeaderLabel 组头Label 41 | @param section section 42 | @return title 43 | */ 44 | - (NSString *)segmentMenuController:(ERSegmentMenuController *)segmentMenuController sectionHeaderLabel:(UILabel *)sectionHeaderLabel titleForHeaderInSection:(NSInteger)section; 45 | 46 | @end 47 | 48 | @protocol ERSegmentMenuControllerDelegate 49 | 50 | @optional; 51 | 52 | - (void)displayChannelListDidChange; 53 | 54 | @end 55 | 56 | @interface ERSegmentMenuController : UIViewController 57 | 58 | @property (nonatomic, weak) id dataSource; 59 | 60 | @property (nonatomic, weak) id delegate; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /ERPageController/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ERPageController/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/PageViewControllers/ERPageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ERPageViewController.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ERPageViewController; 12 | 13 | @protocol ERPageViewControllerDataSource 14 | 15 | @required 16 | 17 | /** 18 | 返回子控制器总数,类似TableViewDataSource 19 | 20 | @param pageViewController self 21 | @return 子控制器总数 22 | */ 23 | - (NSInteger)numberOfControllersInPageViewController:(ERPageViewController *)pageViewController; 24 | 25 | 26 | /** 27 | 返回对应index的ViewController 28 | 29 | @param pageViewController self 30 | @param index 当前index 31 | @return 需要展示的ViewController 32 | */ 33 | - (UIViewController *)pageViewController:(ERPageViewController *)pageViewController childControllerAtIndex:(NSInteger)index; 34 | 35 | /** 36 | 子控制器title 37 | 38 | @param pageViewController self 39 | @param index 当前index 40 | @return title 41 | */ 42 | - (NSString *)pageViewController:(ERPageViewController *)pageViewController titleForChildControllerAtIndex:(NSInteger)index; 43 | 44 | 45 | @end 46 | 47 | 48 | @interface ERPageViewController : UIViewController 49 | 50 | /** 导航选择器高度 */ 51 | @property (nonatomic, assign) NSInteger segmentHeight; 52 | 53 | @property (nonatomic, assign) NSInteger countOfControllers; 54 | 55 | @property (nonatomic, assign) NSInteger currentIndex; 56 | 57 | @property (nonatomic, strong) UIScrollView *contentScrollerView; 58 | 59 | @property (nonatomic, weak) id dataSource; 60 | 61 | /** 62 | 刷新子视图 63 | */ 64 | - (void)reloadPageData; 65 | 66 | 67 | /** 68 | 移动到某一页面 69 | 70 | @param index index 71 | */ 72 | - (void)movePageControllerToIndex:(NSInteger)index; 73 | @end 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /ERPageController/Assets.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 | } -------------------------------------------------------------------------------- /ERPageController/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ERPageController 4 | // 5 | // Created by 胡广宇 on 2017/7/7. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/PageViewControllers/ERSegmentController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ERSegmentController.h 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "ERPageViewController.h" 10 | #import "ERSegmentMenuController.h" 11 | 12 | @class ERSegmentController; 13 | 14 | @protocol ERSegmentControllerDelegte 15 | 16 | @optional 17 | 18 | /** 19 | 导航按钮点击事件回调 20 | 21 | @param segmentController self 22 | @param indexPath indexPath 23 | */ 24 | - (void)segmentController:(ERSegmentController *)segmentController didSelectItemAtIndexPath:(NSIndexPath *)indexPath; 25 | 26 | /** 27 | 导航菜单编辑按钮点击回调 28 | 29 | @param segmentController self 30 | @param editMenuButton editMenuButton 31 | */ 32 | - (void)segmentController:(ERSegmentController *)segmentController didSelectEditMenuButton:(UIButton *)editMenuButton; 33 | /** 34 | 导航按钮双击事件回调 35 | 36 | @param segmentController self 37 | @param indexPath indexPath 38 | */ 39 | - (void)segmentController:(ERSegmentController *)segmentController itemDoubleClickAtIndexPath:(NSIndexPath *)indexPath; 40 | 41 | /** 42 | 页面切换滚动完成回调 43 | 44 | @param pageController superClass 45 | @param fromIndex fromIndex 46 | @param toIndex toIndex 47 | */ 48 | - (void)pageControllerDidScroll:(ERPageViewController *)pageController fromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex; 49 | 50 | @end 51 | 52 | @interface ERSegmentController : ERPageViewController 53 | 54 | @property (nonatomic, weak) iddelegate; 55 | /** 是否展示编辑菜单按钮 */ 56 | @property (nonatomic, weak) id menuDataSource; 57 | /** 编辑菜单图片(这里在editMenuButton上又覆盖一层imageView是为了让阴影和旋转分开,用不同的视图表示) */ 58 | @property (nonatomic, strong) UIImageView *editMenuIconIgV; 59 | /** 底部横线高度 */ 60 | @property (nonatomic, assign) CGFloat progressHeight; 61 | /** 底部横线宽度 */ 62 | @property (nonatomic, assign) CGFloat progressWidth; 63 | /** 未选中状态下的字号 */ 64 | @property (nonatomic, strong) UIFont *normalTextFont; 65 | /** 选中状态下的字号 */ 66 | @property (nonatomic, strong) UIFont *selectedTextFont; 67 | /** 未选中状态下的字体颜色 */ 68 | @property (nonatomic, strong) UIColor *normalTextColor; 69 | /** 选中状态下的字体颜色 */ 70 | @property (nonatomic, strong) UIColor *selectedTextColor; 71 | /** item直接的行间距 */ 72 | @property (nonatomic, assign) NSInteger itemMinimumSpace; 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/LXReorderableCollectionViewFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // LXReorderableCollectionViewFlowLayout.h 3 | // 4 | // Created by Stan Chang Khin Boon on 1/10/12. 5 | // Copyright (c) 2012 d--buzz. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | @interface LXReorderableCollectionViewFlowLayout : UICollectionViewFlowLayout 11 | 12 | @property (assign, nonatomic) CGFloat scrollingSpeed; 13 | @property (assign, nonatomic) UIEdgeInsets scrollingTriggerEdgeInsets; 14 | @property (strong, nonatomic, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer; 15 | @property (strong, nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer; 16 | 17 | - (void)setUpGestureRecognizersOnCollectionView __attribute__((deprecated("Calls to setUpGestureRecognizersOnCollectionView method are not longer needed as setup are done automatically through KVO."))); 18 | 19 | @end 20 | 21 | @protocol LXReorderableCollectionViewDataSource 22 | 23 | @optional 24 | 25 | - (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath; 26 | - (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath didMoveToIndexPath:(NSIndexPath *)toIndexPath; 27 | 28 | - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath; 29 | - (BOOL)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath canMoveToIndexPath:(NSIndexPath *)toIndexPath; 30 | 31 | @end 32 | 33 | @protocol LXReorderableCollectionViewDelegateFlowLayout 34 | @optional 35 | 36 | - (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willBeginDraggingItemAtIndexPath:(NSIndexPath *)indexPath; 37 | - (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didBeginDraggingItemAtIndexPath:(NSIndexPath *)indexPath; 38 | - (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath; 39 | - (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath; 40 | 41 | @end -------------------------------------------------------------------------------- /ERPageController/ERPageController/PageViewControllers/ERSegmentCollectionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/PageViewControllers/ERPageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ERPageViewController.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "ERPageViewController.h" 10 | 11 | @interface ERPageViewController () 12 | 13 | @property (nonatomic, assign) NSMutableArray *visiblecontrollers; 14 | 15 | @end 16 | 17 | @implementation ERPageViewController 18 | 19 | - (void)viewDidLoad { 20 | 21 | [super viewDidLoad]; 22 | 23 | self.automaticallyAdjustsScrollViewInsets = false; 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | self.segmentHeight = 30; 27 | } 28 | 29 | /** 30 | 添加子视图控制器 31 | */ 32 | - (void)initializeSubViews{ 33 | 34 | [self.visiblecontrollers removeAllObjects]; 35 | self.countOfControllers = [self.dataSource numberOfControllersInPageViewController:self]; 36 | for (int i = 0; i < self.countOfControllers; i++) { 37 | [self addViewController:[self.dataSource pageViewController:self childControllerAtIndex:i] atIndex:i]; 38 | } 39 | self.contentScrollerView.contentSize = CGSizeMake(self.countOfControllers * CGRectGetWidth(self.contentScrollerView.frame), 0); 40 | self.contentScrollerView.contentOffset = CGPointMake(self.currentIndex * CGRectGetWidth(self.contentScrollerView.frame), 0); 41 | } 42 | 43 | /** 44 | 刷新子视图 45 | */ 46 | - (void)reloadPageData{ 47 | [self initializeSubViews]; 48 | } 49 | 50 | /** 51 | 移动到某一页面 52 | 53 | @param index index 54 | */ 55 | - (void)movePageControllerToIndex:(NSInteger)index{ 56 | if (index < 0 && index > self.countOfControllers) return; 57 | [self.contentScrollerView setContentOffset:CGPointMake(index * CGRectGetWidth(self.contentScrollerView .frame),0)]; 58 | } 59 | 60 | #pragma mark - 控制器的增删操作 61 | 62 | - (void)addViewController:(UIViewController *)viewController atIndex:(NSInteger)index 63 | { 64 | if (!viewController.parentViewController) { 65 | [self addChildViewController:viewController]; 66 | viewController.view.frame = [self getControllerFrameWithIndex:index]; 67 | [self.contentScrollerView addSubview:viewController.view]; 68 | [self.visiblecontrollers addObject:viewController]; 69 | }else { 70 | viewController.view.frame = [self getControllerFrameWithIndex:index]; 71 | } 72 | } 73 | 74 | - (void)removeViewController:(UIViewController *)viewController atIndex:(NSInteger)index 75 | { 76 | if (viewController.parentViewController) { 77 | [viewController.view removeFromSuperview]; 78 | [viewController removeFromParentViewController]; 79 | [self.visiblecontrollers removeObjectAtIndex:index]; 80 | } 81 | } 82 | 83 | - (CGRect)getControllerFrameWithIndex:(NSInteger)index{ 84 | return CGRectMake(index * CGRectGetWidth(self.contentScrollerView.frame), 0, CGRectGetWidth(self.contentScrollerView.frame), CGRectGetHeight(self.contentScrollerView.frame)); 85 | } 86 | 87 | - (void)viewWillLayoutSubviews{ 88 | if (!CGRectEqualToRect(self.contentScrollerView.bounds, self.view.bounds)) { 89 | self.contentScrollerView.frame = CGRectMake(0, self.segmentHeight, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - self.segmentHeight); 90 | [self initializeSubViews]; 91 | } 92 | } 93 | 94 | 95 | #pragma mark - getter/setter 96 | 97 | - (UIScrollView *)contentScrollerView{ 98 | if (!_contentScrollerView) { 99 | _contentScrollerView = [[UIScrollView alloc] initWithFrame:CGRectZero]; 100 | _contentScrollerView.showsHorizontalScrollIndicator = NO; 101 | _contentScrollerView.showsVerticalScrollIndicator = NO; 102 | _contentScrollerView.pagingEnabled = YES; 103 | [self.view addSubview:_contentScrollerView]; 104 | } 105 | return _contentScrollerView; 106 | } 107 | 108 | - (void)didReceiveMemoryWarning { 109 | [super didReceiveMemoryWarning]; 110 | 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/EditMenuCollectionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /ERPageController.xcodeproj/xcuserdata/huguangyu.xcuserdatad/xcschemes/ERPageController.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ERPageController 3 | 4 | ## 带编辑菜单的分页选择器 5 | 6 | ### 功能说明: 7 | ##### 页面为不同的ViewController,可以左右滑动. 8 | ##### 顶部导航条可点击切换(双击刷新事件回调),可以左右滑动查看. 9 | ##### 按需显示的编辑菜单,可以长按拖动顺序/添加/删除 10 |
11 | 12 | ![gif](http://upload-images.jianshu.io/upload_images/2773241-c8f9682d4e7de898.gif?imageMogr2/auto-orient/strip) 13 | 14 | ### 使用方法: 15 | 16 | ### 特别声明: 数据模型我使用的是``` (NSMutableArray *) ``` 通过 "name" 获取页面标题 (可根据自己需求更改) 17 | 18 | ### 初始化控件 19 | 20 | ``` 21 | ERSegmentController *pageManager = [[ERSegmentController alloc] init]; 22 | pageManager.view.frame = CGRectMake(0, 64, ScreenWidth, ScreenHeight - 64 - 49); 23 | pageManager.segmentHeight = 25;//导航条高度 24 | pageManager.progressWidth = 15;//导航条底横线度宽度 25 | pageManager.progressHeight = 1;//导航条底横线高 26 | pageManager.itemMinimumSpace = 10;//导航条item直接的间距 27 | pageManager.normalTextFont = [UIFont systemFontOfSize:12];//未选中字体大小 28 | pageManager.selectedTextFont = [UIFont systemFontOfSize:16];//已选中字体大小 29 | pageManager.normalTextColor = [UIColor blackColor];//未选中字体颜色 30 | pageManager.selectedTextColor = [UIColor redColor];//已选中字体颜色 31 | pageManager.dataSource = self;//页面管理数据源 32 | pageManager.menuDataSource = self;//菜单管理数据源, 如果不设置改代理则没有菜单按钮 33 | pageManager.editMenuIconIgV.image = [UIImage imageNamed:@"editButtonImage"];//编辑菜单icon (按需设置) 34 | pageManager.delegate = self;//相关事件返回代理 35 | [self.view addSubview:pageManager.view]; 36 | [self addChildViewController:pageManager]; 37 | ``` 38 | ### 通过 dataSource 获取所有页面的数据源 39 | 40 | ``` 41 | @protocol ERPageViewControllerDataSource 42 | 43 | @required 44 | 45 | /** 46 | 返回子控制器总数,类似TableViewDataSource 47 | 48 | @param pageViewController self 49 | @return 子控制器总数 50 | */ 51 | - (NSInteger)numberOfControllersInPageViewController:(ERPageViewController *)pageViewController; 52 | 53 | 54 | /** 55 | 返回对应index的ViewController 56 | 57 | @param pageViewController self 58 | @param index 当前index 59 | @return 需要展示的ViewController 60 | */ 61 | - (UIViewController *)pageViewController:(ERPageViewController *)pageViewController childControllerAtIndex:(NSInteger)index; 62 | 63 | /** 64 | 子控制器title 65 | 66 | @param pageViewController self 67 | @param index 当前index 68 | @return title 69 | */ 70 | - (NSString *)pageViewController:(ERPageViewController *)pageViewController titleForChildControllerAtIndex:(NSInteger)index; 71 | 72 | 73 | 74 | @end 75 | 76 | ``` 77 | 78 | ### 通过 menuDataSource 获取编辑菜单的数据源,如不需要则不用设置该代理 79 | 80 | ``` 81 | @protocol ERSegmentMenuControllerDataSource 82 | 83 | @required; 84 | 85 | /** 86 | 已经选择的频道列表信息 87 | 88 | @param segmentMenuController self 89 | @return 必须为字典型数组(必须包含一个KEY为@"name"的字符串) 90 | */ 91 | - (NSMutableArray *)selectedChannelLisInSegmentMenuController:(ERSegmentMenuController *)segmentMenuController; 92 | 93 | @optional; 94 | 95 | /** 96 | 未选择的频道列表信息 97 | 98 | @param segmentMenuController self 99 | @return 可以为nil ,若不为nil则必须为字典型数组(必须包含一个KEY为@"name"的字符串) 100 | */ 101 | - (NSMutableArray *)unSelectChannelListInSegmentMenuController:(ERSegmentMenuController *)segmentMenuController; 102 | 103 | 104 | /** 105 | 每组标题 106 | 107 | @param segmentMenuController segmentMenuController 108 | @param sectionHeaderLabel 组头Label 109 | @param section section 110 | @return title 111 | */ 112 | - (NSString *)segmentMenuController:(ERSegmentMenuController *)segmentMenuController sectionHeaderLabel:(UILabel *)sectionHeaderLabel titleForHeaderInSection:(NSInteger)section; 113 | 114 | @end 115 | 116 | ``` 117 | 118 | ### delegate 可以获取点击事件(双击导航条item)已经滚动事件的回调,用于自定义处理 119 | 120 | ``` 121 | @protocol ERSegmentControllerDelegte 122 | 123 | @optional 124 | 125 | /** 126 | 导航按钮点击事件回调 127 | 128 | @param segmentController self 129 | @param indexPath indexPath 130 | */ 131 | - (void)segmentController:(ERSegmentController *)segmentController didSelectItemAtIndexPath:(NSIndexPath *)indexPath; 132 | 133 | /** 134 | 导航菜单编辑按钮点击回调 135 | 136 | @param segmentController self 137 | @param editMenuButton editMenuButton 138 | */ 139 | - (void)segmentController:(ERSegmentController *)segmentController didSelectEditMenuButton:(UIButton *)editMenuButton; 140 | /** 141 | 导航按钮双击事件回调 142 | 143 | @param segmentController self 144 | @param indexPath indexPath 145 | */ 146 | - (void)segmentController:(ERSegmentController *)segmentController itemDoubleClickAtIndexPath:(NSIndexPath *)indexPath; 147 | 148 | /** 149 | 页面切换滚动完成回调 150 | 151 | @param pageController superClass 152 | @param fromIndex fromIndex 153 | @param toIndex toIndex 154 | */ 155 | - (void)pageControllerDidScroll:(ERPageViewController *)pageController fromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex; 156 | 157 | @end 158 | 159 | ``` 160 | 161 |
162 | 163 | ### 参考类库/文章 (已表感谢 ! 学习并膜拜大神们 ~) 164 | 165 | [DDNews](https://github.com/iDvel/DDNews)
166 | [ZYColumnViewController](https://github.com/r9ronaldozhang/ZYColumnViewController)
167 | [LXReorderableCollectionViewFlowLayout](https://github.com/lxcid/LXReorderableCollectionViewFlowLayout)
168 | [TYPagerController](https://github.com/12207480/TYPagerController)
169 | 170 | -------------------------------------------------------------------------------- /ERPageController/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ERPageController 4 | // 5 | // Created by 胡广宇 on 2017/7/7. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "ERSegmentController.h" 11 | 12 | #define isIPhoneX (([UIScreen mainScreen].bounds.size.width == 375.f || [UIScreen mainScreen].bounds.size.width == 812.f) && ([UIScreen mainScreen].bounds.size.height == 375.f || [UIScreen mainScreen].bounds.size.height == 812.f) ? YES : NO) 13 | #define TabbarSafeBottomMargin (isIPhoneX ? 34.f : 0.f) 14 | #define NavigationSafeTopMargin (isIPhoneX ? 24.f : 0.f) 15 | 16 | @interface ViewController () 17 | 18 | @property (nonatomic, strong) NSMutableArray *displayArray; 19 | 20 | @property (nonatomic, strong) NSMutableArray *unDisplayArray; 21 | 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | [self prepareData]; 30 | 31 | [self addPageController]; 32 | } 33 | 34 | - (void)prepareData{ 35 | 36 | NSArray *displayTitlesArry = @[@"Two",@"Two",@"Two",@"Two",@"Four",@"Four",@"Four",@"Four",@"Three",@"Three",@"Three"]; 37 | 38 | self.displayArray = [NSMutableArray arrayWithCapacity:displayTitlesArry.count]; 39 | 40 | for (int i = 0; i < displayTitlesArry.count; i++) { 41 | NSMutableDictionary *dic = [NSMutableDictionary dictionary]; 42 | [dic setValue:displayTitlesArry[i] forKey:@"name"]; 43 | [dic setValue:@(i) forKey:@"tag"]; 44 | [dic setValue:[[NSClassFromString([NSString stringWithFormat:@"Page%@ViewController",displayTitlesArry[i]]) alloc] init] forKey:@"viewController"]; 45 | [self.displayArray addObject:dic]; 46 | } 47 | 48 | NSArray *undisplayTitlesArry = @[@"One",@"One",@"One",@"One"]; 49 | 50 | self.unDisplayArray = [NSMutableArray arrayWithCapacity:undisplayTitlesArry.count]; 51 | 52 | for (int i = 0; i < undisplayTitlesArry.count; i++) { 53 | NSMutableDictionary *dic = [NSMutableDictionary dictionary]; 54 | [dic setValue:undisplayTitlesArry[i] forKey:@"name"]; 55 | [dic setValue:@(i + displayTitlesArry.count) forKey:@"tag"]; 56 | [dic setValue:[[NSClassFromString([NSString stringWithFormat:@"Page%@ViewController",undisplayTitlesArry[i]]) alloc] init] forKey:@"viewController"]; 57 | [self.unDisplayArray addObject:dic]; 58 | } 59 | } 60 | 61 | - (void)addPageController{ 62 | 63 | ERSegmentController *pageVC = [[ERSegmentController alloc] init]; 64 | pageVC.view.frame = CGRectMake(CGRectGetMinX(self.view.frame), NavigationSafeTopMargin + CGRectGetMinY(self.view.frame) + 20, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame) - 20); 65 | pageVC.segmentHeight = 25; 66 | pageVC.progressWidth = 15; 67 | pageVC.progressHeight = 1; 68 | pageVC.itemMinimumSpace = 10; 69 | pageVC.editMenuIconIgV.image = [UIImage imageNamed:@"editButtonImage"]; 70 | pageVC.normalTextFont = [UIFont systemFontOfSize:12]; 71 | pageVC.selectedTextFont = [UIFont systemFontOfSize:16]; 72 | pageVC.normalTextColor = [UIColor blackColor]; 73 | pageVC.selectedTextColor = [UIColor redColor]; 74 | pageVC.delegate = self; 75 | pageVC.dataSource = self; 76 | pageVC.menuDataSource = self; 77 | [self.view addSubview:pageVC.view]; 78 | [self addChildViewController:pageVC]; 79 | 80 | } 81 | 82 | #pragma mark - ERSegmentMenuControllerDataSource 83 | 84 | - (NSMutableArray *)selectedChannelLisInSegmentMenuController:(ERSegmentMenuController *)segmentMenuController{ 85 | 86 | return self.displayArray; 87 | } 88 | 89 | - (NSMutableArray *)unSelectChannelListInSegmentMenuController:(ERSegmentMenuController *)segmentMenuController{ 90 | return self.unDisplayArray; 91 | } 92 | 93 | - (NSString *)segmentMenuController:(ERSegmentMenuController *)segmentMenuController sectionHeaderLabel:(UILabel *)sectionHeaderLabel titleForHeaderInSection:(NSInteger)section{ 94 | if (!section) { 95 | sectionHeaderLabel.textColor = [UIColor redColor]; 96 | return @"长按排序"; 97 | }else{ 98 | sectionHeaderLabel.textColor = [UIColor blackColor]; 99 | return @"点击添加标签"; 100 | } 101 | } 102 | 103 | #pragma mark - ERPageViewControllerDataSource 104 | 105 | - (NSInteger)numberOfControllersInPageViewController:(ERPageViewController *)pageViewController{ 106 | return self.displayArray.count; 107 | } 108 | 109 | - (UIViewController *)pageViewController:(ERPageViewController *)pageViewController childControllerAtIndex:(NSInteger)index{ 110 | return [self.displayArray[index] valueForKey:@"viewController"]; 111 | } 112 | 113 | - (NSString *)pageViewController:(ERPageViewController *)pageViewController titleForChildControllerAtIndex:(NSInteger)index{ 114 | return [NSString stringWithFormat:@"%@", [self.displayArray[index] valueForKey:@"name"]]; 115 | } 116 | 117 | #pragma mark - ERSegmentControllerDelegte 118 | 119 | - (void)segmentController:(ERSegmentController *)segmentController didSelectEditMenuButton:(UIButton *)editMenuButton{ 120 | 121 | CGFloat angle = editMenuButton.selected ? M_PI * 0.25 : - M_PI * 0.25; 122 | //旋转动画 123 | [UIView animateWithDuration:0.25 animations:^{ 124 | segmentController.editMenuIconIgV.transform = CGAffineTransformRotate(segmentController.editMenuIconIgV.transform, angle); 125 | }]; 126 | } 127 | 128 | - (void)segmentController:(ERSegmentController *)segmentController didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ 129 | UIViewController *currentVC = [self.displayArray[indexPath.item] valueForKey:@"viewController"]; 130 | NSLog(@"currentVC: %@ , index : %ld",currentVC,indexPath.item); 131 | } 132 | 133 | - (void)segmentController:(ERSegmentController *)segmentController itemDoubleClickAtIndexPath:(NSIndexPath *)indexPath{ 134 | UIViewController *currentVC = [self.displayArray[indexPath.item] valueForKey:@"viewController"]; 135 | NSLog(@"双击了,可刷新 currentVC: %@ , index : %ld",currentVC,indexPath.item); 136 | } 137 | 138 | - (void)pageControllerDidScroll:(ERPageViewController *)pageController fromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex{ 139 | UIViewController *currentVC = [self.displayArray[toIndex] valueForKey:@"viewController"]; 140 | NSLog(@"滚动切换 完成 currentVC: %@ , fromIndex : %ld toindex : %ld",currentVC,fromIndex,toIndex); 141 | } 142 | 143 | 144 | - (void)didReceiveMemoryWarning { 145 | [super didReceiveMemoryWarning]; 146 | } 147 | 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/ERSegmentMenuController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ERSegmentMenuController.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/29. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "ERSegmentMenuController.h" 10 | #import "LXReorderableCollectionViewFlowLayout.h" 11 | #import "EditMenuCollectionViewCell.h" 12 | #import "SectionHeader.h" 13 | 14 | 15 | static NSString * const CollectionViewCellIdentifier = @"EditMenuCollectionViewCell"; 16 | 17 | @interface ERSegmentMenuController ()< 18 | UICollectionViewDelegate, 19 | UICollectionViewDataSource, 20 | UICollectionViewDelegateFlowLayout 21 | > 22 | 23 | /** 选中的标签列表 */ 24 | @property (nonatomic, strong) NSMutableArray *selectedChannelList; 25 | /** 未选中的标签列表 */ 26 | @property (nonatomic, strong) NSMutableArray *unSelectChannelList; 27 | /** 布局视图 */ 28 | @property (nonatomic, strong) UICollectionView *collectionView; 29 | @end 30 | 31 | @implementation ERSegmentMenuController 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | } 36 | 37 | - (void)viewWillAppear:(BOOL)animated{ 38 | [super viewWillAppear:animated]; 39 | [self refreshContentData]; 40 | } 41 | 42 | - (void)refreshContentData{ 43 | self.selectedChannelList = [self.dataSource selectedChannelLisInSegmentMenuController:self]; 44 | if ([self.dataSource respondsToSelector:@selector(unSelectChannelListInSegmentMenuController:)]) { 45 | self.unSelectChannelList = [self.dataSource unSelectChannelListInSegmentMenuController:self]; 46 | } 47 | [self.collectionView reloadData]; 48 | } 49 | 50 | #pragma mark - UICollectionViewDataSource 51 | 52 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{ 53 | 54 | return (self.selectedChannelList.count && self.unSelectChannelList.count) ? 2 : (self.selectedChannelList.count || self.unSelectChannelList.count); 55 | } 56 | 57 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 58 | { 59 | if (section == 0){ 60 | return self.selectedChannelList.count; 61 | }else{ 62 | return self.unSelectChannelList.count; 63 | } 64 | } 65 | 66 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 67 | { 68 | EditMenuCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CollectionViewCellIdentifier forIndexPath:indexPath]; 69 | 70 | if (indexPath.section == 0 ) { 71 | //选中的列表 72 | [cell.channelButton setTitle:[NSString stringWithFormat:@"%@ %@",[self.selectedChannelList[indexPath.row] objectForKey:@"name"],[self.selectedChannelList[indexPath.row] objectForKey:@"tag"]] forState:UIControlStateNormal]; 73 | }else{ 74 | //未选中的列表 75 | [cell.channelButton setTitle:[NSString stringWithFormat:@"%@ %@",[self.unSelectChannelList[indexPath.row] objectForKey:@"name"],[self.unSelectChannelList[indexPath.row] objectForKey:@"tag"]] forState:UIControlStateNormal]; 76 | } 77 | 78 | return cell; 79 | } 80 | 81 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{ 82 | 83 | SectionHeader *header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"SectionHeader" forIndexPath:indexPath]; 84 | 85 | if ([self.dataSource respondsToSelector:@selector(segmentMenuController:sectionHeaderLabel:titleForHeaderInSection:)]) { 86 | header.titleLabel.text = [self.dataSource segmentMenuController:self sectionHeaderLabel:header.titleLabel titleForHeaderInSection:indexPath.section]; 87 | return header; 88 | }else{ 89 | if (!indexPath.section) { 90 | header.titleLabel.text = @"长按排序"; 91 | }else{ 92 | header.titleLabel.text = @"点击添加标签"; 93 | } 94 | } 95 | return header; 96 | } 97 | 98 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { 99 | if (section) { 100 | //未选中组 101 | return CGSizeMake(CGRectGetWidth(self.view.frame), 15); 102 | } 103 | //选中组 104 | return CGSizeMake(CGRectGetWidth(self.view.frame), 15 + 20); 105 | } 106 | 107 | 108 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ 109 | 110 | if (indexPath.section) { 111 | 112 | NSDictionary *dic = self.unSelectChannelList[indexPath.row]; 113 | 114 | [self.unSelectChannelList removeObjectAtIndex:indexPath.row]; 115 | 116 | if (!self.unSelectChannelList.count) { 117 | [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:1]]; 118 | }else{ 119 | [self.collectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:indexPath.row inSection:1]]]; 120 | } 121 | 122 | [self.selectedChannelList addObject:dic]; 123 | 124 | [self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.selectedChannelList.count - 1 inSection:0]]]; 125 | 126 | }else{ 127 | 128 | if (self.selectedChannelList.count > 1) { 129 | 130 | NSDictionary *dic = self.selectedChannelList[indexPath.row]; 131 | 132 | [self.selectedChannelList removeObjectAtIndex:indexPath.row]; 133 | 134 | [self.collectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:indexPath.row inSection:0]]]; 135 | 136 | [self.unSelectChannelList addObject:dic]; 137 | 138 | if (self.unSelectChannelList.count == 1) { 139 | [self.collectionView insertSections:[NSIndexSet indexSetWithIndex:1]]; 140 | }else{ 141 | [self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.unSelectChannelList.count - 1 inSection:1]]]; 142 | } 143 | } 144 | } 145 | 146 | [self.delegate displayChannelListDidChange]; 147 | } 148 | 149 | #pragma mark LXReorderableCollectionViewDataSource 150 | 151 | /** 152 | 已经移动到toIndexPath 153 | 154 | @param collectionView collectionView 155 | @param fromIndexPath fromIndexPath 156 | @param toIndexPath toIndexPath 157 | */ 158 | - (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath didMoveToIndexPath:(NSIndexPath *)toIndexPath{ 159 | NSDictionary *dic = self.selectedChannelList[fromIndexPath.item]; 160 | [self.selectedChannelList removeObjectAtIndex:fromIndexPath.row]; 161 | [self.selectedChannelList insertObject:dic atIndex:toIndexPath.row]; 162 | [self.delegate displayChannelListDidChange]; 163 | } 164 | 165 | /** 166 | 是否可以移动 167 | 168 | @param collectionView collectionView 169 | @param indexPath indexPath 170 | @return bool 171 | */ 172 | - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath{ 173 | if (indexPath.section || self.selectedChannelList.count == 1) return false; 174 | return YES; 175 | } 176 | 177 | /** 178 | 是否可以移动到某toIndexPath 179 | 180 | @param collectionView collectionView 181 | @param fromIndexPath fromIndexPath 182 | @param toIndexPath toIndexPath 183 | @return bool 184 | */ 185 | - (BOOL)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath canMoveToIndexPath:(NSIndexPath *)toIndexPath{ 186 | if (toIndexPath.section) return false; 187 | return YES; 188 | } 189 | 190 | #pragma mark - getter/setter 191 | 192 | - (UICollectionView *)collectionView{ 193 | if (!_collectionView) { 194 | 195 | LXReorderableCollectionViewFlowLayout *flowLayout = [[LXReorderableCollectionViewFlowLayout alloc] init]; 196 | // 设置cell的大小和细节,每排4个 197 | CGFloat margin = 20.0; 198 | CGFloat width = (CGRectGetWidth(self.view.frame) - margin * 5) / 4.f; 199 | CGFloat height = width * 3.f / 7.f; 200 | flowLayout.sectionInset = UIEdgeInsetsMake(margin, margin, margin, margin); 201 | flowLayout.itemSize = CGSizeMake(width, height); 202 | flowLayout.minimumInteritemSpacing = margin; 203 | flowLayout.minimumLineSpacing = 20; 204 | 205 | _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:flowLayout]; 206 | _collectionView.backgroundColor = [UIColor whiteColor]; 207 | _collectionView.dataSource = self; 208 | _collectionView.delegate = self; 209 | [_collectionView registerNib:[UINib nibWithNibName:@"EditMenuCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:CollectionViewCellIdentifier]; 210 | [_collectionView registerClass:[SectionHeader class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"SectionHeader"]; 211 | [self.view addSubview:_collectionView]; 212 | } 213 | return _collectionView; 214 | } 215 | 216 | #pragma mark - viewWillLayoutSubviews 217 | 218 | - (void)viewWillLayoutSubviews{ 219 | self.collectionView.frame = self.view.bounds; 220 | } 221 | 222 | - (void)didReceiveMemoryWarning { 223 | [super didReceiveMemoryWarning]; 224 | 225 | } 226 | @end 227 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/PageViewControllers/ERSegmentController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ERSegmentController.m 3 | // ErHuDemo 4 | // 5 | // Created by 胡广宇 on 2017/6/27. 6 | // Copyright © 2017年 胡广宇. All rights reserved. 7 | // 8 | 9 | #import "ERSegmentController.h" 10 | #import "ERSegmentCollectionViewCell.h" 11 | 12 | static NSString *segmentCellIdentifier = @"ERSegmentCollectionViewCell"; 13 | 14 | @interface ERSegmentController () 15 | 16 | /** "导航"条 */ 17 | @property (nonatomic, strong) UICollectionView *segCollectionView; 18 | /** 底部红线 */ 19 | @property (nonatomic, strong) UIView *underLineView; 20 | /** 记录点击时间,用于判断双击事件 */ 21 | @property (nonatomic, strong) NSDate *lastTime; 22 | /** 开始滚动时坐标 */ 23 | @property (nonatomic, assign) CGFloat beginOffsetX; 24 | /** 选中状态下字体放大倍数 */ 25 | @property (nonatomic, assign) CGFloat selectFontScale; 26 | /** 是否是点击item触发的滚动 */ 27 | @property (nonatomic, assign) BOOL isTapItemToScroll; 28 | /** 编辑按钮 */ 29 | @property (nonatomic, strong) UIButton *editMenuButton; 30 | /** 菜单控制器 */ 31 | @property (nonatomic, strong) ERSegmentMenuController *menuController; 32 | /** 是否展示编辑菜单按钮 */ 33 | @property (nonatomic, assign) BOOL showEditMenuButton; 34 | @end 35 | 36 | @implementation ERSegmentController 37 | 38 | - (instancetype)init { 39 | if (self = [super init]) { 40 | [self configirePropertys]; 41 | } 42 | return self; 43 | } 44 | 45 | 46 | - (void)viewDidLoad { 47 | [super viewDidLoad]; 48 | } 49 | 50 | #pragma mark - 初始化属性配置 51 | - (void)configirePropertys{ 52 | 53 | self.showEditMenuButton = false; 54 | 55 | self.progressWidth = 20; 56 | self.progressHeight = 2; 57 | self.itemMinimumSpace = 5; 58 | 59 | self.normalTextFont = [UIFont systemFontOfSize:15]; 60 | self.selectedTextFont = [UIFont systemFontOfSize:18]; 61 | 62 | self.contentScrollerView.delegate = self; 63 | 64 | self.normalTextColor = [UIColor blackColor]; 65 | self.selectedTextColor = [UIColor redColor]; 66 | } 67 | 68 | 69 | /** 70 | 刷新数据 71 | */ 72 | - (void)reloadData{ 73 | [self.segCollectionView reloadData]; 74 | [self reloadPageData]; 75 | [self changeSegmentImmediately]; 76 | } 77 | 78 | #pragma mark - 编辑菜单点击事件 79 | 80 | - (void)editMenuButtonClick:(UIButton *)btn{ 81 | 82 | btn.selected = !btn.selected; 83 | 84 | if ([self.delegate respondsToSelector:@selector(segmentController:didSelectEditMenuButton:)]) { 85 | // 存在回调则自定义点击事件, 否则弹出菜单 这里按需求自行修改 86 | [self.delegate segmentController:self didSelectEditMenuButton:btn]; 87 | } 88 | if (btn.selected) { 89 | [self showMenuViewController]; 90 | }else{ 91 | [self hideMenuViewController]; 92 | } 93 | } 94 | 95 | - (void)showMenuViewController{ 96 | 97 | self.segCollectionView.userInteractionEnabled = false; 98 | 99 | [UIView animateWithDuration:0.25 animations:^{ 100 | self.menuController.view.frame = CGRectMake(CGRectGetMinX(self.view.frame), CGRectGetMaxY(self.segCollectionView.frame), self.view.frame.size.width, [UIScreen mainScreen].bounds.size.height - CGRectGetMaxY(self.segCollectionView.frame)); 101 | [self.menuController.view layoutIfNeeded]; 102 | 103 | } completion:^(BOOL finished) { 104 | self.tabBarController.tabBar.hidden = YES; 105 | }]; 106 | } 107 | 108 | - (void)hideMenuViewController{ 109 | 110 | self.segCollectionView.userInteractionEnabled = true;; 111 | 112 | [UIView animateWithDuration:0.25 animations:^{ 113 | self.menuController.view.frame = CGRectMake(CGRectGetMinX(self.view.frame), CGRectGetMaxY(self.segCollectionView.frame), self.view.frame.size.width, 0); 114 | [self.menuController.view layoutIfNeeded]; 115 | } completion:^(BOOL finished) { 116 | self.tabBarController.tabBar.hidden = false; 117 | }]; 118 | } 119 | 120 | #pragma mark - UICollectionView 相关 121 | 122 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ 123 | return [self.dataSource numberOfControllersInPageViewController:self]; 124 | } 125 | 126 | - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ 127 | 128 | ERSegmentCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:segmentCellIdentifier forIndexPath:indexPath]; 129 | 130 | NSString *title = [self.dataSource pageViewController:self titleForChildControllerAtIndex:indexPath.item]; 131 | 132 | cell.titleLabel.text = title; 133 | 134 | cell.titleLabel.font = self.normalTextFont; 135 | 136 | [self changeCellTextColorImmediatelyFromCell:(indexPath.item == self.currentIndex ? nil : cell) toCell:(indexPath.item == self.currentIndex ? cell : nil)]; 137 | 138 | return cell; 139 | } 140 | 141 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{ 142 | 143 | NSString *title = [self.dataSource pageViewController:self titleForChildControllerAtIndex:indexPath.item]; 144 | 145 | CGFloat width = [self boundingSizeWithString:title font:self.selectedTextFont constrainedToSize:CGSizeMake(MAXFLOAT, self.segmentHeight)].width; 146 | 147 | return CGSizeMake(width, self.segmentHeight); 148 | } 149 | 150 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ 151 | NSDate *nowTime = [NSDate date]; 152 | if (self.currentIndex != indexPath.item) { 153 | self.isTapItemToScroll = true; 154 | [self movePageControllerToIndex:indexPath.item]; 155 | [self changeSegmentImmediately]; 156 | if ([self.delegate respondsToSelector:@selector(segmentController:didSelectItemAtIndexPath:)]) { 157 | [self.delegate segmentController:self didSelectItemAtIndexPath:indexPath]; 158 | } 159 | }else{ 160 | if ([nowTime timeIntervalSinceDate:self.lastTime] < 0.3) { 161 | //判定为双击事件 162 | if ([self.delegate respondsToSelector:@selector(segmentController:itemDoubleClickAtIndexPath:)]) { 163 | [self.delegate segmentController:self itemDoubleClickAtIndexPath:indexPath]; 164 | } 165 | } 166 | } 167 | self.lastTime = nowTime; 168 | } 169 | 170 | - (ERSegmentCollectionViewCell *)cellForIndex:(NSInteger)index 171 | { 172 | if (index >= self.countOfControllers) { 173 | return nil; 174 | } 175 | return (ERSegmentCollectionViewCell *)[self.segCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]; 176 | } 177 | 178 | - (CGRect)cellFrameWithIndex:(NSInteger)index 179 | { 180 | if (index >= self.countOfControllers) { 181 | return CGRectZero; 182 | } 183 | UICollectionViewLayoutAttributes * cellAttrs = [self.segCollectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]; 184 | return cellAttrs.frame; 185 | } 186 | 187 | #pragma mark - UIScrollViewDelegate 188 | 189 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView{ 190 | if (scrollView == self.contentScrollerView) { 191 | if (!self.isTapItemToScroll) { 192 | [self shangeSegmentProgress]; 193 | } 194 | [self changeCurrentPageIndex]; 195 | } 196 | } 197 | 198 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{ 199 | if (scrollView == self.contentScrollerView) { 200 | self.beginOffsetX = scrollView.contentOffset.x; 201 | } 202 | } 203 | 204 | #pragma mark - ERSegmentMenuControllerDelegate 205 | 206 | - (void)displayChannelListDidChange{ 207 | [self reloadData]; 208 | } 209 | 210 | #pragma mark - Item 渐变滚动动画 211 | 212 | /** 213 | 带渐变效果的改变 214 | */ 215 | - (void)shangeSegmentProgress{ 216 | 217 | CGFloat offsetX = self.contentScrollerView.contentOffset.x; 218 | CGFloat width = CGRectGetWidth(self.contentScrollerView.frame); 219 | CGFloat floorIndex = floor(offsetX / width); 220 | CGFloat progress = offsetX / width - floorIndex; 221 | 222 | if (floorIndex < 0 || floorIndex >= self.countOfControllers) { 223 | return; 224 | } 225 | 226 | NSInteger fromIndex = 0, toIndex = 0; 227 | 228 | if (offsetX >= self.beginOffsetX) { 229 | //左滑 230 | if (floorIndex >= self.countOfControllers - 1) { 231 | return; 232 | } 233 | fromIndex = floorIndex; 234 | toIndex = MIN(self.countOfControllers - 1, fromIndex + 1); 235 | }else { 236 | //右滑 237 | if (floorIndex < 0 ) { 238 | return; 239 | } 240 | toIndex = floorIndex; 241 | fromIndex = MIN(self.countOfControllers - 1, toIndex + 1); 242 | progress = 1.0 - progress; 243 | } 244 | 245 | [self transitionFromIndex:fromIndex toIndex:toIndex progress:progress isTapToScroller:self.isTapItemToScroll]; 246 | } 247 | 248 | 249 | /** 250 | 无渐变效果的改变 251 | */ 252 | - (void)changeSegmentImmediately{ 253 | 254 | CGRect toCellFrame = [self cellFrameWithIndex:self.currentIndex]; 255 | CGFloat progressEdging = (toCellFrame.size.width - self.progressWidth) / 2; 256 | CGFloat progressX = toCellFrame.origin.x + progressEdging; 257 | CGFloat progressY = (toCellFrame.size.height - self.progressHeight); 258 | CGFloat width = toCellFrame.size.width - 2 * progressEdging; 259 | 260 | [UIView animateWithDuration:0.25 animations:^{ 261 | self.underLineView.frame = CGRectMake(progressX, progressY, width, self.progressHeight); 262 | } completion:^(BOOL finished) { 263 | self.isTapItemToScroll = false; 264 | }]; 265 | } 266 | 267 | /** 268 | 判断当前页面的index 269 | */ 270 | - (void)changeCurrentPageIndex{ 271 | 272 | CGFloat offsetX = self.contentScrollerView.contentOffset.x; 273 | CGFloat width = CGRectGetWidth(self.contentScrollerView.frame); 274 | 275 | NSInteger index = 0; 276 | 277 | if (offsetX >= self.beginOffsetX) { 278 | index = offsetX / width; 279 | }else { 280 | index = ceil(offsetX / width); 281 | } 282 | 283 | if (index < 0) { 284 | index = 0; 285 | }else if (index >= self.countOfControllers) { 286 | index = self.countOfControllers - 1; 287 | } 288 | 289 | if (index != self.currentIndex) { 290 | NSInteger fromIndex = self.currentIndex; 291 | self.currentIndex = index; 292 | if (self.currentIndex < self.countOfControllers) { 293 | [self.segCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:self.currentIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES]; 294 | } 295 | if (self.isTapItemToScroll) { 296 | [self transitionFromIndex:fromIndex toIndex:self.currentIndex progress:1 isTapToScroller:self.isTapItemToScroll]; 297 | } 298 | 299 | if ([self.delegate respondsToSelector:@selector(pageControllerDidScroll:fromIndex:toIndex:)]) { 300 | [self.delegate pageControllerDidScroll:self fromIndex:fromIndex toIndex:self.currentIndex]; 301 | } 302 | } 303 | } 304 | 305 | 306 | /** 307 | 逐渐改变cell选中和未选中的状态 308 | 309 | @param fromIndex fromIndex 310 | @param toIndex toIndex 311 | @param progress progress 312 | @param isTapToScroller 是否是点击item触发的 313 | */ 314 | - (void)transitionFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex progress:(CGFloat)progress isTapToScroller:(BOOL)isTapToScroller{ 315 | 316 | ERSegmentCollectionViewCell *fromCell = [self cellForIndex:fromIndex]; 317 | ERSegmentCollectionViewCell *toCell = [self cellForIndex:toIndex]; 318 | 319 | if (isTapToScroller) { 320 | [self changeCellTextColorImmediatelyFromCell:fromCell toCell:toCell]; 321 | }else{ 322 | [self transitionCellTextColorFromCell:fromCell toCell:toCell progress:progress]; 323 | [self transitionUnderLineFrameWithfromIndex:fromIndex toIndex:toIndex progress:progress]; 324 | } 325 | } 326 | 327 | /** 328 | 立即改变cell选中和未选中的状态,无渐变效果 329 | 330 | @param fromCell fromCell 331 | @param toCell toCell 332 | */ 333 | - (void)changeCellTextColorImmediatelyFromCell:(ERSegmentCollectionViewCell *)fromCell toCell:(ERSegmentCollectionViewCell *)toCell{ 334 | 335 | if (fromCell) { 336 | fromCell.titleLabel.textColor = self.normalTextColor; 337 | fromCell.transform = CGAffineTransformMakeScale(self.selectFontScale, self.selectFontScale); 338 | } 339 | 340 | if (toCell) { 341 | toCell.titleLabel.textColor = self.selectedTextColor; 342 | toCell.transform = CGAffineTransformIdentity; 343 | } 344 | } 345 | 346 | 347 | /** 348 | 根据progress来控制Cell标题颜色的转变和Cell放大效果 349 | 350 | @param fromCell fromCell 351 | @param toCell toCell 352 | @param progress progress 353 | */ 354 | - (void)transitionCellTextColorFromCell:(ERSegmentCollectionViewCell *)fromCell toCell:(ERSegmentCollectionViewCell *)toCell progress:(CGFloat)progress{ 355 | 356 | CGFloat currentTransform = (1.0 - self.selectFontScale) * progress; 357 | fromCell.transform = CGAffineTransformMakeScale(1.0 - currentTransform, 1.0 - currentTransform); 358 | toCell.transform = CGAffineTransformMakeScale(self.selectFontScale + currentTransform, self.selectFontScale + currentTransform); 359 | 360 | CGFloat narR,narG,narB,narA; 361 | [self.normalTextColor getRed:&narR green:&narG blue:&narB alpha:&narA]; 362 | CGFloat selR,selG,selB,selA; 363 | [self.selectedTextColor getRed:&selR green:&selG blue:&selB alpha:&selA]; 364 | CGFloat detalR = narR - selR ,detalG = narG - selG,detalB = narB - selB,detalA = narA - selA; 365 | 366 | fromCell.titleLabel.textColor = [UIColor colorWithRed:selR + detalR * progress green:selG + detalG * progress blue:selB + detalB * progress alpha:selA + detalA * progress]; 367 | toCell.titleLabel.textColor = [UIColor colorWithRed:narR - detalR * progress green:narG - detalG * progress blue:narB - detalB * progress alpha:narA - detalA * progress]; 368 | } 369 | 370 | 371 | /** 372 | 根据progress来控制底部横线便宜 373 | 374 | @param fromIndex fromIndex 375 | @param toIndex toIndex 376 | @param progress progress 377 | */ 378 | - (void)transitionUnderLineFrameWithfromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex progress:(CGFloat)progress{ 379 | if (self.countOfControllers == 0) { 380 | return; 381 | } 382 | 383 | CGRect fromCellFrame = [self cellFrameWithIndex:fromIndex]; 384 | CGRect toCellFrame = [self cellFrameWithIndex:toIndex]; 385 | 386 | CGFloat progressFromEdging = ABS((fromCellFrame.size.width - self.progressWidth)) / 2; 387 | CGFloat progressToEdging = ABS((toCellFrame.size.width - self.progressWidth)) / 2; 388 | 389 | CGFloat progressY = (toCellFrame.size.height - self.progressHeight); 390 | 391 | CGFloat progressX, width; 392 | 393 | if (fromCellFrame.origin.x < toCellFrame.origin.x) { 394 | if (progress <= 0.5) { 395 | progressX = fromCellFrame.origin.x + progressFromEdging + (fromCellFrame.size.width - 2 * progressFromEdging) * progress; 396 | width = (toCellFrame.size.width - progressToEdging + progressFromEdging) * 2 * progress - (toCellFrame.size.width - 2 *progressToEdging) * progress + fromCellFrame.size.width - 2 * progressFromEdging - (fromCellFrame.size.width - 2 * progressFromEdging) * progress; 397 | }else { 398 | progressX = fromCellFrame.origin.x + progressFromEdging + (fromCellFrame.size.width - 2 * progressFromEdging) * 0.5 + (fromCellFrame.size.width - progressFromEdging - (fromCellFrame.size.width - 2 * progressFromEdging) * 0.5 + progressToEdging + self.itemMinimumSpace)*(progress - 0.5) * 2; 399 | width = CGRectGetMaxX(toCellFrame) - progressToEdging - progressX - (toCellFrame.size.width - 2 * progressToEdging) * ( 1 - progress); 400 | } 401 | }else { 402 | if (progress <= 0.5) { 403 | progressX = fromCellFrame.origin.x + progressFromEdging - (toCellFrame.size.width - (toCellFrame.size.width - 2 * progressToEdging) / 2 - progressToEdging + progressFromEdging) * 2 * progress; 404 | width = CGRectGetMaxX(fromCellFrame) - (fromCellFrame.size.width - 2 * progressFromEdging) * progress - progressFromEdging - progressX; 405 | }else { 406 | progressX = toCellFrame.origin.x + progressToEdging+(toCellFrame.size.width - 2 * progressToEdging) * (1 - progress); 407 | width = (fromCellFrame.size.width - progressFromEdging + progressToEdging - (fromCellFrame.size.width - 2 * progressFromEdging) / 2 ) * (1 - progress) * 2 + toCellFrame.size.width - 2 * progressToEdging - (toCellFrame.size.width - 2 * progressToEdging) * (1 - progress); 408 | } 409 | } 410 | 411 | self.underLineView.frame = CGRectMake(progressX,progressY, width, self.progressHeight); 412 | } 413 | 414 | #pragma mark - Tool 支持工具 415 | 416 | /** 417 | 获取内容大小(Item大小适配) 418 | 419 | @param string 内容 420 | @param font 字号 421 | @param size 最大size 422 | @return contentSize 423 | */ 424 | - (CGSize)boundingSizeWithString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)size 425 | { 426 | CGSize textSize = CGSizeZero; 427 | 428 | #if (__IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0) 429 | if (![string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { 430 | textSize = [string sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByWordWrapping]; 431 | } 432 | else 433 | #endif 434 | { 435 | CGRect frame = [string boundingRectWithSize:size options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{ NSFontAttributeName:font} context:nil]; 436 | textSize = CGSizeMake(frame.size.width, frame.size.height + 1); 437 | } 438 | 439 | return textSize; 440 | } 441 | 442 | #pragma mark - LayoutSubviews 443 | 444 | - (void)viewWillLayoutSubviews{ 445 | 446 | [super viewWillLayoutSubviews]; 447 | 448 | if (CGRectEqualToRect(self.segCollectionView.frame, CGRectZero)) { 449 | 450 | self.editMenuButton.frame = CGRectMake(self.showEditMenuButton ? CGRectGetMaxX(self.view.frame) - self.segmentHeight : CGRectGetMaxX(self.view.frame), 0, self.showEditMenuButton ? self.segmentHeight : 0, self.segmentHeight); 451 | 452 | self.segCollectionView.frame = CGRectMake(0, 0,CGRectGetMinX(self.editMenuButton.frame), self.segmentHeight); 453 | 454 | [self scrollViewDidScroll:self.contentScrollerView]; 455 | 456 | if (self.showEditMenuButton) { 457 | self.editMenuIconIgV.frame = CGRectMake(self.segmentHeight / 4, self.segmentHeight / 4, self.segmentHeight / 2, self.segmentHeight/ 2); 458 | } 459 | } 460 | } 461 | 462 | #pragma mark - getter/setter 463 | 464 | - (void)setMenuDataSource:(id)menuDataSource{ 465 | if (self.menuController.dataSource != menuDataSource) { 466 | self.menuController.dataSource = menuDataSource; 467 | if (self.menuController.dataSource) { 468 | self.showEditMenuButton = true; 469 | } 470 | } 471 | } 472 | 473 | - (ERSegmentMenuController *)menuController{ 474 | if (!_menuController) { 475 | _menuController = [[ERSegmentMenuController alloc] init]; 476 | _menuController.view.frame = CGRectMake(CGRectGetMinX(self.view.frame), self.segmentHeight, CGRectGetWidth(self.view.frame), 0); 477 | _menuController.dataSource = self.menuDataSource; 478 | _menuController.delegate = self; 479 | [_menuController.view layoutIfNeeded]; 480 | [self addChildViewController:_menuController]; 481 | [self.view addSubview:_menuController.view]; 482 | } 483 | return _menuController; 484 | } 485 | 486 | - (void)setNormalTextFont:(UIFont *)normalTextFont{ 487 | if (_normalTextFont.pointSize != normalTextFont.pointSize) { 488 | _normalTextFont = normalTextFont; 489 | self.selectFontScale = _normalTextFont.pointSize / self.selectedTextFont.pointSize; 490 | } 491 | } 492 | 493 | - (void)setSelectedTextFont:(UIFont *)selectedTextFont{ 494 | if (_selectedTextFont.pointSize != selectedTextFont.pointSize) { 495 | _selectedTextFont = selectedTextFont; 496 | self.selectFontScale = self.normalTextFont.pointSize / _selectedTextFont.pointSize; 497 | } 498 | } 499 | 500 | - (UIButton *)editMenuButton{ 501 | if (!_editMenuButton) { 502 | _editMenuButton = [UIButton buttonWithType:UIButtonTypeCustom]; 503 | [_editMenuButton addTarget:self action:@selector(editMenuButtonClick:) forControlEvents:UIControlEventTouchUpInside]; 504 | _editMenuButton.backgroundColor = [UIColor whiteColor]; 505 | _editMenuButton.layer.shadowColor = [UIColor whiteColor].CGColor; 506 | _editMenuButton.layer.shadowRadius = self.segmentHeight / 10; 507 | _editMenuButton.layer.shadowOffset = CGSizeMake(- self.segmentHeight / 5, - 2); 508 | _editMenuButton.layer.shadowOpacity = 1; 509 | [_editMenuButton addSubview:self.editMenuIconIgV]; 510 | [self.view addSubview:_editMenuButton]; 511 | } 512 | return _editMenuButton; 513 | } 514 | 515 | - (UIImageView *)editMenuIconIgV{ 516 | if (!_editMenuIconIgV) { 517 | _editMenuIconIgV = [[UIImageView alloc] initWithFrame:CGRectZero]; 518 | _editMenuIconIgV.image = [UIImage imageNamed:@"editButtonImage"]; 519 | } 520 | return _editMenuIconIgV; 521 | } 522 | 523 | - (UICollectionView *)segCollectionView{ 524 | if (!_segCollectionView) { 525 | UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; 526 | layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; 527 | layout.sectionInset = UIEdgeInsetsMake(0, 5, 0, self.segmentHeight / 5); 528 | layout.minimumInteritemSpacing = self.itemMinimumSpace; 529 | _segCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; 530 | _segCollectionView.showsHorizontalScrollIndicator = NO; 531 | _segCollectionView.showsVerticalScrollIndicator = NO; 532 | if ([_segCollectionView respondsToSelector:@selector(setPrefetchingEnabled:)]) _segCollectionView.prefetchingEnabled = NO; 533 | _segCollectionView.backgroundColor = [UIColor whiteColor]; 534 | _segCollectionView.delegate = self; 535 | _segCollectionView.dataSource = self; 536 | [_segCollectionView registerNib:[UINib nibWithNibName:segmentCellIdentifier bundle:nil] forCellWithReuseIdentifier:segmentCellIdentifier]; 537 | [self.view addSubview:_segCollectionView]; 538 | 539 | self.underLineView = [[UIView alloc] initWithFrame:CGRectZero]; 540 | self.underLineView.layer.zPosition = -1; 541 | self.underLineView.backgroundColor = self.selectedTextColor; 542 | [_segCollectionView insertSubview:self.underLineView atIndex:0]; 543 | } 544 | return _segCollectionView; 545 | } 546 | 547 | - (void)didReceiveMemoryWarning { 548 | [super didReceiveMemoryWarning]; 549 | 550 | } 551 | 552 | @end 553 | -------------------------------------------------------------------------------- /ERPageController/ERPageController/EditMenuControllers/LXReorderableCollectionViewFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // LXReorderableCollectionViewFlowLayout.m 3 | // 4 | // Created by Stan Chang Khin Boon on 1/10/12. 5 | // Copyright (c) 2012 d--buzz. All rights reserved. 6 | // 7 | 8 | #import "LXReorderableCollectionViewFlowLayout.h" 9 | #import 10 | #import 11 | 12 | #ifndef CGGEOMETRY_LXSUPPORT_H_ 13 | CG_INLINE CGPoint 14 | LXS_CGPointAdd(CGPoint point1, CGPoint point2) { 15 | return CGPointMake(point1.x + point2.x, point1.y + point2.y); 16 | } 17 | #endif 18 | 19 | typedef NS_ENUM(NSInteger, LXScrollingDirection) { 20 | LXScrollingDirectionUnknown = 0, 21 | LXScrollingDirectionUp, 22 | LXScrollingDirectionDown, 23 | LXScrollingDirectionLeft, 24 | LXScrollingDirectionRight 25 | }; 26 | 27 | static NSString * const kLXScrollingDirectionKey = @"LXScrollingDirection"; 28 | static NSString * const kLXCollectionViewKeyPath = @"collectionView"; 29 | 30 | @interface CADisplayLink (LX_userInfo) 31 | @property (nonatomic, copy) NSDictionary *LX_userInfo; 32 | @end 33 | 34 | @implementation CADisplayLink (LX_userInfo) 35 | - (void) setLX_userInfo:(NSDictionary *) LX_userInfo { 36 | objc_setAssociatedObject(self, "LX_userInfo", LX_userInfo, OBJC_ASSOCIATION_COPY); 37 | } 38 | 39 | - (NSDictionary *) LX_userInfo { 40 | return objc_getAssociatedObject(self, "LX_userInfo"); 41 | } 42 | @end 43 | 44 | @interface UICollectionViewCell (LXReorderableCollectionViewFlowLayout) 45 | 46 | - (UIView *)LX_snapshotView; 47 | 48 | @end 49 | 50 | @implementation UICollectionViewCell (LXReorderableCollectionViewFlowLayout) 51 | 52 | - (UIView *)LX_snapshotView { 53 | if ([self respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)]) { 54 | return [self snapshotViewAfterScreenUpdates:YES]; 55 | } else { 56 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0f); 57 | [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 58 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 59 | UIGraphicsEndImageContext(); 60 | return [[UIImageView alloc] initWithImage:image]; 61 | } 62 | } 63 | 64 | @end 65 | 66 | @interface LXReorderableCollectionViewFlowLayout () 67 | 68 | @property (strong, nonatomic) NSIndexPath *selectedItemIndexPath; 69 | @property (strong, nonatomic) UIView *currentView; 70 | @property (assign, nonatomic) CGPoint currentViewCenter; 71 | @property (assign, nonatomic) CGPoint panTranslationInCollectionView; 72 | @property (strong, nonatomic) CADisplayLink *displayLink; 73 | 74 | @property (assign, nonatomic, readonly) id dataSource; 75 | @property (assign, nonatomic, readonly) id delegate; 76 | 77 | @end 78 | 79 | @implementation LXReorderableCollectionViewFlowLayout 80 | 81 | - (void)setDefaults { 82 | _scrollingSpeed = 300.0f; 83 | _scrollingTriggerEdgeInsets = UIEdgeInsetsMake(50.0f, 50.0f, 50.0f, 50.0f); 84 | } 85 | 86 | - (void)setupCollectionView { 87 | _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self 88 | action:@selector(handleLongPressGesture:)]; 89 | _longPressGestureRecognizer.delegate = self; 90 | 91 | // Links the default long press gesture recognizer to the custom long press gesture recognizer we are creating now 92 | // by enforcing failure dependency so that they doesn't clash. 93 | for (UIGestureRecognizer *gestureRecognizer in self.collectionView.gestureRecognizers) { 94 | if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { 95 | [gestureRecognizer requireGestureRecognizerToFail:_longPressGestureRecognizer]; 96 | } 97 | } 98 | 99 | [self.collectionView addGestureRecognizer:_longPressGestureRecognizer]; 100 | 101 | _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self 102 | action:@selector(handlePanGesture:)]; 103 | _panGestureRecognizer.delegate = self; 104 | [self.collectionView addGestureRecognizer:_panGestureRecognizer]; 105 | 106 | // Useful in multiple scenarios: one common scenario being when the Notification Center drawer is pulled down 107 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleApplicationWillResignActive:) name: UIApplicationWillResignActiveNotification object:nil]; 108 | } 109 | 110 | - (void)tearDownCollectionView { 111 | // Tear down long press gesture 112 | if (_longPressGestureRecognizer) { 113 | UIView *view = _longPressGestureRecognizer.view; 114 | if (view) { 115 | [view removeGestureRecognizer:_longPressGestureRecognizer]; 116 | } 117 | _longPressGestureRecognizer.delegate = nil; 118 | _longPressGestureRecognizer = nil; 119 | } 120 | 121 | // Tear down pan gesture 122 | if (_panGestureRecognizer) { 123 | UIView *view = _panGestureRecognizer.view; 124 | if (view) { 125 | [view removeGestureRecognizer:_panGestureRecognizer]; 126 | } 127 | _panGestureRecognizer.delegate = nil; 128 | _panGestureRecognizer = nil; 129 | } 130 | 131 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; 132 | } 133 | 134 | - (id)init { 135 | self = [super init]; 136 | if (self) { 137 | [self setDefaults]; 138 | [self addObserver:self forKeyPath:kLXCollectionViewKeyPath options:NSKeyValueObservingOptionNew context:nil]; 139 | } 140 | return self; 141 | } 142 | 143 | - (id)initWithCoder:(NSCoder *)aDecoder { 144 | self = [super initWithCoder:aDecoder]; 145 | if (self) { 146 | [self setDefaults]; 147 | [self addObserver:self forKeyPath:kLXCollectionViewKeyPath options:NSKeyValueObservingOptionNew context:nil]; 148 | } 149 | return self; 150 | } 151 | 152 | - (void)dealloc { 153 | [self invalidatesScrollTimer]; 154 | [self tearDownCollectionView]; 155 | [self removeObserver:self forKeyPath:kLXCollectionViewKeyPath]; 156 | } 157 | 158 | - (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { 159 | if ([layoutAttributes.indexPath isEqual:self.selectedItemIndexPath]) { 160 | layoutAttributes.hidden = YES; 161 | } 162 | } 163 | 164 | - (id)dataSource { 165 | return (id)self.collectionView.dataSource; 166 | } 167 | 168 | - (id)delegate { 169 | return (id)self.collectionView.delegate; 170 | } 171 | 172 | - (void)invalidateLayoutIfNecessary { 173 | NSIndexPath *newIndexPath = [self.collectionView indexPathForItemAtPoint:self.currentView.center]; 174 | NSIndexPath *previousIndexPath = self.selectedItemIndexPath; 175 | 176 | if ((newIndexPath == nil) || [newIndexPath isEqual:previousIndexPath]) { 177 | return; 178 | } 179 | 180 | if ([self.dataSource respondsToSelector:@selector(collectionView:itemAtIndexPath:canMoveToIndexPath:)] && 181 | ![self.dataSource collectionView:self.collectionView itemAtIndexPath:previousIndexPath canMoveToIndexPath:newIndexPath]) { 182 | return; 183 | } 184 | 185 | self.selectedItemIndexPath = newIndexPath; 186 | 187 | if ([self.dataSource respondsToSelector:@selector(collectionView:itemAtIndexPath:willMoveToIndexPath:)]) { 188 | [self.dataSource collectionView:self.collectionView itemAtIndexPath:previousIndexPath willMoveToIndexPath:newIndexPath]; 189 | } 190 | 191 | __weak typeof(self) weakSelf = self; 192 | [self.collectionView performBatchUpdates:^{ 193 | __strong typeof(self) strongSelf = weakSelf; 194 | if (strongSelf) { 195 | [strongSelf.collectionView deleteItemsAtIndexPaths:@[ previousIndexPath ]]; 196 | [strongSelf.collectionView insertItemsAtIndexPaths:@[ newIndexPath ]]; 197 | } 198 | } completion:^(BOOL finished) { 199 | __strong typeof(self) strongSelf = weakSelf; 200 | if ([strongSelf.dataSource respondsToSelector:@selector(collectionView:itemAtIndexPath:didMoveToIndexPath:)]) { 201 | [strongSelf.dataSource collectionView:strongSelf.collectionView itemAtIndexPath:previousIndexPath didMoveToIndexPath:newIndexPath]; 202 | } 203 | }]; 204 | } 205 | 206 | - (void)invalidatesScrollTimer { 207 | if (!self.displayLink.paused) { 208 | [self.displayLink invalidate]; 209 | } 210 | self.displayLink = nil; 211 | } 212 | 213 | - (void)setupScrollTimerInDirection:(LXScrollingDirection)direction { 214 | if (!self.displayLink.paused) { 215 | LXScrollingDirection oldDirection = [self.displayLink.LX_userInfo[kLXScrollingDirectionKey] integerValue]; 216 | 217 | if (direction == oldDirection) { 218 | return; 219 | } 220 | } 221 | 222 | [self invalidatesScrollTimer]; 223 | 224 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleScroll:)]; 225 | self.displayLink.LX_userInfo = @{ kLXScrollingDirectionKey : @(direction) }; 226 | 227 | [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 228 | } 229 | 230 | #pragma mark - Target/Action methods 231 | 232 | // Tight loop, allocate memory sparely, even if they are stack allocation. 233 | - (void)handleScroll:(CADisplayLink *)displayLink { 234 | LXScrollingDirection direction = (LXScrollingDirection)[displayLink.LX_userInfo[kLXScrollingDirectionKey] integerValue]; 235 | if (direction == LXScrollingDirectionUnknown) { 236 | return; 237 | } 238 | 239 | CGSize frameSize = self.collectionView.bounds.size; 240 | CGSize contentSize = self.collectionView.contentSize; 241 | CGPoint contentOffset = self.collectionView.contentOffset; 242 | UIEdgeInsets contentInset = self.collectionView.contentInset; 243 | // Important to have an integer `distance` as the `contentOffset` property automatically gets rounded 244 | // and it would diverge from the view's center resulting in a "cell is slipping away under finger"-bug. 245 | CGFloat distance = rint(self.scrollingSpeed * displayLink.duration); 246 | CGPoint translation = CGPointZero; 247 | 248 | switch(direction) { 249 | case LXScrollingDirectionUp: { 250 | distance = -distance; 251 | CGFloat minY = 0.0f - contentInset.top; 252 | 253 | if ((contentOffset.y + distance) <= minY) { 254 | distance = -contentOffset.y - contentInset.top; 255 | } 256 | 257 | translation = CGPointMake(0.0f, distance); 258 | } break; 259 | case LXScrollingDirectionDown: { 260 | CGFloat maxY = MAX(contentSize.height, frameSize.height) - frameSize.height + contentInset.bottom; 261 | 262 | if ((contentOffset.y + distance) >= maxY) { 263 | distance = maxY - contentOffset.y; 264 | } 265 | 266 | translation = CGPointMake(0.0f, distance); 267 | } break; 268 | case LXScrollingDirectionLeft: { 269 | distance = -distance; 270 | CGFloat minX = 0.0f - contentInset.left; 271 | 272 | if ((contentOffset.x + distance) <= minX) { 273 | distance = -contentOffset.x - contentInset.left; 274 | } 275 | 276 | translation = CGPointMake(distance, 0.0f); 277 | } break; 278 | case LXScrollingDirectionRight: { 279 | CGFloat maxX = MAX(contentSize.width, frameSize.width) - frameSize.width + contentInset.right; 280 | 281 | if ((contentOffset.x + distance) >= maxX) { 282 | distance = maxX - contentOffset.x; 283 | } 284 | 285 | translation = CGPointMake(distance, 0.0f); 286 | } break; 287 | default: { 288 | // Do nothing... 289 | } break; 290 | } 291 | 292 | self.currentViewCenter = LXS_CGPointAdd(self.currentViewCenter, translation); 293 | self.currentView.center = LXS_CGPointAdd(self.currentViewCenter, self.panTranslationInCollectionView); 294 | self.collectionView.contentOffset = LXS_CGPointAdd(contentOffset, translation); 295 | } 296 | 297 | 298 | - (void)handleLongPressGesture:(UILongPressGestureRecognizer *)gestureRecognizer { 299 | switch(gestureRecognizer.state) { 300 | case UIGestureRecognizerStateBegan: { 301 | NSIndexPath *currentIndexPath = [self.collectionView indexPathForItemAtPoint:[gestureRecognizer locationInView:self.collectionView]]; 302 | 303 | if (currentIndexPath == nil) { 304 | return; 305 | } 306 | 307 | if ([self.dataSource respondsToSelector:@selector(collectionView:canMoveItemAtIndexPath:)] && 308 | ![self.dataSource collectionView:self.collectionView canMoveItemAtIndexPath:currentIndexPath]) { 309 | return; 310 | } 311 | 312 | self.selectedItemIndexPath = currentIndexPath; 313 | 314 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:willBeginDraggingItemAtIndexPath:)]) { 315 | [self.delegate collectionView:self.collectionView layout:self willBeginDraggingItemAtIndexPath:self.selectedItemIndexPath]; 316 | } 317 | 318 | UICollectionViewCell *collectionViewCell = [self.collectionView cellForItemAtIndexPath:self.selectedItemIndexPath]; 319 | 320 | self.currentView = [[UIView alloc] initWithFrame:collectionViewCell.frame]; 321 | 322 | collectionViewCell.highlighted = YES; 323 | UIView *highlightedImageView = [collectionViewCell LX_snapshotView]; 324 | highlightedImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 325 | highlightedImageView.alpha = 1.0f; 326 | 327 | collectionViewCell.highlighted = NO; 328 | UIView *imageView = [collectionViewCell LX_snapshotView]; 329 | imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 330 | imageView.alpha = 0.0f; 331 | 332 | [self.currentView addSubview:imageView]; 333 | [self.currentView addSubview:highlightedImageView]; 334 | [self.collectionView addSubview:self.currentView]; 335 | 336 | self.currentViewCenter = self.currentView.center; 337 | 338 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:didBeginDraggingItemAtIndexPath:)]) { 339 | [self.delegate collectionView:self.collectionView layout:self didBeginDraggingItemAtIndexPath:self.selectedItemIndexPath]; 340 | } 341 | 342 | __weak typeof(self) weakSelf = self; 343 | [UIView 344 | animateWithDuration:0.3 345 | delay:0.0 346 | options:UIViewAnimationOptionBeginFromCurrentState 347 | animations:^{ 348 | __strong typeof(self) strongSelf = weakSelf; 349 | if (strongSelf) { 350 | strongSelf.currentView.transform = CGAffineTransformMakeScale(1.1f, 1.1f); 351 | highlightedImageView.alpha = 0.0f; 352 | imageView.alpha = 1.0f; 353 | } 354 | } 355 | completion:^(BOOL finished) { 356 | __strong typeof(self) strongSelf = weakSelf; 357 | if (strongSelf) { 358 | [highlightedImageView removeFromSuperview]; 359 | } 360 | }]; 361 | 362 | [self invalidateLayout]; 363 | } break; 364 | case UIGestureRecognizerStateCancelled: 365 | case UIGestureRecognizerStateEnded: { 366 | NSIndexPath *currentIndexPath = self.selectedItemIndexPath; 367 | 368 | if (currentIndexPath) { 369 | if ([self.delegate respondsToSelector:@selector(collectionView:layout:willEndDraggingItemAtIndexPath:)]) { 370 | [self.delegate collectionView:self.collectionView layout:self willEndDraggingItemAtIndexPath:currentIndexPath]; 371 | } 372 | 373 | self.selectedItemIndexPath = nil; 374 | self.currentViewCenter = CGPointZero; 375 | 376 | UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForItemAtIndexPath:currentIndexPath]; 377 | 378 | self.longPressGestureRecognizer.enabled = NO; 379 | 380 | __weak typeof(self) weakSelf = self; 381 | [UIView 382 | animateWithDuration:0.3 383 | delay:0.0 384 | options:UIViewAnimationOptionBeginFromCurrentState 385 | animations:^{ 386 | __strong typeof(self) strongSelf = weakSelf; 387 | if (strongSelf) { 388 | strongSelf.currentView.transform = CGAffineTransformMakeScale(1.0f, 1.0f); 389 | strongSelf.currentView.center = layoutAttributes.center; 390 | } 391 | } 392 | completion:^(BOOL finished) { 393 | 394 | self.longPressGestureRecognizer.enabled = YES; 395 | 396 | __strong typeof(self) strongSelf = weakSelf; 397 | if (strongSelf) { 398 | [strongSelf.currentView removeFromSuperview]; 399 | strongSelf.currentView = nil; 400 | [strongSelf invalidateLayout]; 401 | 402 | if ([strongSelf.delegate respondsToSelector:@selector(collectionView:layout:didEndDraggingItemAtIndexPath:)]) { 403 | [strongSelf.delegate collectionView:strongSelf.collectionView layout:strongSelf didEndDraggingItemAtIndexPath:currentIndexPath]; 404 | } 405 | } 406 | }]; 407 | } 408 | } break; 409 | 410 | default: break; 411 | } 412 | } 413 | 414 | - (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer { 415 | switch (gestureRecognizer.state) { 416 | case UIGestureRecognizerStateBegan: 417 | case UIGestureRecognizerStateChanged: { 418 | self.panTranslationInCollectionView = [gestureRecognizer translationInView:self.collectionView]; 419 | CGPoint viewCenter = self.currentView.center = LXS_CGPointAdd(self.currentViewCenter, self.panTranslationInCollectionView); 420 | 421 | [self invalidateLayoutIfNecessary]; 422 | 423 | switch (self.scrollDirection) { 424 | case UICollectionViewScrollDirectionVertical: { 425 | if (viewCenter.y < (CGRectGetMinY(self.collectionView.bounds) + self.scrollingTriggerEdgeInsets.top)) { 426 | [self setupScrollTimerInDirection:LXScrollingDirectionUp]; 427 | } else { 428 | if (viewCenter.y > (CGRectGetMaxY(self.collectionView.bounds) - self.scrollingTriggerEdgeInsets.bottom)) { 429 | [self setupScrollTimerInDirection:LXScrollingDirectionDown]; 430 | } else { 431 | [self invalidatesScrollTimer]; 432 | } 433 | } 434 | } break; 435 | case UICollectionViewScrollDirectionHorizontal: { 436 | if (viewCenter.x < (CGRectGetMinX(self.collectionView.bounds) + self.scrollingTriggerEdgeInsets.left)) { 437 | [self setupScrollTimerInDirection:LXScrollingDirectionLeft]; 438 | } else { 439 | if (viewCenter.x > (CGRectGetMaxX(self.collectionView.bounds) - self.scrollingTriggerEdgeInsets.right)) { 440 | [self setupScrollTimerInDirection:LXScrollingDirectionRight]; 441 | } else { 442 | [self invalidatesScrollTimer]; 443 | } 444 | } 445 | } break; 446 | } 447 | } break; 448 | case UIGestureRecognizerStateCancelled: 449 | case UIGestureRecognizerStateEnded: { 450 | [self invalidatesScrollTimer]; 451 | } break; 452 | default: { 453 | // Do nothing... 454 | } break; 455 | } 456 | } 457 | 458 | #pragma mark - UICollectionViewLayout overridden methods 459 | 460 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { 461 | NSArray *layoutAttributesForElementsInRect = [super layoutAttributesForElementsInRect:rect]; 462 | 463 | for (UICollectionViewLayoutAttributes *layoutAttributes in layoutAttributesForElementsInRect) { 464 | switch (layoutAttributes.representedElementCategory) { 465 | case UICollectionElementCategoryCell: { 466 | [self applyLayoutAttributes:layoutAttributes]; 467 | } break; 468 | default: { 469 | // Do nothing... 470 | } break; 471 | } 472 | } 473 | 474 | return layoutAttributesForElementsInRect; 475 | } 476 | 477 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { 478 | UICollectionViewLayoutAttributes *layoutAttributes = [super layoutAttributesForItemAtIndexPath:indexPath]; 479 | 480 | switch (layoutAttributes.representedElementCategory) { 481 | case UICollectionElementCategoryCell: { 482 | [self applyLayoutAttributes:layoutAttributes]; 483 | } break; 484 | default: { 485 | // Do nothing... 486 | } break; 487 | } 488 | 489 | return layoutAttributes; 490 | } 491 | 492 | #pragma mark - UIGestureRecognizerDelegate methods 493 | 494 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { 495 | if ([self.panGestureRecognizer isEqual:gestureRecognizer]) { 496 | return (self.selectedItemIndexPath != nil); 497 | } 498 | return YES; 499 | } 500 | 501 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 502 | if ([self.longPressGestureRecognizer isEqual:gestureRecognizer]) { 503 | return [self.panGestureRecognizer isEqual:otherGestureRecognizer]; 504 | } 505 | 506 | if ([self.panGestureRecognizer isEqual:gestureRecognizer]) { 507 | return [self.longPressGestureRecognizer isEqual:otherGestureRecognizer]; 508 | } 509 | 510 | return NO; 511 | } 512 | 513 | #pragma mark - Key-Value Observing methods 514 | 515 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 516 | if ([keyPath isEqualToString:kLXCollectionViewKeyPath]) { 517 | if (self.collectionView != nil) { 518 | [self setupCollectionView]; 519 | } else { 520 | [self invalidatesScrollTimer]; 521 | [self tearDownCollectionView]; 522 | } 523 | } 524 | } 525 | 526 | #pragma mark - Notifications 527 | 528 | - (void)handleApplicationWillResignActive:(NSNotification *)notification { 529 | self.panGestureRecognizer.enabled = NO; 530 | self.panGestureRecognizer.enabled = YES; 531 | } 532 | 533 | #pragma mark - Depreciated methods 534 | 535 | #pragma mark Starting from 0.1.0 536 | - (void)setUpGestureRecognizersOnCollectionView { 537 | // Do nothing... 538 | } 539 | 540 | @end 541 | -------------------------------------------------------------------------------- /ERPageController.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D85CD4861F13650700CE5640 /* EditMenuCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D85CD4761F13650700CE5640 /* EditMenuCollectionViewCell.m */; }; 11 | D85CD4871F13650700CE5640 /* EditMenuCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D85CD4771F13650700CE5640 /* EditMenuCollectionViewCell.xib */; }; 12 | D85CD4881F13650700CE5640 /* ERSegmentMenuController.m in Sources */ = {isa = PBXBuildFile; fileRef = D85CD4791F13650700CE5640 /* ERSegmentMenuController.m */; }; 13 | D85CD4891F13650700CE5640 /* LXReorderableCollectionViewFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = D85CD47B1F13650700CE5640 /* LXReorderableCollectionViewFlowLayout.m */; }; 14 | D85CD48A1F13650700CE5640 /* SectionHeader.m in Sources */ = {isa = PBXBuildFile; fileRef = D85CD47D1F13650700CE5640 /* SectionHeader.m */; }; 15 | D85CD48B1F13650700CE5640 /* ERPageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D85CD4801F13650700CE5640 /* ERPageViewController.m */; }; 16 | D85CD48C1F13650700CE5640 /* ERSegmentCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D85CD4821F13650700CE5640 /* ERSegmentCollectionViewCell.m */; }; 17 | D85CD48D1F13650700CE5640 /* ERSegmentCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D85CD4831F13650700CE5640 /* ERSegmentCollectionViewCell.xib */; }; 18 | D85CD48E1F13650700CE5640 /* ERSegmentController.m in Sources */ = {isa = PBXBuildFile; fileRef = D85CD4851F13650700CE5640 /* ERSegmentController.m */; }; 19 | D8F7C8111F0F5E5B000DB0E7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C8101F0F5E5B000DB0E7 /* main.m */; }; 20 | D8F7C8141F0F5E5B000DB0E7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C8131F0F5E5B000DB0E7 /* AppDelegate.m */; }; 21 | D8F7C8171F0F5E5B000DB0E7 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C8161F0F5E5B000DB0E7 /* ViewController.m */; }; 22 | D8F7C81A1F0F5E5B000DB0E7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D8F7C8181F0F5E5B000DB0E7 /* Main.storyboard */; }; 23 | D8F7C81C1F0F5E5B000DB0E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D8F7C81B1F0F5E5B000DB0E7 /* Assets.xcassets */; }; 24 | D8F7C81F1F0F5E5B000DB0E7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D8F7C81D1F0F5E5B000DB0E7 /* LaunchScreen.storyboard */; }; 25 | D8F7C82A1F0F5E5B000DB0E7 /* ERPageControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C8291F0F5E5B000DB0E7 /* ERPageControllerTests.m */; }; 26 | D8F7C8351F0F5E5B000DB0E7 /* ERPageControllerUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C8341F0F5E5B000DB0E7 /* ERPageControllerUITests.m */; }; 27 | D8F7C8661F0F60B8000DB0E7 /* PageFourViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C85F1F0F60B8000DB0E7 /* PageFourViewController.m */; }; 28 | D8F7C8671F0F60B8000DB0E7 /* PageOneViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C8611F0F60B8000DB0E7 /* PageOneViewController.m */; }; 29 | D8F7C8681F0F60B8000DB0E7 /* PageThreeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C8631F0F60B8000DB0E7 /* PageThreeViewController.m */; }; 30 | D8F7C8691F0F60B8000DB0E7 /* PageTwoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8F7C8651F0F60B8000DB0E7 /* PageTwoViewController.m */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | D8F7C8261F0F5E5B000DB0E7 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = D8F7C8041F0F5E5B000DB0E7 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = D8F7C80B1F0F5E5B000DB0E7; 39 | remoteInfo = ERPageController; 40 | }; 41 | D8F7C8311F0F5E5B000DB0E7 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = D8F7C8041F0F5E5B000DB0E7 /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = D8F7C80B1F0F5E5B000DB0E7; 46 | remoteInfo = ERPageController; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | D85CD4751F13650700CE5640 /* EditMenuCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditMenuCollectionViewCell.h; sourceTree = ""; }; 52 | D85CD4761F13650700CE5640 /* EditMenuCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EditMenuCollectionViewCell.m; sourceTree = ""; }; 53 | D85CD4771F13650700CE5640 /* EditMenuCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditMenuCollectionViewCell.xib; sourceTree = ""; }; 54 | D85CD4781F13650700CE5640 /* ERSegmentMenuController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ERSegmentMenuController.h; sourceTree = ""; }; 55 | D85CD4791F13650700CE5640 /* ERSegmentMenuController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ERSegmentMenuController.m; sourceTree = ""; }; 56 | D85CD47A1F13650700CE5640 /* LXReorderableCollectionViewFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXReorderableCollectionViewFlowLayout.h; sourceTree = ""; }; 57 | D85CD47B1F13650700CE5640 /* LXReorderableCollectionViewFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXReorderableCollectionViewFlowLayout.m; sourceTree = ""; }; 58 | D85CD47C1F13650700CE5640 /* SectionHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SectionHeader.h; sourceTree = ""; }; 59 | D85CD47D1F13650700CE5640 /* SectionHeader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SectionHeader.m; sourceTree = ""; }; 60 | D85CD47F1F13650700CE5640 /* ERPageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ERPageViewController.h; sourceTree = ""; }; 61 | D85CD4801F13650700CE5640 /* ERPageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ERPageViewController.m; sourceTree = ""; }; 62 | D85CD4811F13650700CE5640 /* ERSegmentCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ERSegmentCollectionViewCell.h; sourceTree = ""; }; 63 | D85CD4821F13650700CE5640 /* ERSegmentCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ERSegmentCollectionViewCell.m; sourceTree = ""; }; 64 | D85CD4831F13650700CE5640 /* ERSegmentCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ERSegmentCollectionViewCell.xib; sourceTree = ""; }; 65 | D85CD4841F13650700CE5640 /* ERSegmentController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ERSegmentController.h; sourceTree = ""; }; 66 | D85CD4851F13650700CE5640 /* ERSegmentController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ERSegmentController.m; sourceTree = ""; }; 67 | D8F7C80C1F0F5E5B000DB0E7 /* ERPageController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ERPageController.app; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | D8F7C8101F0F5E5B000DB0E7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 69 | D8F7C8121F0F5E5B000DB0E7 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 70 | D8F7C8131F0F5E5B000DB0E7 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 71 | D8F7C8151F0F5E5B000DB0E7 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 72 | D8F7C8161F0F5E5B000DB0E7 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 73 | D8F7C8191F0F5E5B000DB0E7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 74 | D8F7C81B1F0F5E5B000DB0E7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 75 | D8F7C81E1F0F5E5B000DB0E7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 76 | D8F7C8201F0F5E5B000DB0E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 77 | D8F7C8251F0F5E5B000DB0E7 /* ERPageControllerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ERPageControllerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | D8F7C8291F0F5E5B000DB0E7 /* ERPageControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ERPageControllerTests.m; sourceTree = ""; }; 79 | D8F7C82B1F0F5E5B000DB0E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 80 | D8F7C8301F0F5E5B000DB0E7 /* ERPageControllerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ERPageControllerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | D8F7C8341F0F5E5B000DB0E7 /* ERPageControllerUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ERPageControllerUITests.m; sourceTree = ""; }; 82 | D8F7C8361F0F5E5B000DB0E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 83 | D8F7C85E1F0F60B8000DB0E7 /* PageFourViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageFourViewController.h; sourceTree = ""; }; 84 | D8F7C85F1F0F60B8000DB0E7 /* PageFourViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PageFourViewController.m; sourceTree = ""; }; 85 | D8F7C8601F0F60B8000DB0E7 /* PageOneViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageOneViewController.h; sourceTree = ""; }; 86 | D8F7C8611F0F60B8000DB0E7 /* PageOneViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PageOneViewController.m; sourceTree = ""; }; 87 | D8F7C8621F0F60B8000DB0E7 /* PageThreeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageThreeViewController.h; sourceTree = ""; }; 88 | D8F7C8631F0F60B8000DB0E7 /* PageThreeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PageThreeViewController.m; sourceTree = ""; }; 89 | D8F7C8641F0F60B8000DB0E7 /* PageTwoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PageTwoViewController.h; sourceTree = ""; }; 90 | D8F7C8651F0F60B8000DB0E7 /* PageTwoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PageTwoViewController.m; sourceTree = ""; }; 91 | /* End PBXFileReference section */ 92 | 93 | /* Begin PBXFrameworksBuildPhase section */ 94 | D8F7C8091F0F5E5B000DB0E7 /* Frameworks */ = { 95 | isa = PBXFrameworksBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | D8F7C8221F0F5E5B000DB0E7 /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | D8F7C82D1F0F5E5B000DB0E7 /* Frameworks */ = { 109 | isa = PBXFrameworksBuildPhase; 110 | buildActionMask = 2147483647; 111 | files = ( 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | /* End PBXFrameworksBuildPhase section */ 116 | 117 | /* Begin PBXGroup section */ 118 | D85CD4731F13650700CE5640 /* ERPageController */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | D85CD4741F13650700CE5640 /* EditMenuControllers */, 122 | D85CD47E1F13650700CE5640 /* PageViewControllers */, 123 | ); 124 | path = ERPageController; 125 | sourceTree = ""; 126 | }; 127 | D85CD4741F13650700CE5640 /* EditMenuControllers */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | D85CD4751F13650700CE5640 /* EditMenuCollectionViewCell.h */, 131 | D85CD4761F13650700CE5640 /* EditMenuCollectionViewCell.m */, 132 | D85CD4771F13650700CE5640 /* EditMenuCollectionViewCell.xib */, 133 | D85CD4781F13650700CE5640 /* ERSegmentMenuController.h */, 134 | D85CD4791F13650700CE5640 /* ERSegmentMenuController.m */, 135 | D85CD47A1F13650700CE5640 /* LXReorderableCollectionViewFlowLayout.h */, 136 | D85CD47B1F13650700CE5640 /* LXReorderableCollectionViewFlowLayout.m */, 137 | D85CD47C1F13650700CE5640 /* SectionHeader.h */, 138 | D85CD47D1F13650700CE5640 /* SectionHeader.m */, 139 | ); 140 | path = EditMenuControllers; 141 | sourceTree = ""; 142 | }; 143 | D85CD47E1F13650700CE5640 /* PageViewControllers */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | D85CD47F1F13650700CE5640 /* ERPageViewController.h */, 147 | D85CD4801F13650700CE5640 /* ERPageViewController.m */, 148 | D85CD4811F13650700CE5640 /* ERSegmentCollectionViewCell.h */, 149 | D85CD4821F13650700CE5640 /* ERSegmentCollectionViewCell.m */, 150 | D85CD4831F13650700CE5640 /* ERSegmentCollectionViewCell.xib */, 151 | D85CD4841F13650700CE5640 /* ERSegmentController.h */, 152 | D85CD4851F13650700CE5640 /* ERSegmentController.m */, 153 | ); 154 | path = PageViewControllers; 155 | sourceTree = ""; 156 | }; 157 | D8F7C8031F0F5E5B000DB0E7 = { 158 | isa = PBXGroup; 159 | children = ( 160 | D8F7C80E1F0F5E5B000DB0E7 /* ERPageController */, 161 | D8F7C8281F0F5E5B000DB0E7 /* ERPageControllerTests */, 162 | D8F7C8331F0F5E5B000DB0E7 /* ERPageControllerUITests */, 163 | D8F7C80D1F0F5E5B000DB0E7 /* Products */, 164 | ); 165 | sourceTree = ""; 166 | }; 167 | D8F7C80D1F0F5E5B000DB0E7 /* Products */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | D8F7C80C1F0F5E5B000DB0E7 /* ERPageController.app */, 171 | D8F7C8251F0F5E5B000DB0E7 /* ERPageControllerTests.xctest */, 172 | D8F7C8301F0F5E5B000DB0E7 /* ERPageControllerUITests.xctest */, 173 | ); 174 | name = Products; 175 | sourceTree = ""; 176 | }; 177 | D8F7C80E1F0F5E5B000DB0E7 /* ERPageController */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | D85CD4731F13650700CE5640 /* ERPageController */, 181 | D8F7C8121F0F5E5B000DB0E7 /* AppDelegate.h */, 182 | D8F7C8131F0F5E5B000DB0E7 /* AppDelegate.m */, 183 | D8F7C8151F0F5E5B000DB0E7 /* ViewController.h */, 184 | D8F7C8161F0F5E5B000DB0E7 /* ViewController.m */, 185 | D8F7C85E1F0F60B8000DB0E7 /* PageFourViewController.h */, 186 | D8F7C85F1F0F60B8000DB0E7 /* PageFourViewController.m */, 187 | D8F7C8601F0F60B8000DB0E7 /* PageOneViewController.h */, 188 | D8F7C8611F0F60B8000DB0E7 /* PageOneViewController.m */, 189 | D8F7C8621F0F60B8000DB0E7 /* PageThreeViewController.h */, 190 | D8F7C8631F0F60B8000DB0E7 /* PageThreeViewController.m */, 191 | D8F7C8641F0F60B8000DB0E7 /* PageTwoViewController.h */, 192 | D8F7C8651F0F60B8000DB0E7 /* PageTwoViewController.m */, 193 | D8F7C8181F0F5E5B000DB0E7 /* Main.storyboard */, 194 | D8F7C81B1F0F5E5B000DB0E7 /* Assets.xcassets */, 195 | D8F7C81D1F0F5E5B000DB0E7 /* LaunchScreen.storyboard */, 196 | D8F7C8201F0F5E5B000DB0E7 /* Info.plist */, 197 | D8F7C80F1F0F5E5B000DB0E7 /* Supporting Files */, 198 | ); 199 | path = ERPageController; 200 | sourceTree = ""; 201 | }; 202 | D8F7C80F1F0F5E5B000DB0E7 /* Supporting Files */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | D8F7C8101F0F5E5B000DB0E7 /* main.m */, 206 | ); 207 | name = "Supporting Files"; 208 | sourceTree = ""; 209 | }; 210 | D8F7C8281F0F5E5B000DB0E7 /* ERPageControllerTests */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | D8F7C8291F0F5E5B000DB0E7 /* ERPageControllerTests.m */, 214 | D8F7C82B1F0F5E5B000DB0E7 /* Info.plist */, 215 | ); 216 | path = ERPageControllerTests; 217 | sourceTree = ""; 218 | }; 219 | D8F7C8331F0F5E5B000DB0E7 /* ERPageControllerUITests */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | D8F7C8341F0F5E5B000DB0E7 /* ERPageControllerUITests.m */, 223 | D8F7C8361F0F5E5B000DB0E7 /* Info.plist */, 224 | ); 225 | path = ERPageControllerUITests; 226 | sourceTree = ""; 227 | }; 228 | /* End PBXGroup section */ 229 | 230 | /* Begin PBXNativeTarget section */ 231 | D8F7C80B1F0F5E5B000DB0E7 /* ERPageController */ = { 232 | isa = PBXNativeTarget; 233 | buildConfigurationList = D8F7C8391F0F5E5B000DB0E7 /* Build configuration list for PBXNativeTarget "ERPageController" */; 234 | buildPhases = ( 235 | D8F7C8081F0F5E5B000DB0E7 /* Sources */, 236 | D8F7C8091F0F5E5B000DB0E7 /* Frameworks */, 237 | D8F7C80A1F0F5E5B000DB0E7 /* Resources */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | ); 243 | name = ERPageController; 244 | productName = ERPageController; 245 | productReference = D8F7C80C1F0F5E5B000DB0E7 /* ERPageController.app */; 246 | productType = "com.apple.product-type.application"; 247 | }; 248 | D8F7C8241F0F5E5B000DB0E7 /* ERPageControllerTests */ = { 249 | isa = PBXNativeTarget; 250 | buildConfigurationList = D8F7C83C1F0F5E5B000DB0E7 /* Build configuration list for PBXNativeTarget "ERPageControllerTests" */; 251 | buildPhases = ( 252 | D8F7C8211F0F5E5B000DB0E7 /* Sources */, 253 | D8F7C8221F0F5E5B000DB0E7 /* Frameworks */, 254 | D8F7C8231F0F5E5B000DB0E7 /* Resources */, 255 | ); 256 | buildRules = ( 257 | ); 258 | dependencies = ( 259 | D8F7C8271F0F5E5B000DB0E7 /* PBXTargetDependency */, 260 | ); 261 | name = ERPageControllerTests; 262 | productName = ERPageControllerTests; 263 | productReference = D8F7C8251F0F5E5B000DB0E7 /* ERPageControllerTests.xctest */; 264 | productType = "com.apple.product-type.bundle.unit-test"; 265 | }; 266 | D8F7C82F1F0F5E5B000DB0E7 /* ERPageControllerUITests */ = { 267 | isa = PBXNativeTarget; 268 | buildConfigurationList = D8F7C83F1F0F5E5B000DB0E7 /* Build configuration list for PBXNativeTarget "ERPageControllerUITests" */; 269 | buildPhases = ( 270 | D8F7C82C1F0F5E5B000DB0E7 /* Sources */, 271 | D8F7C82D1F0F5E5B000DB0E7 /* Frameworks */, 272 | D8F7C82E1F0F5E5B000DB0E7 /* Resources */, 273 | ); 274 | buildRules = ( 275 | ); 276 | dependencies = ( 277 | D8F7C8321F0F5E5B000DB0E7 /* PBXTargetDependency */, 278 | ); 279 | name = ERPageControllerUITests; 280 | productName = ERPageControllerUITests; 281 | productReference = D8F7C8301F0F5E5B000DB0E7 /* ERPageControllerUITests.xctest */; 282 | productType = "com.apple.product-type.bundle.ui-testing"; 283 | }; 284 | /* End PBXNativeTarget section */ 285 | 286 | /* Begin PBXProject section */ 287 | D8F7C8041F0F5E5B000DB0E7 /* Project object */ = { 288 | isa = PBXProject; 289 | attributes = { 290 | LastUpgradeCheck = 0830; 291 | ORGANIZATIONNAME = "胡广宇"; 292 | TargetAttributes = { 293 | D8F7C80B1F0F5E5B000DB0E7 = { 294 | CreatedOnToolsVersion = 8.3.3; 295 | DevelopmentTeam = 7W6345K7JN; 296 | ProvisioningStyle = Automatic; 297 | }; 298 | D8F7C8241F0F5E5B000DB0E7 = { 299 | CreatedOnToolsVersion = 8.3.3; 300 | DevelopmentTeam = 7W6345K7JN; 301 | ProvisioningStyle = Automatic; 302 | TestTargetID = D8F7C80B1F0F5E5B000DB0E7; 303 | }; 304 | D8F7C82F1F0F5E5B000DB0E7 = { 305 | CreatedOnToolsVersion = 8.3.3; 306 | DevelopmentTeam = 7W6345K7JN; 307 | ProvisioningStyle = Automatic; 308 | TestTargetID = D8F7C80B1F0F5E5B000DB0E7; 309 | }; 310 | }; 311 | }; 312 | buildConfigurationList = D8F7C8071F0F5E5B000DB0E7 /* Build configuration list for PBXProject "ERPageController" */; 313 | compatibilityVersion = "Xcode 3.2"; 314 | developmentRegion = English; 315 | hasScannedForEncodings = 0; 316 | knownRegions = ( 317 | en, 318 | Base, 319 | ); 320 | mainGroup = D8F7C8031F0F5E5B000DB0E7; 321 | productRefGroup = D8F7C80D1F0F5E5B000DB0E7 /* Products */; 322 | projectDirPath = ""; 323 | projectRoot = ""; 324 | targets = ( 325 | D8F7C80B1F0F5E5B000DB0E7 /* ERPageController */, 326 | D8F7C8241F0F5E5B000DB0E7 /* ERPageControllerTests */, 327 | D8F7C82F1F0F5E5B000DB0E7 /* ERPageControllerUITests */, 328 | ); 329 | }; 330 | /* End PBXProject section */ 331 | 332 | /* Begin PBXResourcesBuildPhase section */ 333 | D8F7C80A1F0F5E5B000DB0E7 /* Resources */ = { 334 | isa = PBXResourcesBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | D8F7C81F1F0F5E5B000DB0E7 /* LaunchScreen.storyboard in Resources */, 338 | D85CD48D1F13650700CE5640 /* ERSegmentCollectionViewCell.xib in Resources */, 339 | D85CD4871F13650700CE5640 /* EditMenuCollectionViewCell.xib in Resources */, 340 | D8F7C81C1F0F5E5B000DB0E7 /* Assets.xcassets in Resources */, 341 | D8F7C81A1F0F5E5B000DB0E7 /* Main.storyboard in Resources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | D8F7C8231F0F5E5B000DB0E7 /* Resources */ = { 346 | isa = PBXResourcesBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | ); 350 | runOnlyForDeploymentPostprocessing = 0; 351 | }; 352 | D8F7C82E1F0F5E5B000DB0E7 /* Resources */ = { 353 | isa = PBXResourcesBuildPhase; 354 | buildActionMask = 2147483647; 355 | files = ( 356 | ); 357 | runOnlyForDeploymentPostprocessing = 0; 358 | }; 359 | /* End PBXResourcesBuildPhase section */ 360 | 361 | /* Begin PBXSourcesBuildPhase section */ 362 | D8F7C8081F0F5E5B000DB0E7 /* Sources */ = { 363 | isa = PBXSourcesBuildPhase; 364 | buildActionMask = 2147483647; 365 | files = ( 366 | D85CD4891F13650700CE5640 /* LXReorderableCollectionViewFlowLayout.m in Sources */, 367 | D8F7C8661F0F60B8000DB0E7 /* PageFourViewController.m in Sources */, 368 | D85CD4861F13650700CE5640 /* EditMenuCollectionViewCell.m in Sources */, 369 | D85CD48A1F13650700CE5640 /* SectionHeader.m in Sources */, 370 | D85CD48C1F13650700CE5640 /* ERSegmentCollectionViewCell.m in Sources */, 371 | D8F7C8171F0F5E5B000DB0E7 /* ViewController.m in Sources */, 372 | D8F7C8671F0F60B8000DB0E7 /* PageOneViewController.m in Sources */, 373 | D8F7C8691F0F60B8000DB0E7 /* PageTwoViewController.m in Sources */, 374 | D8F7C8681F0F60B8000DB0E7 /* PageThreeViewController.m in Sources */, 375 | D85CD48B1F13650700CE5640 /* ERPageViewController.m in Sources */, 376 | D85CD4881F13650700CE5640 /* ERSegmentMenuController.m in Sources */, 377 | D8F7C8141F0F5E5B000DB0E7 /* AppDelegate.m in Sources */, 378 | D85CD48E1F13650700CE5640 /* ERSegmentController.m in Sources */, 379 | D8F7C8111F0F5E5B000DB0E7 /* main.m in Sources */, 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | D8F7C8211F0F5E5B000DB0E7 /* Sources */ = { 384 | isa = PBXSourcesBuildPhase; 385 | buildActionMask = 2147483647; 386 | files = ( 387 | D8F7C82A1F0F5E5B000DB0E7 /* ERPageControllerTests.m in Sources */, 388 | ); 389 | runOnlyForDeploymentPostprocessing = 0; 390 | }; 391 | D8F7C82C1F0F5E5B000DB0E7 /* Sources */ = { 392 | isa = PBXSourcesBuildPhase; 393 | buildActionMask = 2147483647; 394 | files = ( 395 | D8F7C8351F0F5E5B000DB0E7 /* ERPageControllerUITests.m in Sources */, 396 | ); 397 | runOnlyForDeploymentPostprocessing = 0; 398 | }; 399 | /* End PBXSourcesBuildPhase section */ 400 | 401 | /* Begin PBXTargetDependency section */ 402 | D8F7C8271F0F5E5B000DB0E7 /* PBXTargetDependency */ = { 403 | isa = PBXTargetDependency; 404 | target = D8F7C80B1F0F5E5B000DB0E7 /* ERPageController */; 405 | targetProxy = D8F7C8261F0F5E5B000DB0E7 /* PBXContainerItemProxy */; 406 | }; 407 | D8F7C8321F0F5E5B000DB0E7 /* PBXTargetDependency */ = { 408 | isa = PBXTargetDependency; 409 | target = D8F7C80B1F0F5E5B000DB0E7 /* ERPageController */; 410 | targetProxy = D8F7C8311F0F5E5B000DB0E7 /* PBXContainerItemProxy */; 411 | }; 412 | /* End PBXTargetDependency section */ 413 | 414 | /* Begin PBXVariantGroup section */ 415 | D8F7C8181F0F5E5B000DB0E7 /* Main.storyboard */ = { 416 | isa = PBXVariantGroup; 417 | children = ( 418 | D8F7C8191F0F5E5B000DB0E7 /* Base */, 419 | ); 420 | name = Main.storyboard; 421 | sourceTree = ""; 422 | }; 423 | D8F7C81D1F0F5E5B000DB0E7 /* LaunchScreen.storyboard */ = { 424 | isa = PBXVariantGroup; 425 | children = ( 426 | D8F7C81E1F0F5E5B000DB0E7 /* Base */, 427 | ); 428 | name = LaunchScreen.storyboard; 429 | sourceTree = ""; 430 | }; 431 | /* End PBXVariantGroup section */ 432 | 433 | /* Begin XCBuildConfiguration section */ 434 | D8F7C8371F0F5E5B000DB0E7 /* Debug */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | ALWAYS_SEARCH_USER_PATHS = NO; 438 | CLANG_ANALYZER_NONNULL = YES; 439 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 440 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 441 | CLANG_CXX_LIBRARY = "libc++"; 442 | CLANG_ENABLE_MODULES = YES; 443 | CLANG_ENABLE_OBJC_ARC = YES; 444 | CLANG_WARN_BOOL_CONVERSION = YES; 445 | CLANG_WARN_CONSTANT_CONVERSION = YES; 446 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 447 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 448 | CLANG_WARN_EMPTY_BODY = YES; 449 | CLANG_WARN_ENUM_CONVERSION = YES; 450 | CLANG_WARN_INFINITE_RECURSION = YES; 451 | CLANG_WARN_INT_CONVERSION = YES; 452 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 453 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 454 | CLANG_WARN_UNREACHABLE_CODE = YES; 455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 456 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 457 | COPY_PHASE_STRIP = NO; 458 | DEBUG_INFORMATION_FORMAT = dwarf; 459 | ENABLE_STRICT_OBJC_MSGSEND = YES; 460 | ENABLE_TESTABILITY = YES; 461 | GCC_C_LANGUAGE_STANDARD = gnu99; 462 | GCC_DYNAMIC_NO_PIC = NO; 463 | GCC_NO_COMMON_BLOCKS = YES; 464 | GCC_OPTIMIZATION_LEVEL = 0; 465 | GCC_PREPROCESSOR_DEFINITIONS = ( 466 | "DEBUG=1", 467 | "$(inherited)", 468 | ); 469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 471 | GCC_WARN_UNDECLARED_SELECTOR = YES; 472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 473 | GCC_WARN_UNUSED_FUNCTION = YES; 474 | GCC_WARN_UNUSED_VARIABLE = YES; 475 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 476 | MTL_ENABLE_DEBUG_INFO = YES; 477 | ONLY_ACTIVE_ARCH = YES; 478 | SDKROOT = iphoneos; 479 | TARGETED_DEVICE_FAMILY = "1,2"; 480 | }; 481 | name = Debug; 482 | }; 483 | D8F7C8381F0F5E5B000DB0E7 /* Release */ = { 484 | isa = XCBuildConfiguration; 485 | buildSettings = { 486 | ALWAYS_SEARCH_USER_PATHS = NO; 487 | CLANG_ANALYZER_NONNULL = YES; 488 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 489 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 490 | CLANG_CXX_LIBRARY = "libc++"; 491 | CLANG_ENABLE_MODULES = YES; 492 | CLANG_ENABLE_OBJC_ARC = YES; 493 | CLANG_WARN_BOOL_CONVERSION = YES; 494 | CLANG_WARN_CONSTANT_CONVERSION = YES; 495 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 496 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 497 | CLANG_WARN_EMPTY_BODY = YES; 498 | CLANG_WARN_ENUM_CONVERSION = YES; 499 | CLANG_WARN_INFINITE_RECURSION = YES; 500 | CLANG_WARN_INT_CONVERSION = YES; 501 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 502 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 503 | CLANG_WARN_UNREACHABLE_CODE = YES; 504 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 505 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 506 | COPY_PHASE_STRIP = NO; 507 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 508 | ENABLE_NS_ASSERTIONS = NO; 509 | ENABLE_STRICT_OBJC_MSGSEND = YES; 510 | GCC_C_LANGUAGE_STANDARD = gnu99; 511 | GCC_NO_COMMON_BLOCKS = YES; 512 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 513 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 514 | GCC_WARN_UNDECLARED_SELECTOR = YES; 515 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 516 | GCC_WARN_UNUSED_FUNCTION = YES; 517 | GCC_WARN_UNUSED_VARIABLE = YES; 518 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 519 | MTL_ENABLE_DEBUG_INFO = NO; 520 | SDKROOT = iphoneos; 521 | TARGETED_DEVICE_FAMILY = "1,2"; 522 | VALIDATE_PRODUCT = YES; 523 | }; 524 | name = Release; 525 | }; 526 | D8F7C83A1F0F5E5B000DB0E7 /* Debug */ = { 527 | isa = XCBuildConfiguration; 528 | buildSettings = { 529 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 530 | DEVELOPMENT_TEAM = 7W6345K7JN; 531 | INFOPLIST_FILE = ERPageController/Info.plist; 532 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 533 | PRODUCT_BUNDLE_IDENTIFIER = com.erhu.ERPageController; 534 | PRODUCT_NAME = "$(TARGET_NAME)"; 535 | }; 536 | name = Debug; 537 | }; 538 | D8F7C83B1F0F5E5B000DB0E7 /* Release */ = { 539 | isa = XCBuildConfiguration; 540 | buildSettings = { 541 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 542 | DEVELOPMENT_TEAM = 7W6345K7JN; 543 | INFOPLIST_FILE = ERPageController/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 545 | PRODUCT_BUNDLE_IDENTIFIER = com.erhu.ERPageController; 546 | PRODUCT_NAME = "$(TARGET_NAME)"; 547 | }; 548 | name = Release; 549 | }; 550 | D8F7C83D1F0F5E5B000DB0E7 /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | BUNDLE_LOADER = "$(TEST_HOST)"; 554 | DEVELOPMENT_TEAM = 7W6345K7JN; 555 | INFOPLIST_FILE = ERPageControllerTests/Info.plist; 556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 557 | PRODUCT_BUNDLE_IDENTIFIER = com.erhu.ERPageControllerTests; 558 | PRODUCT_NAME = "$(TARGET_NAME)"; 559 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ERPageController.app/ERPageController"; 560 | }; 561 | name = Debug; 562 | }; 563 | D8F7C83E1F0F5E5B000DB0E7 /* Release */ = { 564 | isa = XCBuildConfiguration; 565 | buildSettings = { 566 | BUNDLE_LOADER = "$(TEST_HOST)"; 567 | DEVELOPMENT_TEAM = 7W6345K7JN; 568 | INFOPLIST_FILE = ERPageControllerTests/Info.plist; 569 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 570 | PRODUCT_BUNDLE_IDENTIFIER = com.erhu.ERPageControllerTests; 571 | PRODUCT_NAME = "$(TARGET_NAME)"; 572 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ERPageController.app/ERPageController"; 573 | }; 574 | name = Release; 575 | }; 576 | D8F7C8401F0F5E5B000DB0E7 /* Debug */ = { 577 | isa = XCBuildConfiguration; 578 | buildSettings = { 579 | DEVELOPMENT_TEAM = 7W6345K7JN; 580 | INFOPLIST_FILE = ERPageControllerUITests/Info.plist; 581 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 582 | PRODUCT_BUNDLE_IDENTIFIER = com.erhu.ERPageControllerUITests; 583 | PRODUCT_NAME = "$(TARGET_NAME)"; 584 | TEST_TARGET_NAME = ERPageController; 585 | }; 586 | name = Debug; 587 | }; 588 | D8F7C8411F0F5E5B000DB0E7 /* Release */ = { 589 | isa = XCBuildConfiguration; 590 | buildSettings = { 591 | DEVELOPMENT_TEAM = 7W6345K7JN; 592 | INFOPLIST_FILE = ERPageControllerUITests/Info.plist; 593 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 594 | PRODUCT_BUNDLE_IDENTIFIER = com.erhu.ERPageControllerUITests; 595 | PRODUCT_NAME = "$(TARGET_NAME)"; 596 | TEST_TARGET_NAME = ERPageController; 597 | }; 598 | name = Release; 599 | }; 600 | /* End XCBuildConfiguration section */ 601 | 602 | /* Begin XCConfigurationList section */ 603 | D8F7C8071F0F5E5B000DB0E7 /* Build configuration list for PBXProject "ERPageController" */ = { 604 | isa = XCConfigurationList; 605 | buildConfigurations = ( 606 | D8F7C8371F0F5E5B000DB0E7 /* Debug */, 607 | D8F7C8381F0F5E5B000DB0E7 /* Release */, 608 | ); 609 | defaultConfigurationIsVisible = 0; 610 | defaultConfigurationName = Release; 611 | }; 612 | D8F7C8391F0F5E5B000DB0E7 /* Build configuration list for PBXNativeTarget "ERPageController" */ = { 613 | isa = XCConfigurationList; 614 | buildConfigurations = ( 615 | D8F7C83A1F0F5E5B000DB0E7 /* Debug */, 616 | D8F7C83B1F0F5E5B000DB0E7 /* Release */, 617 | ); 618 | defaultConfigurationIsVisible = 0; 619 | defaultConfigurationName = Release; 620 | }; 621 | D8F7C83C1F0F5E5B000DB0E7 /* Build configuration list for PBXNativeTarget "ERPageControllerTests" */ = { 622 | isa = XCConfigurationList; 623 | buildConfigurations = ( 624 | D8F7C83D1F0F5E5B000DB0E7 /* Debug */, 625 | D8F7C83E1F0F5E5B000DB0E7 /* Release */, 626 | ); 627 | defaultConfigurationIsVisible = 0; 628 | defaultConfigurationName = Release; 629 | }; 630 | D8F7C83F1F0F5E5B000DB0E7 /* Build configuration list for PBXNativeTarget "ERPageControllerUITests" */ = { 631 | isa = XCConfigurationList; 632 | buildConfigurations = ( 633 | D8F7C8401F0F5E5B000DB0E7 /* Debug */, 634 | D8F7C8411F0F5E5B000DB0E7 /* Release */, 635 | ); 636 | defaultConfigurationIsVisible = 0; 637 | defaultConfigurationName = Release; 638 | }; 639 | /* End XCConfigurationList section */ 640 | }; 641 | rootObject = D8F7C8041F0F5E5B000DB0E7 /* Project object */; 642 | } 643 | --------------------------------------------------------------------------------