├── LXScrollContentView.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── LXScrollContentView
├── Classes
│ └── Controller
│ │ ├── LXFirstViewController.h
│ │ ├── LXSecondViewController.h
│ │ ├── LXTestViewController.h
│ │ ├── LXSecondViewController.m
│ │ ├── LXTestViewController.m
│ │ └── LXFirstViewController.m
├── AppDelegate.h
├── main.m
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
└── AppDelegate.m
├── LXScrollContentView.podspec
├── LXScrollContentViewTests
├── Info.plist
└── LXScrollContentViewTests.m
├── LXScrollContentViewUITests
├── Info.plist
└── LXScrollContentViewUITests.m
├── LICENSE
├── .gitignore
├── LXScrollContentViewLib
├── LXScrollContentView.h
├── LXSegmentTitleView.h
├── LXSegmentTitleView.m
└── LXScrollContentView.m
└── README.md
/LXScrollContentView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LXScrollContentView/Classes/Controller/LXFirstViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // LXFirstViewController.h
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LXFirstViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/LXScrollContentView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LXScrollContentView/Classes/Controller/LXSecondViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // LXSecondViewController.h
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LXSecondViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/LXScrollContentView/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
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 |
--------------------------------------------------------------------------------
/LXScrollContentView/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
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 |
--------------------------------------------------------------------------------
/LXScrollContentView/Classes/Controller/LXTestViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // LXTestViewController.h
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LXTestViewController : UIViewController
12 |
13 | @property (nonatomic, copy) NSString *category;
14 |
15 | @property (nonatomic, assign) NSUInteger index;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/LXScrollContentView/Classes/Controller/LXSecondViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // LXSecondViewController.m
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import "LXSecondViewController.h"
10 |
11 | @interface LXSecondViewController ()
12 |
13 | @end
14 |
15 | @implementation LXSecondViewController
16 |
17 | - (void)viewDidLoad {
18 | [super viewDidLoad];
19 | // Do any additional setup after loading the view.
20 | }
21 |
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/LXScrollContentView.podspec:
--------------------------------------------------------------------------------
1 |
2 |
3 | Pod::Spec.new do |s|
4 | s.name = 'LXScrollContentView'
5 | s.version = '1.7.2'
6 | s.summary = 'A similar to the NetEase news home left and right to switch the scroll frame'
7 | s.homepage = 'https://github.com/LiuXingCode/LXScrollContentView'
8 | s.license = 'MIT'
9 | s.authors = {'xing.liu' => 'liuxinghenau@163.com'}
10 | s.platform = :ios, '6.0'
11 | s.source = {:git => 'https://github.com/LiuXingCode/LXScrollContentView.git', :tag => s.version}
12 | s.source_files = 'LXScrollContentViewLib/**/*.{h,m}'
13 | s.requires_arc = true
14 | end
15 |
16 |
--------------------------------------------------------------------------------
/LXScrollContentViewTests/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 |
--------------------------------------------------------------------------------
/LXScrollContentViewUITests/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 |
--------------------------------------------------------------------------------
/LXScrollContentView/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 xing.liu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LXScrollContentViewTests/LXScrollContentViewTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // LXScrollContentViewTests.m
3 | // LXScrollContentViewTests
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LXScrollContentViewTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation LXScrollContentViewTests
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 |
--------------------------------------------------------------------------------
/LXScrollContentView/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 |
38 |
39 |
--------------------------------------------------------------------------------
/LXScrollContentViewUITests/LXScrollContentViewUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // LXScrollContentViewUITests.m
3 | // LXScrollContentViewUITests
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LXScrollContentViewUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation LXScrollContentViewUITests
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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | # CocoaPods
31 | #
32 | # We recommend against adding the Pods directory to your .gitignore. However
33 | # you should judge for yourself, the pros and cons are mentioned at:
34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
35 | #
36 | # Pods/
37 |
38 | # Carthage
39 | #
40 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
41 | # Carthage/Checkouts
42 |
43 | Carthage/Build
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
51 |
52 | fastlane/report.xml
53 | fastlane/screenshots
54 |
55 | #Code Injection
56 | #
57 | # After new code Injection tools there's a generated folder /iOSInjectionProject
58 | # https://github.com/johnno1962/injectionforxcode
59 |
60 | iOSInjectionProject/
61 |
--------------------------------------------------------------------------------
/LXScrollContentView/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 |
--------------------------------------------------------------------------------
/LXScrollContentViewLib/LXScrollContentView.h:
--------------------------------------------------------------------------------
1 | //
2 | // LXScrollContentView.h
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class LXScrollContentView;
12 |
13 | @protocol LXScrollContentViewDataSource
14 |
15 | - (UIEdgeInsets)scrollContentView:(LXScrollContentView *)scrollContentView childVcContentInsetAtIndex:(NSInteger)index;
16 |
17 | @required
18 |
19 | - (NSInteger)numberOfchildVcsInScrollContentView:(LXScrollContentView *)scrollContentView;
20 |
21 | - (UIViewController *)scrollContentView:(LXScrollContentView *)scrollContentView childVcAtIndex:(NSInteger)index;
22 |
23 | - (UIViewController *)parentVcInScrollContentView:(LXScrollContentView *)scrollContentView;
24 |
25 | @end
26 |
27 | @protocol LXScrollContentViewDelegate
28 |
29 | @optional
30 |
31 | - (void)contentViewDidScroll:(LXScrollContentView *)contentView
32 | fromIndex:(NSInteger)fromIndex
33 | toIndex:(NSInteger)toIndex
34 | progress:(float)progress;
35 |
36 | - (void)contentViewDidEndDecelerating:(LXScrollContentView *)contentView
37 | startIndex:(NSInteger)startIndex
38 | endIndex:(NSInteger)endIndex;
39 |
40 | - (void)childVcViewWillAppearAtIndex:(NSUInteger)index;
41 |
42 | - (void)childVcViewDidDisappearAtIndex:(NSUInteger)index;
43 |
44 | @end
45 |
46 | @interface LXScrollContentView : UIView
47 |
48 | - (void)reloadData;
49 |
50 | @property (nonatomic, weak) id dataSource;
51 |
52 | @property (nonatomic, weak) id delegate;
53 |
54 | @property (nonatomic, assign) NSUInteger currentIndex;
55 |
56 | @property (nonatomic, assign) BOOL preloadNearVcs;//提前加载当前vc
57 |
58 | @property (nonatomic, weak, readonly) UICollectionView *collectionView;
59 |
60 | @end
61 |
--------------------------------------------------------------------------------
/LXScrollContentViewLib/LXSegmentTitleView.h:
--------------------------------------------------------------------------------
1 | //
2 | // LXSegmentTitleView.h
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/31.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class LXSegmentTitleView;
12 |
13 | @protocol LXSegmentTitleViewDelegate
14 |
15 | @optional
16 |
17 | - (void)segmentTitleView:(LXSegmentTitleView *)segmentView
18 | selectedIndex:(NSInteger)selectedIndex
19 | lastSelectedIndex:(NSInteger)lastSelectedIndex;
20 |
21 | @end
22 |
23 | @protocol LXSegmentTitleViewDataSource
24 |
25 | @optional
26 |
27 | - (NSArray *)segmentTitlesOfSegmentTitleView:(LXSegmentTitleView *)segmentView;
28 |
29 | @end
30 |
31 | @interface LXSegmentTitleView : UIView
32 |
33 | - (void)reloadData;
34 |
35 | @property (nonatomic, weak) id delegate;
36 |
37 | @property (nonatomic, weak) id dataSource;
38 |
39 | /**
40 | 文字未选中颜色,默认black
41 | */
42 | @property (nonatomic, strong) UIColor *titleNormalColor;
43 |
44 | /**
45 | 文字选中和下方滚动条颜色,默认red
46 | */
47 | @property (nonatomic, strong) UIColor *titleSelectedColor;
48 |
49 |
50 | /**
51 | 第几个标题处于选中状态,默认第0个
52 | */
53 | @property (nonatomic, assign) NSInteger selectedIndex;
54 |
55 |
56 | /**
57 | 标题font,默认[UIFont systemFontOfSize:14.f]
58 | */
59 | @property (nonatomic, strong) UIFont *titleFont;
60 |
61 |
62 | /**
63 | 下方指示条颜色,默认red
64 | */
65 | @property (nonatomic, strong) UIColor *indicatorColor;
66 |
67 | /**
68 | 下方滚动指示条高度,默认2.f
69 | */
70 | @property (nonatomic, assign) CGFloat indicatorHeight;
71 |
72 |
73 | /**
74 | 下方指示条延伸宽度,默认5.f
75 | */
76 | @property (nonatomic, assign) CGFloat indicatorExtraW;
77 |
78 |
79 | /**
80 | 下方指示条距离底部距离,默认为0
81 | */
82 | @property (nonatomic, assign) CGFloat indicatorBottomMargin;
83 |
84 | /**
85 | 每个item之间最小间隔,默认25.f
86 | */
87 | @property (nonatomic, assign) CGFloat itemMinMargin;
88 |
89 | /**
90 | 选项卡标题数组
91 | */
92 | @property (nonatomic, copy) NSArray *segmentTitles;
93 |
94 | @end
95 |
--------------------------------------------------------------------------------
/LXScrollContentView/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
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 |
--------------------------------------------------------------------------------
/LXScrollContentView/Classes/Controller/LXTestViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // LXTestViewController.m
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import "LXTestViewController.h"
10 |
11 | static NSString *kCellIdentifier = @"kCellIdentifier";
12 |
13 | @interface LXTestViewController ()
14 |
15 | @property (nonatomic, strong) UITableView *tableView;
16 |
17 | @end
18 |
19 | @implementation LXTestViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 | NSLog(@"viewDidLoad -- %ld", self.index);
24 |
25 | [self setupUI];
26 | }
27 |
28 | - (void)setupUI {
29 | UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 100)];
30 | headerLabel.backgroundColor = [UIColor redColor];
31 | headerLabel.text = [NSString stringWithFormat:@"%@ %ld", self.category, self.index];
32 | headerLabel.font = [UIFont boldSystemFontOfSize:40.f];
33 | headerLabel.textAlignment = NSTextAlignmentCenter;
34 | self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
35 |
36 | if (@available(iOS 11.0, *)) {
37 | self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
38 | }
39 |
40 | self.tableView.tableHeaderView = headerLabel;
41 | self.tableView.delegate = self;
42 | self.tableView.dataSource = self;
43 | [self.view addSubview:self.tableView];
44 | }
45 |
46 | - (void)viewWillLayoutSubviews{
47 | [super viewWillLayoutSubviews];
48 | self.tableView.frame = self.view.bounds;
49 | }
50 |
51 | - (void)viewWillAppear:(BOOL)animated {
52 | [super viewWillAppear:animated];
53 | NSLog(@"viewWillAppear -- %ld", self.index);
54 | }
55 |
56 | - (void)viewWillDisappear:(BOOL)animated {
57 | [super viewWillDisappear:animated];
58 | NSLog(@"viewWillDisappear -- %ld", self.index);
59 | }
60 |
61 | #pragma mark - UITableViewDataSource
62 |
63 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
64 | return 100;
65 | }
66 |
67 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
68 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
69 | if (cell == nil) {
70 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellIdentifier];
71 | }
72 | cell.textLabel.text = [NSString stringWithFormat:@"%@ 第%ld行",self.category,(long)indexPath.row];
73 | return cell;
74 | }
75 |
76 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
77 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
78 | }
79 |
80 | @end
81 |
--------------------------------------------------------------------------------
/LXScrollContentView/Classes/Controller/LXFirstViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // LXFirstViewController.m
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import "LXFirstViewController.h"
10 | #import "LXScrollContentView.h"
11 | #import "LXTestViewController.h"
12 | #import "LXSegmentTitleView.h"
13 |
14 | @interface LXFirstViewController ()
15 |
16 | @property (nonatomic, strong) LXSegmentTitleView *titleView;
17 |
18 | @property (nonatomic, strong) LXScrollContentView *contentView;
19 |
20 | @property(strong, nonatomic) NSArray *titles;
21 |
22 | @end
23 |
24 | #pragma mark -
25 | @implementation LXFirstViewController
26 |
27 | #pragma mark Lifecycle
28 |
29 | - (void)viewDidLoad {
30 | [super viewDidLoad];
31 |
32 | [self setupUI];
33 | [self setupData];
34 | }
35 |
36 | - (void)viewDidLayoutSubviews {
37 | [super viewDidLayoutSubviews];
38 |
39 | self.titleView.frame = CGRectMake(0, 0, self.view.frame.size.width, 35);
40 | self.contentView.frame = CGRectMake(0, 35, self.view.frame.size.width, self.view.frame.size.height - 35);
41 | }
42 |
43 |
44 | #pragma mark - UI
45 |
46 | - (void)setupUI {
47 | self.titleView = [[LXSegmentTitleView alloc] initWithFrame:CGRectZero];
48 | self.titleView.itemMinMargin = 15.f;
49 | self.titleView.delegate = self;
50 | self.titleView.dataSource = self;
51 | self.titleView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1];
52 | [self.view addSubview:self.titleView];
53 |
54 | self.contentView = [[LXScrollContentView alloc] initWithFrame:CGRectZero];
55 | self.contentView.preloadNearVcs = YES;
56 | self.contentView.delegate = self;
57 | self.contentView.dataSource = self;
58 | [self.view addSubview:self.contentView];
59 | }
60 |
61 |
62 | #pragma mark - Event
63 |
64 | - (void)setupData {
65 | self.titles = @[@"首页", @"体育在线", @"科技日报", @"生活", @"本地", @"精彩视频", @"娱乐", @"时尚", @"房地产", @"经济"];
66 |
67 | self.titleView.selectedIndex = 2;
68 | self.contentView.currentIndex = 2;
69 |
70 | [self.titleView reloadData];
71 | [self.contentView reloadData];
72 | }
73 |
74 | #pragma mark - LXSegmentTitleViewDelegate
75 |
76 | - (void)segmentTitleView:(LXSegmentTitleView *)segmentView
77 | selectedIndex:(NSInteger)selectedIndex
78 | lastSelectedIndex:(NSInteger)lastSelectedIndex
79 | {
80 | self.contentView.currentIndex = selectedIndex;
81 | }
82 |
83 | #pragma mark - LXSegmentTitleViewDataSource
84 |
85 | - (NSArray *)segmentTitlesOfSegmentTitleView:(LXSegmentTitleView *)segmentView {
86 | return self.titles;
87 | }
88 |
89 |
90 | #pragma mark - LXScrollContentViewDelegate
91 |
92 | - (void)contentViewDidScroll:(LXScrollContentView *)contentView
93 | fromIndex:(NSInteger)fromIndex
94 | toIndex:(NSInteger)toIndex
95 | progress:(float)progress
96 | {
97 | self.titleView.selectedIndex = toIndex;
98 | }
99 |
100 | - (void)contentViewDidEndDecelerating:(LXScrollContentView *)contentView
101 | startIndex:(NSInteger)startIndex
102 | endIndex:(NSInteger)endIndex
103 | {
104 | self.titleView.selectedIndex = endIndex;
105 | }
106 |
107 | #pragma mark - LXScrollContentViewDataSource
108 |
109 | - (UIViewController *)parentVcInScrollContentView:(LXScrollContentView *)scrollContentView {
110 | return self;
111 | }
112 |
113 | - (UIViewController *)scrollContentView:(LXScrollContentView *)scrollContentView childVcAtIndex:(NSInteger)index {
114 |
115 | NSString *title = self.titles[index];
116 | LXTestViewController *vc = [[LXTestViewController alloc] init];
117 | vc.category = title;
118 | vc.index = index;
119 | return vc;
120 | }
121 |
122 | - (NSInteger)numberOfchildVcsInScrollContentView:(LXScrollContentView *)scrollContentView {
123 |
124 | return self.titles.count;
125 | }
126 |
127 | @end
128 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LXScrollContentView
2 |
3 | 高仿网易新闻客户端左右滑动切换页面的框架
4 |
5 | github链接:https://github.com/LiuXingCode/LXScrollContentView
6 |
7 | 简书链接:http://www.jianshu.com/p/4ca324102e10
8 |
9 | swift版本链接:https://github.com/LiuXingCode/LXScollContentViewSwift
10 |
11 | ## 1. LXScrollContentView描述
12 |
13 | 这是一个高仿网易新闻客户端首页滑动切换页面的框架。支持点击上方标题,切换下方内容页面,也支持滑动下方内容区域,切换上方的标题。
14 |
15 | 
16 |
17 |
18 | 
19 |
20 | ## 2.安装方法
21 |
22 | LXScrollContentView支持CocoaPods安装
23 |
24 | ```obj-c
25 | pod 'LXScrollContentView'
26 | ```
27 |
28 | 也可以下载示例Demo,把里面的LXScrollContentViewLib文件夹拖到你的项目中即可
29 |
30 | ## 3.API使用说明
31 |
32 | 本框架有 **LXSegmentTitleView** 和 **LXScrollContentView** 两个类,它们完全独立,可以根据项目需求选择使用。
33 |
34 | **LXSegmentTitleView**表示上方标题区域,它的具体使用方法如下:
35 |
36 | ```obj-c
37 | /**
38 | 文字未选中颜色,默认black
39 | */
40 | @property (nonatomic, strong) UIColor *normalColor;
41 |
42 | /**
43 | 文字选中和下方滚动条颜色,默认red
44 | */
45 | @property (nonatomic, strong) UIColor *selectedColor;
46 |
47 |
48 | /**
49 | 第几个标题处于选中状态,默认为0
50 | */
51 | @property (nonatomic, assign) NSInteger selectedIndex;
52 |
53 |
54 | /**
55 | 每个标题宽度,默认85.f
56 | */
57 | @property (nonatomic, assign) CGFloat titleWidth;
58 |
59 |
60 | /**
61 | 标题字体font,默认14.f
62 | */
63 | @property (nonatomic, strong) UIFont *titleFont;
64 |
65 | /**
66 | 下方滚动指示条高度,默认2.f
67 | */
68 | @property (nonatomic, assign) CGFloat indicatorHeight;
69 |
70 | /**
71 | 选中标题回调block
72 | */
73 | @property (nonatomic, copy) BMPageTitleViewSelectedBlock selectedBlock;
74 |
75 |
76 | /**
77 | 刷新界面
78 |
79 | @param titles 标题数组
80 | */
81 | - (void)reloadViewWithTitles:(NSArray *)titles;
82 | ```
83 |
84 | **LXScrollContentView** 表示下方滚动内容区域,它的具体使用方法如下:
85 |
86 | ```obj-c
87 | /**
88 | 设置当前滚动到第几个页面,默认为0
89 | */
90 | @property (nonatomic, assign) NSInteger currentIndex;
91 |
92 |
93 | /**
94 | 页面滚动停止时触发block回调
95 | */
96 | @property (nonatomic, copy) LXScrollContentViewBlock scrollBlock;
97 |
98 |
99 | /**
100 | 刷新页面内容
101 |
102 | @param childVcs 当前View需要装入的控制器集合
103 | @param parentVC 当前View所在的父控制器
104 | */
105 | - (void)reloadViewWithChildVcs:(NSArray *)childVcs parentVC:(UIViewController *)parentVC;
106 | ```
107 |
108 | 以下是一个在ViewController中具体使用案例
109 |
110 | ```obj-c
111 | // 初始化UI
112 | - (void)setupUI {
113 | self.titleView = [[LXSegmentTitleView alloc] initWithFrame:CGRectZero];
114 | self.titleView.itemMinMargin = 15.f;
115 | self.titleView.delegate = self;
116 | self.titleView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1];
117 | [self.view addSubview:self.titleView];
118 |
119 | self.contentView = [[LXScrollContentView alloc] initWithFrame:CGRectZero];
120 | self.contentView.delegate = self;
121 | [self.view addSubview:self.contentView];
122 | }
123 |
124 | // 调整titleView和contentView的frame
125 | - (void)viewDidLayoutSubviews {
126 | [super viewDidLayoutSubviews];
127 |
128 | self.titleView.frame = CGRectMake(0, 0, self.view.frame.size.width, 35);
129 | self.contentView.frame = CGRectMake(0, 35, self.view.frame.size.width, self.view.frame.size.height - 35);
130 | }
131 |
132 | // 刷新titleView和contentView的数据源,根据项目需求自行选择数据源
133 | - (void)reloadData {
134 | NSArray *titles = @[@"首页", @"体育在线", @"科技日报", @"生活", @"本地", @"精彩视频", @"娱乐", @"时尚", @"房地产", @"经济"];
135 | self.titleView.segmentTitles = titles;
136 | NSMutableArray *vcs = [[NSMutableArray alloc] init];
137 | for (NSString *title in titles) {
138 | LXTestViewController *vc = [[LXTestViewController alloc] init];
139 | vc.category = title;
140 | [vcs addObject:vc];
141 | }
142 | [self.contentView reloadViewWithChildVcs:vcs parentVC:self];
143 | self.titleView.selectedIndex = 2;
144 | self.contentView.currentIndex = 2;
145 | }
146 | ```
147 |
148 | ## 4.期望
149 |
150 | 1.这是 **LXScrollContentView** 框架发布的第一个版本,还有很多不完善的地方,欢迎大家提出bug。
151 |
152 | 2.**LXScollTitleView** 暂时只有一种样式,我会尽快增加更多样式。
153 |
154 | 3.LXScrollContentView目前使用UICollectionView滑动,在性能方面已经比较优秀。接下来考虑加入cache功能,争取达到更加顺滑的效果。
155 |
156 | 4.大家如果觉得本框架不错,希望你们可以 **Star** 一下,我会更有动力的去不断完善。
157 |
158 | 5.我的邮箱账号:**liuxinghenau@163.com** ,简书地址:**http://www.jianshu.com/u/f367c6621844** ,大家有问题可以随时联系。
159 |
160 | ## 5.使用问题
161 | 最近好多同学使用此框架发现设置正确的frame,但控件在屏幕位置显示并不正确。这是因为iOS系统导航栏透明对滚动视图的影响,可以在 **viewDidLoad** 里面设置写上 **self.automaticallyAdjustsScrollViewInsets = NO** 解决此问题
162 |
--------------------------------------------------------------------------------
/LXScrollContentView/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 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/LXScrollContentViewLib/LXSegmentTitleView.m:
--------------------------------------------------------------------------------
1 | //
2 | // LXSegmentTitleView.m
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/31.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import "LXSegmentTitleView.h"
10 |
11 | @interface LXSegmentTitleView()
12 |
13 | @property (nonatomic, weak) UIScrollView *scrollView;
14 |
15 | @property (nonatomic, strong) NSMutableArray *itemButtons;
16 |
17 | @property (nonatomic, weak) UIView *indicatorView;
18 |
19 | @end
20 |
21 | @implementation LXSegmentTitleView
22 |
23 | - (void)awakeFromNib {
24 | [super awakeFromNib];
25 | [self setupData];
26 | }
27 |
28 | - (instancetype)initWithFrame:(CGRect)frame {
29 | if (self = [super initWithFrame:frame]) {
30 | [self setupData];
31 | }
32 | return self;
33 | }
34 |
35 | - (void)setupData {
36 | self.titleNormalColor = [UIColor blackColor];
37 | self.titleSelectedColor = [UIColor redColor];
38 | self.selectedIndex = 0;
39 | self.titleFont = [UIFont systemFontOfSize:14.f];
40 | self.indicatorColor = [UIColor redColor];
41 | self.indicatorHeight = 2.f;
42 | self.indicatorExtraW = 5.f;
43 | self.indicatorBottomMargin = 0;
44 | self.itemMinMargin = 25.f;
45 | }
46 |
47 | - (void)layoutSubviews {
48 | [super layoutSubviews];
49 | self.scrollView.frame = self.bounds;
50 | if (self.itemButtons.count == 0) {
51 | return;
52 | }
53 | CGFloat totalBtnW = 0;
54 | for (UIButton *btn in self.itemButtons) {
55 | [btn sizeToFit];
56 | totalBtnW += btn.frame.size.width;
57 | }
58 |
59 | CGFloat itemMarginW = (self.scrollView.frame.size.width - totalBtnW) / (self.itemButtons.count + 1);
60 | if (itemMarginW < self.itemMinMargin) {
61 | itemMarginW = self.itemMinMargin;
62 | }
63 |
64 | CGFloat lastX = itemMarginW;
65 | for (UIButton *btn in self.itemButtons) {
66 | [btn sizeToFit];
67 | btn.frame = CGRectMake(lastX, 0, btn.frame.size.width, self.scrollView.frame.size.height);
68 | lastX += btn.frame.size.width + itemMarginW;
69 | }
70 | self.scrollView.contentSize = CGSizeMake(lastX, self.scrollView.frame.size.height);
71 | [self setSelectedIndicatorFrame:NO];
72 | }
73 |
74 | - (void)setSelectedIndicatorFrame:(BOOL)animated {
75 | UIButton *selectedBtn = self.itemButtons[self.selectedIndex];
76 | [UIView animateWithDuration:(animated? 0.02 : 0) delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
77 | self.indicatorView.frame = CGRectMake(selectedBtn.frame.origin.x - self.indicatorExtraW, self.scrollView.frame.size.height - self.indicatorHeight - self.indicatorBottomMargin, selectedBtn.frame.size.width + 2 * self.indicatorExtraW, self.indicatorHeight);
78 | } completion:^(BOOL finished) {
79 | [self scrollRectToVisibleCenterAnimated:animated];
80 | }];
81 | }
82 |
83 | - (void)scrollRectToVisibleCenterAnimated:(BOOL)animated {
84 | UIButton *selectedBtn = self.itemButtons[self.selectedIndex];
85 | CGRect centeredRect = CGRectMake(selectedBtn.center.x - self.scrollView.frame.size.width / 2, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
86 | [self.scrollView scrollRectToVisible:centeredRect animated:animated];
87 | }
88 |
89 | #pragma mark - public
90 |
91 | - (void)reloadData {
92 |
93 | if (self.dataSource && [self.dataSource respondsToSelector:@selector(segmentTitlesOfSegmentTitleView:)]) {
94 | NSArray *segmentTitles = [self.dataSource segmentTitlesOfSegmentTitleView:self];
95 | self.segmentTitles = segmentTitles;
96 | }
97 | }
98 |
99 | #pragma mark - getter
100 |
101 | - (UIScrollView *)scrollView {
102 | if (!_scrollView) {
103 | UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
104 | scrollView.scrollsToTop = NO;
105 | scrollView.showsHorizontalScrollIndicator = NO;
106 | scrollView.showsVerticalScrollIndicator = NO;
107 | [self addSubview:scrollView];
108 | _scrollView = scrollView;
109 | }
110 | return _scrollView;
111 | }
112 |
113 | - (UIView *)indicatorView {
114 | if (!_indicatorView) {
115 | UIView *indicatorView = [[UIView alloc] initWithFrame:CGRectZero];
116 | [self.scrollView addSubview:indicatorView];
117 | _indicatorView = indicatorView;
118 | }
119 | return _indicatorView;
120 | }
121 |
122 | - (NSMutableArray *)itemButtons {
123 | if (!_itemButtons) {
124 | _itemButtons = [[NSMutableArray alloc] init];
125 | }
126 | return _itemButtons;
127 | }
128 |
129 |
130 | #pragma mark - setter
131 |
132 | - (void)setSegmentTitles:(NSArray *)segmentTitles
133 | {
134 | _segmentTitles = [segmentTitles copy];
135 | [self.itemButtons makeObjectsPerformSelector:@selector(removeFromSuperview)];
136 | self.itemButtons = nil;
137 | for (NSString *title in segmentTitles) {
138 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
139 | if (self.selectedIndex == self.itemButtons.count) {
140 | btn.selected = YES;
141 | }
142 | btn.tag = 888 + self.itemButtons.count;
143 | [btn setTitle:title forState:UIControlStateNormal];
144 | [btn setTitleColor:self.titleNormalColor forState:UIControlStateNormal];
145 | [btn setTitleColor:self.titleSelectedColor forState:UIControlStateSelected];
146 | [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
147 | btn.titleLabel.font = self.titleFont;
148 | [self.scrollView addSubview:btn];
149 | [self.itemButtons addObject:btn];
150 | }
151 | [self setNeedsLayout];
152 | [self layoutIfNeeded];
153 | }
154 |
155 |
156 | - (void)setSelectedIndex:(NSInteger)selectedIndex {
157 | if (_selectedIndex == selectedIndex
158 | || selectedIndex < 0
159 | || selectedIndex > self.itemButtons.count - 1
160 | || self.itemButtons.count <= 0) {
161 | return;
162 | }
163 | UIButton *btn = [self.scrollView viewWithTag:_selectedIndex + 888];
164 | btn.selected = NO;
165 | _selectedIndex = selectedIndex;
166 | UIButton *selectedBtn = [self.scrollView viewWithTag:_selectedIndex + 888];
167 | selectedBtn.selected = YES;
168 | [self setSelectedIndicatorFrame:YES];
169 | }
170 |
171 | - (void)setTitleNormalColor:(UIColor *)titleNormalColor {
172 | _titleNormalColor = titleNormalColor;
173 | for (UIButton *btn in self.itemButtons) {
174 | [btn setTitleColor:titleNormalColor forState:UIControlStateNormal];
175 | }
176 | }
177 |
178 | - (void)setTitleSelectedColor:(UIColor *)titleSelectedColor {
179 | _titleSelectedColor = titleSelectedColor;
180 | for (UIButton *btn in self.itemButtons) {
181 | [btn setTitleColor:titleSelectedColor forState:UIControlStateSelected];
182 | }
183 | }
184 |
185 | - (void)setTitleFont:(UIFont *)titleFont {
186 | _titleFont = titleFont;
187 | for (UIButton *btn in self.itemButtons) {
188 | btn.titleLabel.font = titleFont;
189 | }
190 | [self setNeedsLayout];
191 | [self layoutIfNeeded];
192 | }
193 |
194 | - (void)setIndicatorColor:(UIColor *)indicatorColor {
195 | _indicatorColor = indicatorColor;
196 | self.indicatorView.backgroundColor = indicatorColor;
197 | }
198 |
199 | - (void)setIndicatorHeight:(CGFloat)indicatorHeight {
200 | _indicatorHeight = indicatorHeight;
201 | [self setNeedsLayout];
202 | [self layoutIfNeeded];
203 | }
204 |
205 | - (void)setIndicatorExtraW:(CGFloat)indicatorExtraW {
206 | _indicatorExtraW = indicatorExtraW;
207 | [self setNeedsLayout];
208 | [self layoutIfNeeded];
209 | }
210 |
211 | - (void)setItemMinMargin:(CGFloat)itemMinMargin {
212 | _itemMinMargin = itemMinMargin;
213 | [self setNeedsLayout];
214 | [self layoutIfNeeded];
215 | }
216 |
217 | - (void)setIndicatorBottomMargin:(CGFloat)indicatorBottomMargin {
218 | _indicatorBottomMargin = indicatorBottomMargin;
219 | [self setNeedsLayout];
220 | [self layoutIfNeeded];
221 | }
222 |
223 |
224 | #pragma mark - EventResponse
225 |
226 | - (void)btnClick:(UIButton *)btn {
227 | NSInteger btnIndex = btn.tag - 888;
228 | if (btnIndex == self.selectedIndex) {
229 | return;
230 | }
231 | if (self.delegate && [self.delegate respondsToSelector:@selector(segmentTitleView:selectedIndex:lastSelectedIndex:)]) {
232 | [self.delegate segmentTitleView:self selectedIndex:btnIndex lastSelectedIndex:self.selectedIndex];
233 | }
234 | self.selectedIndex = btnIndex;
235 | }
236 |
237 | @end
238 |
--------------------------------------------------------------------------------
/LXScrollContentViewLib/LXScrollContentView.m:
--------------------------------------------------------------------------------
1 | //
2 | // LXScrollContentView.m
3 | // LXScrollContentView
4 | //
5 | // Created by 刘行 on 2017/3/23.
6 | // Copyright © 2017年 刘行. All rights reserved.
7 | //
8 |
9 | #import "LXScrollContentView.h"
10 |
11 | static NSString *kContentCellID = @"kContentCellID";
12 |
13 | @interface LXScrollContentView()
14 | {
15 | CGFloat _startOffsetX;
16 | }
17 |
18 | @property (nonatomic, strong) NSMutableDictionary *childVcDicts;
19 |
20 | @property (nonatomic, assign) NSInteger childVcsCount;
21 |
22 | @property (nonatomic, assign) BOOL isForbidScrollDelegate;
23 |
24 | @property (nonatomic, weak) UIViewController *parentVC;
25 |
26 | @property (nonatomic, weak, readwrite) UICollectionView *collectionView;
27 |
28 | @end
29 |
30 | @implementation LXScrollContentView
31 |
32 | - (void)awakeFromNib {
33 | [super awakeFromNib];
34 | [self setup];
35 | }
36 |
37 | - (instancetype)initWithFrame:(CGRect)frame {
38 | if (self = [super initWithFrame:frame]) {
39 | [self setup];
40 | }
41 | return self;
42 | }
43 |
44 | - (void)setup {
45 | self.backgroundColor = [UIColor whiteColor];
46 | }
47 |
48 | - (void)layoutSubviews{
49 | [super layoutSubviews];
50 | self.collectionView.frame = self.bounds;
51 | UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
52 | flowLayout.itemSize = self.bounds.size;
53 | }
54 |
55 | #pragma mark - lazy init
56 |
57 | - (NSMutableDictionary *)childVcDicts {
58 | if (!_childVcDicts) {
59 | _childVcDicts = [[NSMutableDictionary alloc] init];
60 | }
61 | return _childVcDicts;
62 | }
63 |
64 | - (UICollectionView *)collectionView {
65 | if (!_collectionView) {
66 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
67 | flowLayout.minimumLineSpacing = 0;
68 | flowLayout.minimumInteritemSpacing = 0;
69 | flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
70 | UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:flowLayout];
71 | collectionView.scrollsToTop = NO;
72 | collectionView.backgroundColor = [UIColor clearColor];
73 | collectionView.showsHorizontalScrollIndicator = NO;
74 | collectionView.pagingEnabled = YES;
75 | collectionView.bounces = NO;
76 | collectionView.delegate = self;
77 | collectionView.dataSource = self;
78 | [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kContentCellID];
79 |
80 | if (@available(iOS 11.0, *)) {
81 | collectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
82 | }
83 |
84 | [self addSubview:collectionView];
85 | _collectionView = collectionView;
86 | }
87 | return _collectionView;
88 | }
89 |
90 |
91 | #pragma mark - UICollectionViewDataSource
92 |
93 | - (NSInteger)collectionView:(UICollectionView *)collectionView
94 | numberOfItemsInSection:(NSInteger)section {
95 | return self.childVcsCount;
96 | }
97 |
98 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
99 | cellForItemAtIndexPath:(NSIndexPath *)indexPath {
100 | UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kContentCellID forIndexPath:indexPath];
101 | cell.backgroundColor = [UIColor clearColor];
102 | cell.contentView.backgroundColor = [UIColor clearColor];
103 | return cell;
104 | }
105 |
106 |
107 | #pragma mark - UICollectionViewDelegate
108 |
109 | - (void)collectionView:(UICollectionView *)collectionView
110 | willDisplayCell:(UICollectionViewCell *)cell
111 | forItemAtIndexPath:(NSIndexPath *)indexPath {
112 | [cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
113 | UIViewController *childVc = [self fetchChildVcAtIndex:indexPath.row];
114 | if (childVc) {
115 | if (!childVc.parentViewController) {
116 | [self.parentVC addChildViewController:childVc];
117 | }
118 | [cell.contentView addSubview:childVc.view];
119 | }
120 |
121 | if (self.delegate && [self.delegate respondsToSelector:@selector(childVcViewWillAppearAtIndex:)]) {
122 | [self.delegate childVcViewWillAppearAtIndex:indexPath.row];
123 | }
124 |
125 | if (!self.preloadNearVcs) return;
126 | [self preloadNearVcsAtIndex:indexPath.row];
127 | }
128 |
129 | - (void)collectionView:(UICollectionView *)collectionView
130 | didEndDisplayingCell:(UICollectionViewCell *)cell
131 | forItemAtIndexPath:(NSIndexPath *)indexPath {
132 | if (self.delegate && [self.delegate respondsToSelector:@selector(childVcViewDidDisappearAtIndex:)]) {
133 | [self.delegate childVcViewDidDisappearAtIndex:indexPath.row];
134 | }
135 | UIViewController *childVc = [self fetchChildVcAtIndex:indexPath.row];
136 | if (childVc == nil) return;
137 |
138 | if (childVc.parentViewController == self.parentVC) {
139 | [childVc removeFromParentViewController];
140 | }
141 | if (childVc.view.superview) {
142 | [childVc.view removeFromSuperview];
143 | }
144 | }
145 |
146 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
147 | if (scrollView != self.collectionView) return;
148 |
149 | self.isForbidScrollDelegate = NO;
150 | _startOffsetX = scrollView.contentOffset.x;
151 | }
152 |
153 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
154 | if (scrollView != self.collectionView) return;
155 | if (self.isForbidScrollDelegate) return;
156 |
157 | CGFloat endOffsetX = scrollView.contentOffset.x;
158 | NSInteger fromIndex = floor(_startOffsetX / scrollView.frame.size.width);
159 | CGFloat progress;
160 | NSInteger toIndex;
161 | if (_startOffsetX < endOffsetX) {//左滑
162 | progress = (endOffsetX - _startOffsetX) / scrollView.frame.size.width;
163 | toIndex = fromIndex + 1;
164 | if (toIndex > self.childVcsCount - 1) {
165 | toIndex = self.childVcsCount - 1;
166 | }
167 | } else if (_startOffsetX == endOffsetX){
168 | progress = 0;
169 | toIndex = fromIndex;
170 | } else {
171 | progress = (_startOffsetX - endOffsetX) / scrollView.frame.size.width;
172 | toIndex = fromIndex - 1;
173 | if (toIndex < 0) {
174 | toIndex = 0;
175 | }
176 | }
177 | if (self.delegate && [self.delegate respondsToSelector:@selector(contentViewDidScroll:fromIndex:toIndex:progress:)]) {
178 | [self.delegate contentViewDidScroll:self fromIndex:fromIndex toIndex:toIndex progress:progress];
179 | }
180 | }
181 |
182 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
183 | if (!decelerate) {
184 | [self scrollViewDidEndScroll:scrollView];
185 | }
186 | }
187 |
188 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
189 | [self scrollViewDidEndScroll:scrollView];
190 | }
191 |
192 | - (void)scrollViewDidEndScroll:(UIScrollView *)scrollView {
193 | if (scrollView != self.collectionView) return;
194 |
195 | CGFloat endOffsetX = scrollView.contentOffset.x;
196 | NSInteger startIndex = floor(_startOffsetX / scrollView.frame.size.width);
197 | NSInteger endIndex = floor(endOffsetX / scrollView.frame.size.width);
198 | _currentIndex = endIndex;
199 | if (self.delegate && [self.delegate respondsToSelector:@selector(contentViewDidEndDecelerating:startIndex:endIndex:)]) {
200 | [self.delegate contentViewDidEndDecelerating:self startIndex:startIndex endIndex:endIndex];
201 | }
202 | }
203 |
204 | #pragma mark - public methods
205 |
206 | - (void)reloadData {
207 | if (self.dataSource && [self.dataSource respondsToSelector:@selector(numberOfchildVcsInScrollContentView:)]) {
208 | self.childVcsCount = [self.dataSource numberOfchildVcsInScrollContentView:self];
209 | }
210 |
211 | if (self.dataSource && [self.dataSource respondsToSelector:@selector(parentVcInScrollContentView:)]) {
212 | self.parentVC = [self.dataSource parentVcInScrollContentView:self];
213 | }
214 |
215 | NSArray *childVcs = self.childVcDicts.allValues;
216 | for (UIViewController *childVc in childVcs) {
217 | if (childVc.parentViewController == self.parentVC) {
218 | [childVc removeFromParentViewController];
219 | }
220 | if (childVc.view.superview) {
221 | [childVc.view removeFromSuperview];
222 | }
223 | }
224 | [self.childVcDicts removeAllObjects];
225 |
226 | [self.collectionView reloadData];
227 | }
228 |
229 |
230 | - (void)setCurrentIndex:(NSUInteger)currentIndex {
231 | if (currentIndex < 0
232 | || currentIndex > self.childVcsCount - 1
233 | || self.childVcsCount <= 0) {
234 | return;
235 | }
236 | _currentIndex = currentIndex;
237 | self.isForbidScrollDelegate = YES;
238 | dispatch_async(dispatch_get_main_queue(), ^{
239 | NSIndexPath *currentIndexPath = [NSIndexPath indexPathForRow:currentIndex inSection:0];
240 | [self.collectionView scrollToItemAtIndexPath:currentIndexPath
241 | atScrollPosition:UICollectionViewScrollPositionNone
242 | animated:NO];
243 | });
244 | }
245 |
246 | - (void)setDataSource:(id)dataSource {
247 | _dataSource = dataSource;
248 | [self reloadData];
249 | }
250 |
251 | #pragma mark - private methods
252 |
253 | - (UIViewController *)fetchChildVcAtIndex:(NSUInteger)index {
254 | UIViewController *childVc = self.childVcDicts[@(index)];
255 | if (childVc) return childVc;
256 |
257 | if (self.dataSource && [self.dataSource respondsToSelector:@selector(scrollContentView:childVcAtIndex:)]) {
258 | childVc = [self.dataSource scrollContentView:self childVcAtIndex:index];
259 | if (childVc) {
260 | childVc.view.frame = [self childVcFrameAtIndex:index];
261 | self.childVcDicts[@(index)] = childVc;
262 | return childVc;
263 | }
264 | }
265 | return nil;
266 | }
267 |
268 | - (CGRect)childVcFrameAtIndex:(NSUInteger)index {
269 | CGRect frame = self.bounds;
270 | if (self.dataSource && [self.dataSource respondsToSelector:@selector(scrollContentView:childVcContentInsetAtIndex:)]) {
271 | UIEdgeInsets inset = [self.dataSource scrollContentView:self childVcContentInsetAtIndex:index];
272 | CGFloat weight = MAX(0, self.bounds.size.width - inset.left - inset.right);
273 | CGFloat height = MAX(0, self.bounds.size.height - inset.top - inset.bottom);
274 | frame = CGRectMake(inset.left, inset.top, weight, height);
275 | }
276 | return frame;
277 | }
278 |
279 | - (void)preloadNearVcsAtIndex:(NSUInteger)index {
280 | if (index < self.childVcsCount - 1) {
281 | [self fetchChildVcAtIndex:index + 1];
282 | }
283 | if (index > 1) {
284 | [self fetchChildVcAtIndex:index - 1];
285 | }
286 | }
287 |
288 | @end
289 |
--------------------------------------------------------------------------------
/LXScrollContentView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | CA4689841E8E17E700692721 /* LXSegmentTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA4689831E8E17E700692721 /* LXSegmentTitleView.m */; };
11 | CAACE5771E83BB2800977C7E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CAACE5761E83BB2800977C7E /* main.m */; };
12 | CAACE57A1E83BB2800977C7E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = CAACE5791E83BB2800977C7E /* AppDelegate.m */; };
13 | CAACE5801E83BB2800977C7E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CAACE57E1E83BB2800977C7E /* Main.storyboard */; };
14 | CAACE5821E83BB2800977C7E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CAACE5811E83BB2800977C7E /* Assets.xcassets */; };
15 | CAACE5851E83BB2800977C7E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CAACE5831E83BB2800977C7E /* LaunchScreen.storyboard */; };
16 | CAACE5901E83BB2800977C7E /* LXScrollContentViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CAACE58F1E83BB2800977C7E /* LXScrollContentViewTests.m */; };
17 | CAACE59B1E83BB2800977C7E /* LXScrollContentViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = CAACE59A1E83BB2800977C7E /* LXScrollContentViewUITests.m */; };
18 | CAACE5AC1E83BF9B00977C7E /* LXScrollContentView.m in Sources */ = {isa = PBXBuildFile; fileRef = CAACE5AB1E83BF9B00977C7E /* LXScrollContentView.m */; };
19 | CAACE5B31E83C25300977C7E /* LXFirstViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CAACE5B21E83C25300977C7E /* LXFirstViewController.m */; };
20 | CAACE5B61E83C26600977C7E /* LXSecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CAACE5B51E83C26600977C7E /* LXSecondViewController.m */; };
21 | CAACE5B91E83DA3500977C7E /* LXTestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CAACE5B81E83DA3500977C7E /* LXTestViewController.m */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXContainerItemProxy section */
25 | CAACE58C1E83BB2800977C7E /* PBXContainerItemProxy */ = {
26 | isa = PBXContainerItemProxy;
27 | containerPortal = CAACE56A1E83BB2800977C7E /* Project object */;
28 | proxyType = 1;
29 | remoteGlobalIDString = CAACE5711E83BB2800977C7E;
30 | remoteInfo = LXScrollContentView;
31 | };
32 | CAACE5971E83BB2800977C7E /* PBXContainerItemProxy */ = {
33 | isa = PBXContainerItemProxy;
34 | containerPortal = CAACE56A1E83BB2800977C7E /* Project object */;
35 | proxyType = 1;
36 | remoteGlobalIDString = CAACE5711E83BB2800977C7E;
37 | remoteInfo = LXScrollContentView;
38 | };
39 | /* End PBXContainerItemProxy section */
40 |
41 | /* Begin PBXFileReference section */
42 | CA4689821E8E17E700692721 /* LXSegmentTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXSegmentTitleView.h; sourceTree = ""; };
43 | CA4689831E8E17E700692721 /* LXSegmentTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXSegmentTitleView.m; sourceTree = ""; };
44 | CAACE5721E83BB2800977C7E /* LXScrollContentView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LXScrollContentView.app; sourceTree = BUILT_PRODUCTS_DIR; };
45 | CAACE5761E83BB2800977C7E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
46 | CAACE5781E83BB2800977C7E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
47 | CAACE5791E83BB2800977C7E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
48 | CAACE57F1E83BB2800977C7E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
49 | CAACE5811E83BB2800977C7E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
50 | CAACE5841E83BB2800977C7E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
51 | CAACE5861E83BB2800977C7E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
52 | CAACE58B1E83BB2800977C7E /* LXScrollContentViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LXScrollContentViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
53 | CAACE58F1E83BB2800977C7E /* LXScrollContentViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LXScrollContentViewTests.m; sourceTree = ""; };
54 | CAACE5911E83BB2800977C7E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
55 | CAACE5961E83BB2800977C7E /* LXScrollContentViewUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LXScrollContentViewUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
56 | CAACE59A1E83BB2800977C7E /* LXScrollContentViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LXScrollContentViewUITests.m; sourceTree = ""; };
57 | CAACE59C1E83BB2800977C7E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
58 | CAACE5AA1E83BF9B00977C7E /* LXScrollContentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXScrollContentView.h; sourceTree = ""; };
59 | CAACE5AB1E83BF9B00977C7E /* LXScrollContentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXScrollContentView.m; sourceTree = ""; };
60 | CAACE5B11E83C25300977C7E /* LXFirstViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXFirstViewController.h; sourceTree = ""; };
61 | CAACE5B21E83C25300977C7E /* LXFirstViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXFirstViewController.m; sourceTree = ""; };
62 | CAACE5B41E83C26600977C7E /* LXSecondViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXSecondViewController.h; sourceTree = ""; };
63 | CAACE5B51E83C26600977C7E /* LXSecondViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXSecondViewController.m; sourceTree = ""; };
64 | CAACE5B71E83DA3500977C7E /* LXTestViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LXTestViewController.h; sourceTree = ""; };
65 | CAACE5B81E83DA3500977C7E /* LXTestViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LXTestViewController.m; sourceTree = ""; };
66 | /* End PBXFileReference section */
67 |
68 | /* Begin PBXFrameworksBuildPhase section */
69 | CAACE56F1E83BB2800977C7E /* Frameworks */ = {
70 | isa = PBXFrameworksBuildPhase;
71 | buildActionMask = 2147483647;
72 | files = (
73 | );
74 | runOnlyForDeploymentPostprocessing = 0;
75 | };
76 | CAACE5881E83BB2800977C7E /* Frameworks */ = {
77 | isa = PBXFrameworksBuildPhase;
78 | buildActionMask = 2147483647;
79 | files = (
80 | );
81 | runOnlyForDeploymentPostprocessing = 0;
82 | };
83 | CAACE5931E83BB2800977C7E /* Frameworks */ = {
84 | isa = PBXFrameworksBuildPhase;
85 | buildActionMask = 2147483647;
86 | files = (
87 | );
88 | runOnlyForDeploymentPostprocessing = 0;
89 | };
90 | /* End PBXFrameworksBuildPhase section */
91 |
92 | /* Begin PBXGroup section */
93 | CAACE5691E83BB2800977C7E = {
94 | isa = PBXGroup;
95 | children = (
96 | CAACE5A91E83BF5600977C7E /* LXScrollContentViewLib */,
97 | CAACE5741E83BB2800977C7E /* LXScrollContentView */,
98 | CAACE58E1E83BB2800977C7E /* LXScrollContentViewTests */,
99 | CAACE5991E83BB2800977C7E /* LXScrollContentViewUITests */,
100 | CAACE5731E83BB2800977C7E /* Products */,
101 | );
102 | sourceTree = "";
103 | };
104 | CAACE5731E83BB2800977C7E /* Products */ = {
105 | isa = PBXGroup;
106 | children = (
107 | CAACE5721E83BB2800977C7E /* LXScrollContentView.app */,
108 | CAACE58B1E83BB2800977C7E /* LXScrollContentViewTests.xctest */,
109 | CAACE5961E83BB2800977C7E /* LXScrollContentViewUITests.xctest */,
110 | );
111 | name = Products;
112 | sourceTree = "";
113 | };
114 | CAACE5741E83BB2800977C7E /* LXScrollContentView */ = {
115 | isa = PBXGroup;
116 | children = (
117 | CAACE5A81E83BDEE00977C7E /* Classes */,
118 | CAACE5781E83BB2800977C7E /* AppDelegate.h */,
119 | CAACE5791E83BB2800977C7E /* AppDelegate.m */,
120 | CAACE57E1E83BB2800977C7E /* Main.storyboard */,
121 | CAACE5811E83BB2800977C7E /* Assets.xcassets */,
122 | CAACE5831E83BB2800977C7E /* LaunchScreen.storyboard */,
123 | CAACE5861E83BB2800977C7E /* Info.plist */,
124 | CAACE5751E83BB2800977C7E /* Supporting Files */,
125 | );
126 | path = LXScrollContentView;
127 | sourceTree = "";
128 | };
129 | CAACE5751E83BB2800977C7E /* Supporting Files */ = {
130 | isa = PBXGroup;
131 | children = (
132 | CAACE5761E83BB2800977C7E /* main.m */,
133 | );
134 | name = "Supporting Files";
135 | sourceTree = "";
136 | };
137 | CAACE58E1E83BB2800977C7E /* LXScrollContentViewTests */ = {
138 | isa = PBXGroup;
139 | children = (
140 | CAACE58F1E83BB2800977C7E /* LXScrollContentViewTests.m */,
141 | CAACE5911E83BB2800977C7E /* Info.plist */,
142 | );
143 | path = LXScrollContentViewTests;
144 | sourceTree = "";
145 | };
146 | CAACE5991E83BB2800977C7E /* LXScrollContentViewUITests */ = {
147 | isa = PBXGroup;
148 | children = (
149 | CAACE59A1E83BB2800977C7E /* LXScrollContentViewUITests.m */,
150 | CAACE59C1E83BB2800977C7E /* Info.plist */,
151 | );
152 | path = LXScrollContentViewUITests;
153 | sourceTree = "";
154 | };
155 | CAACE5A81E83BDEE00977C7E /* Classes */ = {
156 | isa = PBXGroup;
157 | children = (
158 | CAACE5B01E83C1EF00977C7E /* Controller */,
159 | );
160 | path = Classes;
161 | sourceTree = "";
162 | };
163 | CAACE5A91E83BF5600977C7E /* LXScrollContentViewLib */ = {
164 | isa = PBXGroup;
165 | children = (
166 | CAACE5AA1E83BF9B00977C7E /* LXScrollContentView.h */,
167 | CAACE5AB1E83BF9B00977C7E /* LXScrollContentView.m */,
168 | CA4689821E8E17E700692721 /* LXSegmentTitleView.h */,
169 | CA4689831E8E17E700692721 /* LXSegmentTitleView.m */,
170 | );
171 | path = LXScrollContentViewLib;
172 | sourceTree = "";
173 | };
174 | CAACE5B01E83C1EF00977C7E /* Controller */ = {
175 | isa = PBXGroup;
176 | children = (
177 | CAACE5B11E83C25300977C7E /* LXFirstViewController.h */,
178 | CAACE5B21E83C25300977C7E /* LXFirstViewController.m */,
179 | CAACE5B41E83C26600977C7E /* LXSecondViewController.h */,
180 | CAACE5B51E83C26600977C7E /* LXSecondViewController.m */,
181 | CAACE5B71E83DA3500977C7E /* LXTestViewController.h */,
182 | CAACE5B81E83DA3500977C7E /* LXTestViewController.m */,
183 | );
184 | path = Controller;
185 | sourceTree = "";
186 | };
187 | /* End PBXGroup section */
188 |
189 | /* Begin PBXNativeTarget section */
190 | CAACE5711E83BB2800977C7E /* LXScrollContentView */ = {
191 | isa = PBXNativeTarget;
192 | buildConfigurationList = CAACE59F1E83BB2800977C7E /* Build configuration list for PBXNativeTarget "LXScrollContentView" */;
193 | buildPhases = (
194 | CAACE56E1E83BB2800977C7E /* Sources */,
195 | CAACE56F1E83BB2800977C7E /* Frameworks */,
196 | CAACE5701E83BB2800977C7E /* Resources */,
197 | );
198 | buildRules = (
199 | );
200 | dependencies = (
201 | );
202 | name = LXScrollContentView;
203 | productName = LXScrollContentView;
204 | productReference = CAACE5721E83BB2800977C7E /* LXScrollContentView.app */;
205 | productType = "com.apple.product-type.application";
206 | };
207 | CAACE58A1E83BB2800977C7E /* LXScrollContentViewTests */ = {
208 | isa = PBXNativeTarget;
209 | buildConfigurationList = CAACE5A21E83BB2800977C7E /* Build configuration list for PBXNativeTarget "LXScrollContentViewTests" */;
210 | buildPhases = (
211 | CAACE5871E83BB2800977C7E /* Sources */,
212 | CAACE5881E83BB2800977C7E /* Frameworks */,
213 | CAACE5891E83BB2800977C7E /* Resources */,
214 | );
215 | buildRules = (
216 | );
217 | dependencies = (
218 | CAACE58D1E83BB2800977C7E /* PBXTargetDependency */,
219 | );
220 | name = LXScrollContentViewTests;
221 | productName = LXScrollContentViewTests;
222 | productReference = CAACE58B1E83BB2800977C7E /* LXScrollContentViewTests.xctest */;
223 | productType = "com.apple.product-type.bundle.unit-test";
224 | };
225 | CAACE5951E83BB2800977C7E /* LXScrollContentViewUITests */ = {
226 | isa = PBXNativeTarget;
227 | buildConfigurationList = CAACE5A51E83BB2800977C7E /* Build configuration list for PBXNativeTarget "LXScrollContentViewUITests" */;
228 | buildPhases = (
229 | CAACE5921E83BB2800977C7E /* Sources */,
230 | CAACE5931E83BB2800977C7E /* Frameworks */,
231 | CAACE5941E83BB2800977C7E /* Resources */,
232 | );
233 | buildRules = (
234 | );
235 | dependencies = (
236 | CAACE5981E83BB2800977C7E /* PBXTargetDependency */,
237 | );
238 | name = LXScrollContentViewUITests;
239 | productName = LXScrollContentViewUITests;
240 | productReference = CAACE5961E83BB2800977C7E /* LXScrollContentViewUITests.xctest */;
241 | productType = "com.apple.product-type.bundle.ui-testing";
242 | };
243 | /* End PBXNativeTarget section */
244 |
245 | /* Begin PBXProject section */
246 | CAACE56A1E83BB2800977C7E /* Project object */ = {
247 | isa = PBXProject;
248 | attributes = {
249 | CLASSPREFIX = LX;
250 | LastUpgradeCheck = 0820;
251 | ORGANIZATIONNAME = "刘行";
252 | TargetAttributes = {
253 | CAACE5711E83BB2800977C7E = {
254 | CreatedOnToolsVersion = 8.2.1;
255 | DevelopmentTeam = 6HK953UL65;
256 | ProvisioningStyle = Automatic;
257 | };
258 | CAACE58A1E83BB2800977C7E = {
259 | CreatedOnToolsVersion = 8.2.1;
260 | DevelopmentTeam = RQB4AJCS2L;
261 | ProvisioningStyle = Automatic;
262 | TestTargetID = CAACE5711E83BB2800977C7E;
263 | };
264 | CAACE5951E83BB2800977C7E = {
265 | CreatedOnToolsVersion = 8.2.1;
266 | DevelopmentTeam = RQB4AJCS2L;
267 | ProvisioningStyle = Automatic;
268 | TestTargetID = CAACE5711E83BB2800977C7E;
269 | };
270 | };
271 | };
272 | buildConfigurationList = CAACE56D1E83BB2800977C7E /* Build configuration list for PBXProject "LXScrollContentView" */;
273 | compatibilityVersion = "Xcode 3.2";
274 | developmentRegion = English;
275 | hasScannedForEncodings = 0;
276 | knownRegions = (
277 | English,
278 | en,
279 | Base,
280 | );
281 | mainGroup = CAACE5691E83BB2800977C7E;
282 | productRefGroup = CAACE5731E83BB2800977C7E /* Products */;
283 | projectDirPath = "";
284 | projectRoot = "";
285 | targets = (
286 | CAACE5711E83BB2800977C7E /* LXScrollContentView */,
287 | CAACE58A1E83BB2800977C7E /* LXScrollContentViewTests */,
288 | CAACE5951E83BB2800977C7E /* LXScrollContentViewUITests */,
289 | );
290 | };
291 | /* End PBXProject section */
292 |
293 | /* Begin PBXResourcesBuildPhase section */
294 | CAACE5701E83BB2800977C7E /* Resources */ = {
295 | isa = PBXResourcesBuildPhase;
296 | buildActionMask = 2147483647;
297 | files = (
298 | CAACE5851E83BB2800977C7E /* LaunchScreen.storyboard in Resources */,
299 | CAACE5821E83BB2800977C7E /* Assets.xcassets in Resources */,
300 | CAACE5801E83BB2800977C7E /* Main.storyboard in Resources */,
301 | );
302 | runOnlyForDeploymentPostprocessing = 0;
303 | };
304 | CAACE5891E83BB2800977C7E /* Resources */ = {
305 | isa = PBXResourcesBuildPhase;
306 | buildActionMask = 2147483647;
307 | files = (
308 | );
309 | runOnlyForDeploymentPostprocessing = 0;
310 | };
311 | CAACE5941E83BB2800977C7E /* Resources */ = {
312 | isa = PBXResourcesBuildPhase;
313 | buildActionMask = 2147483647;
314 | files = (
315 | );
316 | runOnlyForDeploymentPostprocessing = 0;
317 | };
318 | /* End PBXResourcesBuildPhase section */
319 |
320 | /* Begin PBXSourcesBuildPhase section */
321 | CAACE56E1E83BB2800977C7E /* Sources */ = {
322 | isa = PBXSourcesBuildPhase;
323 | buildActionMask = 2147483647;
324 | files = (
325 | CAACE5B61E83C26600977C7E /* LXSecondViewController.m in Sources */,
326 | CAACE5B91E83DA3500977C7E /* LXTestViewController.m in Sources */,
327 | CAACE5AC1E83BF9B00977C7E /* LXScrollContentView.m in Sources */,
328 | CA4689841E8E17E700692721 /* LXSegmentTitleView.m in Sources */,
329 | CAACE5B31E83C25300977C7E /* LXFirstViewController.m in Sources */,
330 | CAACE57A1E83BB2800977C7E /* AppDelegate.m in Sources */,
331 | CAACE5771E83BB2800977C7E /* main.m in Sources */,
332 | );
333 | runOnlyForDeploymentPostprocessing = 0;
334 | };
335 | CAACE5871E83BB2800977C7E /* Sources */ = {
336 | isa = PBXSourcesBuildPhase;
337 | buildActionMask = 2147483647;
338 | files = (
339 | CAACE5901E83BB2800977C7E /* LXScrollContentViewTests.m in Sources */,
340 | );
341 | runOnlyForDeploymentPostprocessing = 0;
342 | };
343 | CAACE5921E83BB2800977C7E /* Sources */ = {
344 | isa = PBXSourcesBuildPhase;
345 | buildActionMask = 2147483647;
346 | files = (
347 | CAACE59B1E83BB2800977C7E /* LXScrollContentViewUITests.m in Sources */,
348 | );
349 | runOnlyForDeploymentPostprocessing = 0;
350 | };
351 | /* End PBXSourcesBuildPhase section */
352 |
353 | /* Begin PBXTargetDependency section */
354 | CAACE58D1E83BB2800977C7E /* PBXTargetDependency */ = {
355 | isa = PBXTargetDependency;
356 | target = CAACE5711E83BB2800977C7E /* LXScrollContentView */;
357 | targetProxy = CAACE58C1E83BB2800977C7E /* PBXContainerItemProxy */;
358 | };
359 | CAACE5981E83BB2800977C7E /* PBXTargetDependency */ = {
360 | isa = PBXTargetDependency;
361 | target = CAACE5711E83BB2800977C7E /* LXScrollContentView */;
362 | targetProxy = CAACE5971E83BB2800977C7E /* PBXContainerItemProxy */;
363 | };
364 | /* End PBXTargetDependency section */
365 |
366 | /* Begin PBXVariantGroup section */
367 | CAACE57E1E83BB2800977C7E /* Main.storyboard */ = {
368 | isa = PBXVariantGroup;
369 | children = (
370 | CAACE57F1E83BB2800977C7E /* Base */,
371 | );
372 | name = Main.storyboard;
373 | sourceTree = "";
374 | };
375 | CAACE5831E83BB2800977C7E /* LaunchScreen.storyboard */ = {
376 | isa = PBXVariantGroup;
377 | children = (
378 | CAACE5841E83BB2800977C7E /* Base */,
379 | );
380 | name = LaunchScreen.storyboard;
381 | sourceTree = "";
382 | };
383 | /* End PBXVariantGroup section */
384 |
385 | /* Begin XCBuildConfiguration section */
386 | CAACE59D1E83BB2800977C7E /* Debug */ = {
387 | isa = XCBuildConfiguration;
388 | buildSettings = {
389 | ALWAYS_SEARCH_USER_PATHS = NO;
390 | CLANG_ANALYZER_NONNULL = YES;
391 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
392 | CLANG_CXX_LIBRARY = "libc++";
393 | CLANG_ENABLE_MODULES = YES;
394 | CLANG_ENABLE_OBJC_ARC = YES;
395 | CLANG_WARN_BOOL_CONVERSION = YES;
396 | CLANG_WARN_CONSTANT_CONVERSION = YES;
397 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
398 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
399 | CLANG_WARN_EMPTY_BODY = YES;
400 | CLANG_WARN_ENUM_CONVERSION = YES;
401 | CLANG_WARN_INFINITE_RECURSION = YES;
402 | CLANG_WARN_INT_CONVERSION = YES;
403 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
404 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
405 | CLANG_WARN_UNREACHABLE_CODE = YES;
406 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
407 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
408 | COPY_PHASE_STRIP = NO;
409 | DEBUG_INFORMATION_FORMAT = dwarf;
410 | ENABLE_STRICT_OBJC_MSGSEND = YES;
411 | ENABLE_TESTABILITY = YES;
412 | GCC_C_LANGUAGE_STANDARD = gnu99;
413 | GCC_DYNAMIC_NO_PIC = NO;
414 | GCC_NO_COMMON_BLOCKS = YES;
415 | GCC_OPTIMIZATION_LEVEL = 0;
416 | GCC_PREPROCESSOR_DEFINITIONS = (
417 | "DEBUG=1",
418 | "$(inherited)",
419 | );
420 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
421 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
422 | GCC_WARN_UNDECLARED_SELECTOR = YES;
423 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
424 | GCC_WARN_UNUSED_FUNCTION = YES;
425 | GCC_WARN_UNUSED_VARIABLE = YES;
426 | IPHONEOS_DEPLOYMENT_TARGET = 10.2;
427 | MTL_ENABLE_DEBUG_INFO = YES;
428 | ONLY_ACTIVE_ARCH = YES;
429 | SDKROOT = iphoneos;
430 | };
431 | name = Debug;
432 | };
433 | CAACE59E1E83BB2800977C7E /* Release */ = {
434 | isa = XCBuildConfiguration;
435 | buildSettings = {
436 | ALWAYS_SEARCH_USER_PATHS = NO;
437 | CLANG_ANALYZER_NONNULL = YES;
438 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
439 | CLANG_CXX_LIBRARY = "libc++";
440 | CLANG_ENABLE_MODULES = YES;
441 | CLANG_ENABLE_OBJC_ARC = YES;
442 | CLANG_WARN_BOOL_CONVERSION = YES;
443 | CLANG_WARN_CONSTANT_CONVERSION = YES;
444 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
445 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
446 | CLANG_WARN_EMPTY_BODY = YES;
447 | CLANG_WARN_ENUM_CONVERSION = YES;
448 | CLANG_WARN_INFINITE_RECURSION = YES;
449 | CLANG_WARN_INT_CONVERSION = YES;
450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
451 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
452 | CLANG_WARN_UNREACHABLE_CODE = YES;
453 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
454 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
455 | COPY_PHASE_STRIP = NO;
456 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
457 | ENABLE_NS_ASSERTIONS = NO;
458 | ENABLE_STRICT_OBJC_MSGSEND = YES;
459 | GCC_C_LANGUAGE_STANDARD = gnu99;
460 | GCC_NO_COMMON_BLOCKS = YES;
461 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
462 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
463 | GCC_WARN_UNDECLARED_SELECTOR = YES;
464 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
465 | GCC_WARN_UNUSED_FUNCTION = YES;
466 | GCC_WARN_UNUSED_VARIABLE = YES;
467 | IPHONEOS_DEPLOYMENT_TARGET = 10.2;
468 | MTL_ENABLE_DEBUG_INFO = NO;
469 | SDKROOT = iphoneos;
470 | VALIDATE_PRODUCT = YES;
471 | };
472 | name = Release;
473 | };
474 | CAACE5A01E83BB2800977C7E /* Debug */ = {
475 | isa = XCBuildConfiguration;
476 | buildSettings = {
477 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
478 | DEVELOPMENT_TEAM = 6HK953UL65;
479 | INFOPLIST_FILE = LXScrollContentView/Info.plist;
480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
481 | OTHER_LDFLAGS = "";
482 | PRODUCT_BUNDLE_IDENTIFIER = hahaha.LXScrollContentView;
483 | PRODUCT_NAME = "$(TARGET_NAME)";
484 | };
485 | name = Debug;
486 | };
487 | CAACE5A11E83BB2800977C7E /* Release */ = {
488 | isa = XCBuildConfiguration;
489 | buildSettings = {
490 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
491 | DEVELOPMENT_TEAM = 6HK953UL65;
492 | INFOPLIST_FILE = LXScrollContentView/Info.plist;
493 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
494 | OTHER_LDFLAGS = "";
495 | PRODUCT_BUNDLE_IDENTIFIER = hahaha.LXScrollContentView;
496 | PRODUCT_NAME = "$(TARGET_NAME)";
497 | };
498 | name = Release;
499 | };
500 | CAACE5A31E83BB2800977C7E /* Debug */ = {
501 | isa = XCBuildConfiguration;
502 | buildSettings = {
503 | BUNDLE_LOADER = "$(TEST_HOST)";
504 | DEVELOPMENT_TEAM = RQB4AJCS2L;
505 | INFOPLIST_FILE = LXScrollContentViewTests/Info.plist;
506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
507 | PRODUCT_BUNDLE_IDENTIFIER = snsports.LXScrollContentViewTests;
508 | PRODUCT_NAME = "$(TARGET_NAME)";
509 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LXScrollContentView.app/LXScrollContentView";
510 | };
511 | name = Debug;
512 | };
513 | CAACE5A41E83BB2800977C7E /* Release */ = {
514 | isa = XCBuildConfiguration;
515 | buildSettings = {
516 | BUNDLE_LOADER = "$(TEST_HOST)";
517 | DEVELOPMENT_TEAM = RQB4AJCS2L;
518 | INFOPLIST_FILE = LXScrollContentViewTests/Info.plist;
519 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
520 | PRODUCT_BUNDLE_IDENTIFIER = snsports.LXScrollContentViewTests;
521 | PRODUCT_NAME = "$(TARGET_NAME)";
522 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LXScrollContentView.app/LXScrollContentView";
523 | };
524 | name = Release;
525 | };
526 | CAACE5A61E83BB2800977C7E /* Debug */ = {
527 | isa = XCBuildConfiguration;
528 | buildSettings = {
529 | DEVELOPMENT_TEAM = RQB4AJCS2L;
530 | INFOPLIST_FILE = LXScrollContentViewUITests/Info.plist;
531 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
532 | PRODUCT_BUNDLE_IDENTIFIER = snsports.LXScrollContentViewUITests;
533 | PRODUCT_NAME = "$(TARGET_NAME)";
534 | TEST_TARGET_NAME = LXScrollContentView;
535 | };
536 | name = Debug;
537 | };
538 | CAACE5A71E83BB2800977C7E /* Release */ = {
539 | isa = XCBuildConfiguration;
540 | buildSettings = {
541 | DEVELOPMENT_TEAM = RQB4AJCS2L;
542 | INFOPLIST_FILE = LXScrollContentViewUITests/Info.plist;
543 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
544 | PRODUCT_BUNDLE_IDENTIFIER = snsports.LXScrollContentViewUITests;
545 | PRODUCT_NAME = "$(TARGET_NAME)";
546 | TEST_TARGET_NAME = LXScrollContentView;
547 | };
548 | name = Release;
549 | };
550 | /* End XCBuildConfiguration section */
551 |
552 | /* Begin XCConfigurationList section */
553 | CAACE56D1E83BB2800977C7E /* Build configuration list for PBXProject "LXScrollContentView" */ = {
554 | isa = XCConfigurationList;
555 | buildConfigurations = (
556 | CAACE59D1E83BB2800977C7E /* Debug */,
557 | CAACE59E1E83BB2800977C7E /* Release */,
558 | );
559 | defaultConfigurationIsVisible = 0;
560 | defaultConfigurationName = Release;
561 | };
562 | CAACE59F1E83BB2800977C7E /* Build configuration list for PBXNativeTarget "LXScrollContentView" */ = {
563 | isa = XCConfigurationList;
564 | buildConfigurations = (
565 | CAACE5A01E83BB2800977C7E /* Debug */,
566 | CAACE5A11E83BB2800977C7E /* Release */,
567 | );
568 | defaultConfigurationIsVisible = 0;
569 | defaultConfigurationName = Release;
570 | };
571 | CAACE5A21E83BB2800977C7E /* Build configuration list for PBXNativeTarget "LXScrollContentViewTests" */ = {
572 | isa = XCConfigurationList;
573 | buildConfigurations = (
574 | CAACE5A31E83BB2800977C7E /* Debug */,
575 | CAACE5A41E83BB2800977C7E /* Release */,
576 | );
577 | defaultConfigurationIsVisible = 0;
578 | defaultConfigurationName = Release;
579 | };
580 | CAACE5A51E83BB2800977C7E /* Build configuration list for PBXNativeTarget "LXScrollContentViewUITests" */ = {
581 | isa = XCConfigurationList;
582 | buildConfigurations = (
583 | CAACE5A61E83BB2800977C7E /* Debug */,
584 | CAACE5A71E83BB2800977C7E /* Release */,
585 | );
586 | defaultConfigurationIsVisible = 0;
587 | defaultConfigurationName = Release;
588 | };
589 | /* End XCConfigurationList section */
590 | };
591 | rootObject = CAACE56A1E83BB2800977C7E /* Project object */;
592 | }
593 |
--------------------------------------------------------------------------------