├── README.md
├── Example
├── Example
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.h
│ ├── main.m
│ ├── Info.plist
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ └── AppDelegate.m
├── Example.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
└── Demo
│ ├── TestTitleView.h
│ ├── ViewController.h
│ ├── TestViewController3.h
│ ├── TestViewController2.h
│ ├── TestTitleView.m
│ ├── ViewController.m
│ ├── TestViewController3.m
│ └── TestViewController2.m
├── Source
├── LCHeadView.h
├── MainScrollView.h
├── UIScrollView+Category.h
├── UIViewController+Category.h
├── LCHeadView.m
├── LCPageViewStyle.h
├── UIViewController+Category.m
├── MainScrollView.m
├── LCTitleView.h
├── UIView+LCFrame.h
├── UIScrollView+Category.m
├── LCPageView.h
├── LCPageViewProtocol.h
├── UIView+LCFrame.m
├── LCPageViewStyle.m
├── LCPageView.m
└── LCTitleView.m
├── LCPageView.podspec
├── LICENSE
└── .gitignore
/README.md:
--------------------------------------------------------------------------------
1 | # LCPageView
2 | ScrollView嵌套Collectionview无痕衔接四向滚动, 支持自定义TitleView
3 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Source/LCHeadView.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCHeadView.h
3 | // Example
4 | //
5 | // Created by 复新会智 on 2018/5/18.
6 | // Copyright © 2018年 linechan. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LCHeadView : UIView
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Example/Demo/TestTitleView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TestTitleView.h
3 | // LCPageView
4 | //
5 | // Created by 复新会智 on 2018/5/15.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import "LCTitleView.h"
10 |
11 | @interface TestTitleView : LCTitleView
12 |
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/Source/MainScrollView.h:
--------------------------------------------------------------------------------
1 | //
2 | // MainScrollView.h
3 | // LCPageView
4 | //
5 | // Created by 陈连辰 on 2018/5/13.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MainScrollView : UIScrollView
12 |
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/Example/Demo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // LCPageView
4 | //
5 | // Created by 复新会智 on 2018/5/9.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/Example/Demo/TestViewController3.h:
--------------------------------------------------------------------------------
1 | //
2 | // TestViewController3.h
3 | // LCPageView
4 | //
5 | // Created by 复新会智 on 2018/5/16.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TestViewController3 : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/Demo/TestViewController2.h:
--------------------------------------------------------------------------------
1 | //
2 | // TestViewController2.h
3 | // LCPageView
4 | //
5 | // Created by 复新会智 on 2018/5/14.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TestViewController2 : UIViewController
12 |
13 | - (instancetype)initWithOverallRefresh:(BOOL)isOverall;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Example/Example/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // Example
4 | //
5 | // Created by 陈连辰 on 2018/5/17.
6 | // Copyright © 2018年 linechan. 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 |
--------------------------------------------------------------------------------
/Example/Demo/TestTitleView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TestTitleView.m
3 | // LCPageView
4 | //
5 | // Created by 复新会智 on 2018/5/15.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import "TestTitleView.h"
10 |
11 | @implementation TestTitleView
12 |
13 |
14 | - (void)collectionViewDidScroll:(UICollectionView *)collectionView
15 | {
16 | // NSLog(@"collectionViewDidScroll");
17 | }
18 | @end
19 |
--------------------------------------------------------------------------------
/Source/UIScrollView+Category.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIScrollView+Category.h
3 | // LCPageView
4 | //
5 | // Created by 陈连辰 on 2018/5/12.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import
10 | typedef void(^LCScrollHandle)(UIScrollView *);
11 |
12 | @interface UIScrollView (Category)
13 | /** 滚动代理 */
14 | @property(nonatomic, copy) LCScrollHandle scrollHandle;
15 | @end
16 |
--------------------------------------------------------------------------------
/Example/Example/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // Example
4 | //
5 | // Created by 陈连辰 on 2018/5/17.
6 | // Copyright © 2018年 linechan. 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 |
--------------------------------------------------------------------------------
/Source/UIViewController+Category.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Category.h
3 | // LCPageView
4 | //
5 | // Created by 陈连辰 on 2018/5/12.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | @interface UIViewController (Category)
13 |
14 |
15 | /** 滚动视图(控制器需要把当前的scrollView赋值过来) */
16 | @property(nonatomic, strong) UIScrollView *lcScrollView;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Source/LCHeadView.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCHeadView.m
3 | // Example
4 | //
5 | // Created by 复新会智 on 2018/5/18.
6 | // Copyright © 2018年 linechan. All rights reserved.
7 | //
8 |
9 | #import "LCHeadView.h"
10 |
11 | @implementation LCHeadView
12 |
13 | /*
14 | // Only override drawRect: if you perform custom drawing.
15 | // An empty implementation adversely affects performance during animation.
16 | - (void)drawRect:(CGRect)rect {
17 | // Drawing code
18 | }
19 | */
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/LCPageView.podspec:
--------------------------------------------------------------------------------
1 |
2 | Pod::Spec.new do |s|
3 |
4 | s.name = "LCPageView"
5 | s.version = "0.0.2"
6 | s.summary = "LCPageView"
7 |
8 | s.homepage = "https://github.com/CoderLineChan/LCPageView"
9 |
10 | s.license = "MIT"
11 |
12 | s.author = { "lianchen" => "lianchen551@163.com" }
13 |
14 | s.ios.deployment_target = "8.0"
15 |
16 | s.source = { :git => "https://github.com/CoderLineChan/LCPageView.git", :tag => "#{s.version}" }
17 |
18 | s.source_files = "Source/**/*.{h,m}"
19 |
20 |
21 | end
22 |
--------------------------------------------------------------------------------
/Source/LCPageViewStyle.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCPageViewStyle.h
3 | // LCPageView
4 | //
5 | // Created by 陈连辰 on 2018/5/9.
6 | // Copyright © 2018年 lianchen. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "LCPageViewProtocol.h"
11 |
12 | @interface LCPageViewStyle : NSObject
13 | /**
14 | 普通模式:没有任何特效
15 | 背景平均填满整个TitleView
16 | 只有字体与背景各两种颜色
17 | */
18 | - (void)initNormalStyle;
19 |
20 | /**
21 | 默认模式
22 | */
23 | - (void)initDefaulStyle;
24 |
25 | /**
26 | 渐变模式
27 | */
28 | - (void)initGradientStyle;
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/Source/UIViewController+Category.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Category.m
3 | // LCPageView
4 | //
5 | // Created by 陈连辰 on 2018/5/12.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import "UIViewController+Category.h"
10 | #import
11 | #import "UIScrollView+Category.h"
12 |
13 | #define lcScrollViewKey @"lcScrollViewKey"
14 |
15 | @implementation UIViewController (Category)
16 |
17 |
18 | - (void)setLcScrollView:(UIScrollView *)lcScrollView
19 | {
20 | objc_setAssociatedObject(self, lcScrollViewKey, lcScrollView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
21 | }
22 | - (UIScrollView *)lcScrollView
23 | {
24 | return objc_getAssociatedObject(self, lcScrollViewKey);
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/Source/MainScrollView.m:
--------------------------------------------------------------------------------
1 | //
2 | // MainScrollView.m
3 | // LCPageView
4 | //
5 | // Created by 陈连辰 on 2018/5/13.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import "MainScrollView.h"
10 |
11 | @interface MainScrollView ()
12 |
13 |
14 | @end
15 |
16 | @implementation MainScrollView
17 |
18 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(nonnull UIGestureRecognizer *)otherGestureRecognizer
19 | {
20 | return ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
21 | [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]);
22 | }
23 | - (void)dealloc
24 | {
25 | // NSLog(@"MainScrollView销毁");
26 | }
27 | @end
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 陈陈陈
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 |
--------------------------------------------------------------------------------
/Example/Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | 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 |
--------------------------------------------------------------------------------
/.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 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | # CocoaPods
32 | #
33 | # We recommend against adding the Pods directory to your .gitignore. However
34 | # you should judge for yourself, the pros and cons are mentioned at:
35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
36 | #
37 | # Pods/
38 |
39 | # Carthage
40 | #
41 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
42 | # Carthage/Checkouts
43 |
44 | Carthage/Build
45 |
46 | # fastlane
47 | #
48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
49 | # screenshots whenever they are needed.
50 | # For more information about the recommended setup visit:
51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
52 |
53 | fastlane/report.xml
54 | fastlane/Preview.html
55 | fastlane/screenshots/**/*.png
56 | fastlane/test_output
57 |
58 | # Code Injection
59 | #
60 | # After new code Injection tools there's a generated folder /iOSInjectionProject
61 | # https://github.com/johnno1962/injectionforxcode
62 |
63 | iOSInjectionProject/
64 |
--------------------------------------------------------------------------------
/Source/LCTitleView.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCTitleView.h
3 | // LCPageView
4 | //
5 | // Created by lianchen on 2018/5/15.
6 | // Copyright © 2018年 lianchen. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "LCPageViewStyle.h"
11 |
12 |
13 | #pragma mark - 标题视图
14 |
15 | @interface LCTitleView : UIView
16 | /** 样式 */
17 | @property(nonatomic, strong)id style;
18 | /** 标题内容数组 */
19 | @property(nonatomic, strong, readonly)NSArray *titles;
20 |
21 | #pragma mark - 接口
22 | /// 创建titleView
23 | - (instancetype)initWithFrame:(CGRect)frame
24 | titles:(NSArray *)titles
25 | pageViewStyle:(id )pageViewStyle;
26 |
27 | /**
28 | 更新标题内容
29 | */
30 | - (void)updateTitles:(NSArray *)titles;
31 |
32 | /**
33 | 初始化 默认UI, 不调用则不初始化任何UI
34 | */
35 | - (void)initSuperTitleView;
36 | #pragma mark - 子控制器调用方法
37 | /**
38 | 滚动内容视图
39 |
40 | @param index index
41 | */
42 | - (void)scrollCollectionviewWithIndex:(NSInteger)index;
43 |
44 | #pragma mark - 内容视图滚动的代理方法(子类重写)
45 |
46 | /**
47 | 正在滚动
48 |
49 | @param collectionView 内容滚动视图
50 | */
51 | - (void)collectionViewDidScroll:(UICollectionView *)collectionView;
52 |
53 | /// 即将开始拖拽
54 | - (void)collectionViewWillBeginDragging:(UICollectionView *)collectionView;
55 |
56 | /// 已经停止拖拽
57 | - (void)collectionViewDidEndDragging:(UICollectionView *)collectionView willDecelerate:(BOOL)decelerate;
58 |
59 | /// 已经结束拖拽
60 | - (void)collectionViewDidEndDecelerating:(UICollectionView *)collectionView;
61 |
62 | /// 已经滚动结束
63 | - (void)collectionViewDidEndScrollingAnimation:(UICollectionView *)collectionView;
64 |
65 |
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/Source/UIView+LCFrame.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+LCFrame.h
3 | // 各种分类
4 | //
5 | // Created by 陈连辰 on 16/8/10.
6 | // Copyright © 2016年 linechen. All rights reserved.
7 | //
8 |
9 | #import
10 | //屏幕尺寸
11 | #define kScreenBounds [UIScreen mainScreen].bounds
12 | //屏幕高度
13 | #define kScreenH [UIScreen mainScreen].bounds.size.height
14 | //屏幕宽度
15 | #define kScreenW [UIScreen mainScreen].bounds.size.width
16 |
17 | /// 消除警告
18 | #define SuppressPerformSelectorLeakWarning(Stuff) \
19 | do { \
20 | _Pragma("clang diagnostic push") \
21 | _Pragma("clang diagnostic ignored \"-Wundeclared-selector\"") \
22 | Stuff; \
23 | _Pragma("clang diagnostic pop") \
24 | } while (0)
25 |
26 | /// 消除警告
27 | #define SuppressPerformSelectorLeakWarning1(Stuff) \
28 | do { \
29 | _Pragma("clang diagnostic push") \
30 | _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
31 | Stuff; \
32 | _Pragma("clang diagnostic pop") \
33 | } while (0)
34 |
35 |
36 |
37 | @interface UIView (LCFrame)
38 |
39 | @property (nonatomic, assign) CGSize lc_size;
40 | @property (nonatomic, assign) CGFloat lc_x;
41 | @property (nonatomic, assign) CGFloat lc_y;
42 | @property (nonatomic, assign) CGFloat lc_width;
43 | @property (nonatomic, assign) CGFloat lc_height;
44 | @property (nonatomic, assign) CGFloat lc_centerX;
45 | @property (nonatomic, assign) CGFloat lc_centerY;
46 |
47 | /** 控件最右边线的位置(最大的X值) */
48 | @property (nonatomic, assign) CGFloat lc_right;
49 | /** 控件最左边线的位置(控件的X值) */
50 | @property (nonatomic, assign) CGFloat lc_left;
51 | /** 控件最顶部线的位置(控件的Y值) */
52 | @property (nonatomic, assign) CGFloat lc_top;
53 | /** 控件最底部线的位置(最大的Y值) */
54 | @property (nonatomic, assign) CGFloat lc_bottom;
55 |
56 | /// 设置圆角
57 | - (void)lc_setCornerRadiusWithRadius:(CGFloat)radius;
58 |
59 | @end
60 |
--------------------------------------------------------------------------------
/Example/Example/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 |
--------------------------------------------------------------------------------
/Source/UIScrollView+Category.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIScrollView+Category.m
3 | // LCPageView
4 | //
5 | // Created by 陈连辰 on 2018/5/12.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import "UIScrollView+Category.h"
10 | #import
11 |
12 |
13 | #define LCScrollHandleKey @"scrollHandle"
14 |
15 |
16 | @interface UIScrollView ()
17 |
18 |
19 | @end
20 |
21 | @implementation UIScrollView (Category)
22 |
23 | - (void)scrollDidScroll
24 | {
25 | [self scrollDidScroll];
26 | self.scrollHandle ? self.scrollHandle(self) : nil;
27 | }
28 | - (LCScrollHandle)scrollHandle
29 | {
30 | return objc_getAssociatedObject(self, LCScrollHandleKey);
31 | }
32 | - (void)setScrollHandle:(LCScrollHandle)scrollHandle
33 | {
34 | objc_setAssociatedObject(self, LCScrollHandleKey, scrollHandle, OBJC_ASSOCIATION_COPY_NONATOMIC);
35 | }
36 |
37 | + (void)load
38 | {
39 | static dispatch_once_t predicate;
40 | dispatch_once(&predicate, ^{
41 | SEL originSel = @selector(_notifyDidScroll);
42 | SEL newSel = @selector(scrollDidScroll);
43 | [self associationScrollWethodWithClass:[self class] originSel:originSel associationSel:newSel];
44 | });
45 | }
46 | + (void)associationScrollWethodWithClass:(Class)cls
47 | originSel:(SEL)originSel
48 | associationSel:(SEL)associationSel
49 | {
50 | Method originMethod = class_getInstanceMethod(cls, originSel);
51 | Method associationMethod = class_getInstanceMethod(cls, associationSel);
52 |
53 | BOOL isSucceed = class_addMethod(cls, originSel, method_getImplementation(associationMethod), method_getTypeEncoding(associationMethod));
54 | if (isSucceed) {
55 | class_replaceMethod(cls, associationSel, method_getImplementation(originMethod), method_getTypeEncoding(originMethod));
56 | }else {
57 | method_exchangeImplementations(originMethod, associationMethod);
58 | }
59 | }
60 | @end
61 |
--------------------------------------------------------------------------------
/Source/LCPageView.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCPageView.h
3 | // LCPageView
4 | //
5 | // Created by lianchen on 2018/5/9.
6 | // Copyright © 2018年 lianchen. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "LCPageViewStyle.h"
11 | #import "MainScrollView.h"
12 | #import "LCTitleView.h"
13 | #import "LCHeadView.h"
14 | #import "UIViewController+Category.h"
15 | #import "LCPageViewProtocol.h"
16 |
17 | @class LCPageView;
18 | @protocol LCPageViewDelegate
19 | /*
20 | *代理方法 这个方法是切换了选中的的代理方法
21 | */
22 | - (void)pageView:(LCPageView *)pageView moveToIndex:(NSInteger)index;
23 | @end
24 |
25 |
26 |
27 | @interface LCPageView : UIView
28 |
29 | /** 代理 */
30 | @property(nonatomic, weak) id delegate;
31 |
32 | /**
33 | 最底部滚动视图
34 | 主要用于整个PageView添加下拉刷新功能
35 | bounces 默认为NO
36 | */
37 | @property(nonatomic, strong, readonly)MainScrollView *mainScrollView;
38 |
39 | /**
40 | 初始化一个pageView
41 | 使用注意(重要): 需要在内容控制器里面把主滚动视图赋值给 lcScrollView
42 | 例如: self.lcScrollView = self.tableView;
43 |
44 | @param frame frame
45 | @param headView 头部视图
46 | @param childControllers 内容控制器(需要设置控制器的title)
47 | @param parentController 当前控制器
48 | @param customTitleView 自定义标题的View
49 | @param pageViewStyle pageView的样式(有默认的样式)
50 | @return pageView
51 | */
52 | - (instancetype)initWithFrame:(CGRect)frame
53 | headView:(UIView *)headView
54 | childControllers:(NSArray *)childControllers
55 | parentController:(UIViewController *)parentController
56 | customTitleView:(LCTitleView *)customTitleView
57 | pageViewStyle:(id )pageViewStyle;
58 |
59 |
60 | /**
61 | 手动调用滚动
62 | */
63 | - (void)moveToIndex:(NSInteger)index;
64 |
65 | /**
66 | 更新pageView的高度
67 | */
68 | - (void)updatePageViewHeight:(CGFloat)height;
69 |
70 | /**
71 | 更新标题内容
72 | */
73 | - (void)updatePageViewTitles:(NSArray *)titles;
74 |
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/Example/Demo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // LCPageView
4 | //
5 | // Created by 复新会智 on 2018/5/9.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "LCPageView.h"
11 | #import "TestViewController2.h"
12 |
13 |
14 | @interface ViewController ()
15 |
16 | /** <#注释#> */
17 | @property(nonatomic, strong) LCPageView *pageView;
18 |
19 |
20 | @end
21 |
22 | @implementation ViewController
23 |
24 | - (void)viewDidLoad {
25 | [super viewDidLoad];
26 | [self setupExample];
27 | NSLog(@"完成");
28 | }
29 |
30 | - (void)setupExample
31 | {
32 | UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
33 | btn1.frame = CGRectMake(150, 100, 120, 35);
34 | btn1.titleLabel.font = [UIFont systemFontOfSize:14];
35 | [btn1 setTitle:@"示例1:整体刷新" forState:UIControlStateNormal];
36 | btn1.backgroundColor = [UIColor redColor];
37 | btn1.tag = 1;
38 | [btn1 addTarget:self action:@selector(btnDidClick:) forControlEvents:UIControlEventTouchUpInside];
39 | [self.view addSubview:btn1];
40 |
41 | UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
42 | btn2.frame = CGRectMake(150, 170, 120, 35);
43 | [btn2 setTitle:@"示例2:内部刷新" forState:UIControlStateNormal];
44 | btn2.backgroundColor = [UIColor orangeColor];
45 | btn2.titleLabel.font = [UIFont systemFontOfSize:14];
46 | btn2.tag = 2;
47 | [btn2 addTarget:self action:@selector(btnDidClick:) forControlEvents:UIControlEventTouchUpInside];
48 | [self.view addSubview:btn2];
49 | }
50 | - (void)btnDidClick:(UIButton *)btn
51 | {
52 | if (btn.tag == 1) {
53 | TestViewController2 *vc = [[TestViewController2 alloc] initWithOverallRefresh:YES];
54 | [self.navigationController pushViewController:vc animated:YES];
55 | }else if (btn.tag == 2) {
56 | TestViewController2 *vc = [[TestViewController2 alloc] init];
57 | [self.navigationController pushViewController:vc animated:YES];
58 | }
59 | }
60 | @end
61 |
--------------------------------------------------------------------------------
/Example/Example/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 | }
--------------------------------------------------------------------------------
/Source/LCPageViewProtocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCPageViewProtocol.h
3 | // Example
4 | //
5 | // Created by 陈连辰 on 2018/9/16.
6 | // Copyright © 2018年 linechan. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol LCPageContentViewProtocol
12 |
13 | #pragma mark - 滚动代理
14 | /// 即将开始拖拽
15 | - (void)lc_scrollViewWillBeginDragging:(UIScrollView *)scrollView;
16 |
17 | /// 已经停止拖拽
18 | - (void)lc_scrollViewDidEndDragging:(UICollectionView *)collectionView willDecelerate:(BOOL)decelerate;
19 |
20 | /// 已经结束拖拽
21 | - (void)lc_scrollViewDidEndDecelerating:(UICollectionView *)collectionView;
22 |
23 | /// 已经滚动结束
24 | - (void)lc_scrollViewDidEndScrollingAnimation:(UICollectionView *)collectionView;
25 |
26 | /// 水平滚动
27 | - (void)lc_scrollViewDidHorizontalScroll:(UIScrollView *)scrollView;
28 |
29 | /// 垂直滚动
30 | - (void)lc_scrollViewDidVerticalScroll:(UIScrollView *)scrollView;
31 |
32 |
33 | @end
34 |
35 |
36 | @protocol LCPageViewStyleProtocol
37 | #pragma mark - 标题的属性
38 | /** title标题高度 */
39 | - (CGFloat)titleViewHeight;
40 | /** titleLabel的字体大小 */
41 | - (CGFloat)titleLabelFont;
42 | /** 标题视图背景颜色 */
43 | - (UIColor *)titleViewBgColor;
44 | /** titleLabel正常颜色 */
45 | - (UIColor *)titleLabelNormalColor;
46 | /** titleLabel选中颜色 */
47 | - (UIColor *)titleLabelSelectColor;
48 | /** titleLabel正常背景颜色 */
49 | - (UIColor *)titleLabelNormalBgColor;
50 | /** titleLabel选中背景颜色 */
51 | - (UIColor *)titleLabelSelectBgColor;
52 | /** 是否平均标题的宽度 */
53 | - (BOOL)isAverageTitleWidth;
54 | /** 标题滚动功能 */
55 | - (BOOL)titleScrollEnable;
56 | /** 是否能点击标题 */
57 | - (BOOL)isTitleClick;
58 | #pragma mark - 标题缩放功能
59 | /** 是否启用缩放 */
60 | - (BOOL)titleZoomEnable;
61 | /** 缩放大小0~1 */
62 | - (CGFloat)titleZoomMaxScale;
63 | /** 是否隐藏标题,当只有一个子控制器的时候 */
64 | - (BOOL)titleHiddenForOneController;
65 | #pragma mark - 底部线功能
66 | /** 是否启用底部线 */
67 | - (BOOL)bottomLineEnable;
68 | /** 底部线颜色 */
69 | - (UIColor *)bottomLineColor;
70 | /** 底部线高度 */
71 | - (CGFloat)bottomLineHeight;
72 | #pragma mark - 颜色渐变
73 | /** 是否启用颜色渐变 */
74 | - (BOOL)titleMillcolorGradEnable;
75 | /** 标题label超过长度忽略方式 */
76 | - (NSLineBreakMode)labelLineBreakMode;
77 |
78 | /** 内容视图是否能滚动 */
79 | - (BOOL)contentViewScrollEnabled;
80 |
81 | @end
82 |
--------------------------------------------------------------------------------
/Example/Example/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // Example
4 | //
5 | // Created by 陈连辰 on 2018/5/17.
6 | // Copyright © 2018年 linechan. 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 |
--------------------------------------------------------------------------------
/Example/Demo/TestViewController3.m:
--------------------------------------------------------------------------------
1 | //
2 | // TestViewController3.m
3 | // LCPageView
4 | //
5 | // Created by 复新会智 on 2018/5/16.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 | #import "TestViewController3.h"
10 | //#import
11 | #import "UIViewController+Category.h"
12 | #import "TestViewController2.h"
13 |
14 | @interface TestViewController3 ()
15 |
16 | /** <#describe#> */
17 | @property(nonatomic, strong)UITableView *tableView;
18 |
19 | @end
20 |
21 | @implementation TestViewController3
22 |
23 | - (void)viewDidLoad {
24 | [super viewDidLoad];
25 | self.lcScrollView = self.tableView;
26 |
27 | self.view.backgroundColor = [UIColor greenColor];
28 | [self.view addSubview:self.tableView];
29 | [self.tableView reloadData];
30 | // __weak typeof(self) weakSelf = self;
31 | // self.tableView.mj_header = [MJRefreshGifHeader headerWithRefreshingBlock:^{
32 | // [weakSelf endRefresh];
33 | // }];
34 | }
35 |
36 | - (void)endRefresh
37 | {
38 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
39 | NSLog(@"结束刷新");
40 | // [self.tableView.mj_header endRefreshing];
41 | });
42 | }
43 |
44 | - (UITableView *)tableView
45 | {
46 | if (!_tableView) {
47 | _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
48 | _tableView.delegate = self;
49 | _tableView.dataSource = self;
50 | }
51 | return _tableView;
52 | }
53 |
54 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
55 | {
56 | return 50;
57 | }
58 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
59 | {
60 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"2222"];
61 | if (cell == nil) {
62 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"2222"];
63 | }
64 | cell.textLabel.text = [NSString stringWithFormat:@"测试数据:%ld", indexPath.row];
65 | return cell;
66 | }
67 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
68 | {
69 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
70 | TestViewController2 *vc = [[TestViewController2 alloc] init];
71 | [self.navigationController pushViewController:vc animated:YES];
72 |
73 | }
74 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView
75 | {
76 | // NSLog(@"Controller3 OffsetY:%.2f", scrollView.contentOffset.y);
77 | }
78 |
79 | @end
80 |
--------------------------------------------------------------------------------
/Example/Example/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 |
--------------------------------------------------------------------------------
/Source/UIView+LCFrame.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+LCFrame.m
3 | // 各种分类
4 | //
5 | // Created by 陈连辰 on 16/8/10.
6 | // Copyright © 2016年 linechen. All rights reserved.
7 | //
8 |
9 | #import "UIView+LCFrame.h"
10 |
11 |
12 | @implementation UIView (LCFrame)
13 | - (CGFloat)lc_x {
14 | return self.frame.origin.x;
15 | }
16 |
17 | - (void)setLc_x:(CGFloat)lc_x {
18 | CGRect frame = self.frame;
19 | frame.origin.x = lc_x;
20 | self.frame = frame;
21 | }
22 |
23 | - (CGFloat)lc_y {
24 | return self.frame.origin.y;
25 | }
26 |
27 | - (void)setLc_y:(CGFloat)lc_y{
28 | CGRect frame = self.frame;
29 | frame.origin.y = lc_y;
30 | self.frame = frame;
31 | }
32 |
33 | - (CGFloat)lc_width {
34 | return self.frame.size.width;
35 | }
36 |
37 | - (void)setLc_width:(CGFloat)lc_width {
38 | CGRect frame = self.frame;
39 | frame.size.width = lc_width;
40 | self.frame = frame;
41 | }
42 |
43 | - (CGFloat)lc_height {
44 | return self.frame.size.height;
45 | }
46 |
47 | - (void)setLc_height:(CGFloat)lc_height {
48 | CGRect frame = self.frame;
49 | frame.size.height = lc_height;
50 | self.frame = frame;
51 | }
52 |
53 | - (void)setLc_size:(CGSize)lc_size {
54 | CGRect frame = self.frame;
55 | frame.size = lc_size;
56 | self.frame = frame;
57 | }
58 |
59 |
60 | - (CGSize)lc_size {
61 | return self.frame.size;
62 | }
63 |
64 | - (void)setLc_centerX:(CGFloat)lc_centerX {
65 | CGPoint center = self.center;
66 | center.x = lc_centerX;
67 | self.center = center;
68 | }
69 |
70 | - (CGFloat)lc_centerX {
71 | return self.center.x;
72 | }
73 |
74 | - (void)setLc_centerY:(CGFloat)lc_centerY {
75 | CGPoint center = self.center;
76 | center.y = lc_centerY;
77 | self.center = center;
78 | }
79 |
80 | - (CGFloat)lc_centerY {
81 | return self.center.y;
82 | }
83 |
84 | - (void)setLc_right:(CGFloat)lc_right {
85 | CGRect frame = self.frame;
86 | frame.origin.x = lc_right - frame.size.width;
87 | self.frame = frame;
88 | }
89 |
90 | - (CGFloat)lc_right {
91 | return CGRectGetMaxX(self.frame);
92 | }
93 |
94 | - (void)setLc_left:(CGFloat)lc_left {
95 | self.lc_x = lc_left;
96 | }
97 | - (CGFloat)lc_left {
98 | return self.lc_x;
99 | }
100 |
101 | - (void)setLc_bottom:(CGFloat)lc_bottom {
102 | CGRect frame = self.frame;
103 | frame.origin.y = lc_bottom - frame.size.height;
104 | self.frame = frame;
105 | }
106 |
107 | - (CGFloat)lc_bottom {
108 | return CGRectGetMaxY(self.frame);
109 | }
110 |
111 | - (void)setLc_top:(CGFloat)lc_top {
112 | self.lc_y = lc_top;
113 | }
114 |
115 | - (CGFloat)lc_top {
116 | return self.lc_y;
117 | }
118 |
119 |
120 | - (void)lc_setCornerRadiusWithRadius:(CGFloat)radius
121 | {
122 | UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
123 | CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
124 | maskLayer.frame = self.bounds;
125 | maskLayer.path = maskPath.CGPath;
126 | self.layer.mask = maskLayer;
127 | }
128 | @end
129 |
--------------------------------------------------------------------------------
/Example/Demo/TestViewController2.m:
--------------------------------------------------------------------------------
1 | //
2 | // TestViewController2.m
3 | // LCPageView
4 | //
5 | // Created by 复新会智 on 2018/5/14.
6 | // Copyright © 2018年 复新会智. All rights reserved.
7 | //
8 |
9 |
10 | /// 标题的高度
11 | #define kPageViewTitleHeight 38
12 |
13 | ///导航高度
14 | #define kNavH (kScreenH == 812.0 ? 88 : 64)
15 |
16 | //屏幕尺寸
17 | #define kScreenBounds [UIScreen mainScreen].bounds
18 | //屏幕高度
19 | #define kScreenH [UIScreen mainScreen].bounds.size.height
20 | //屏幕宽度
21 | #define kScreenW [UIScreen mainScreen].bounds.size.width
22 |
23 | #import "TestViewController2.h"
24 | #import "LCPageView.h"
25 | #import "TestViewController3.h"
26 | #import "TestTitleView.h"
27 | //#import
28 |
29 |
30 | @interface TestViewController2 ()
31 | /** <#注释#> */
32 | @property(nonatomic, strong) LCPageView *pageView;
33 |
34 | /** <#describe#> */
35 | @property(nonatomic, assign)BOOL isOverall;
36 | @end
37 |
38 | @implementation TestViewController2
39 | - (instancetype)initWithOverallRefresh:(BOOL)isOverall;
40 | {
41 | self = [super init];
42 | if (self) {
43 | self.isOverall = isOverall;
44 | }
45 | return self;
46 | }
47 |
48 | - (void)dealloc
49 | {
50 | NSLog(@"TestViewController2销毁");
51 | }
52 |
53 |
54 | - (void)viewDidLoad {
55 | [super viewDidLoad];
56 | self.view.backgroundColor = [UIColor whiteColor];
57 | [self testPageView];
58 | }
59 | - (void)testPageView
60 | {
61 | NSArray *titles = @[@"测试&测试1", @"测试&测试2", @"主页", @"我的"];
62 | NSMutableArray *array = [NSMutableArray array];
63 | for (NSString *title in titles) {
64 | TestViewController3 *vc = [[TestViewController3 alloc] init];
65 | vc.title = title;
66 | [array addObject:vc];
67 | }
68 | UIView *headView = [[UIView alloc] init];
69 | headView.backgroundColor = [UIColor redColor];
70 | headView.frame = CGRectMake(0, 0, kScreenW, 150);
71 |
72 | LCPageViewStyle *style = [[LCPageViewStyle alloc] init];
73 | [style initNormalStyle];
74 | CGRect frame = CGRectMake(0, 64, kScreenW, kScreenH - 64);
75 | /// 自定义标题
76 | TestTitleView *titleView = [[TestTitleView alloc] initWithFrame:CGRectMake(0,0, kScreenW, 38) titles:titles pageViewStyle:style];
77 | [titleView initSuperTitleView];
78 |
79 | /// 主pageView
80 | self.pageView = [[LCPageView alloc] initWithFrame:frame headView:headView childControllers:array parentController:self customTitleView:titleView pageViewStyle:style];
81 |
82 | /// 是否开启整体bounces/刷新
83 | self.pageView.mainScrollView.bounces = self.isOverall;
84 | if (self.isOverall) {
85 | // __weak typeof(self) weakSelf = self;
86 | // self.pageView.mainScrollView.mj_header = [MJRefreshGifHeader headerWithRefreshingBlock:^{
87 | // NSLog(@"开启刷新");
88 | // [weakSelf endRefresh];
89 | // }];
90 | }
91 | [self.view addSubview:self.pageView];
92 | }
93 |
94 | - (void)endRefresh
95 | {
96 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
97 | NSLog(@"结束刷新");
98 | // [self.pageView.mainScrollView.mj_header endRefreshing];
99 | });
100 | }
101 |
102 | @end
103 |
--------------------------------------------------------------------------------
/Source/LCPageViewStyle.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCPageViewStyle.m
3 | // LCPageView
4 | //
5 | // Created by 陈连辰 on 2018/5/9.
6 | // Copyright © 2018年 lianchen. All rights reserved.
7 | //
8 |
9 | #import "LCPageViewStyle.h"
10 |
11 | @interface LCPageViewStyle ()
12 |
13 |
14 | @end
15 |
16 | @implementation LCPageViewStyle
17 | {
18 | CGFloat _titleViewHeight;
19 | CGFloat _titleLabelFont;
20 | /** 标题视图背景颜色 */
21 | UIColor *_titleViewBgColor;
22 | /** titleLabel正常颜色 */
23 | UIColor *_titleLabelNormalColor;
24 | /** titleLabel选中颜色 */
25 | UIColor *_titleLabelSelectColor;
26 | /** titleLabel正常背景颜色 */
27 | UIColor *_titleLabelNormalBgColor;
28 | /** titleLabel选中背景颜色 */
29 | UIColor *_titleLabelSelectBgColor;
30 | /** 是否平均标题的宽度 */
31 | BOOL _isAverageTitleWidth;
32 | /** 标题滚动功能 */
33 | BOOL _titleScrollEnable;
34 | /** 是否能点击标题 */
35 | BOOL _isTitleClick;
36 | /** 是否启用缩放 */
37 | BOOL _titleZoomEnable;
38 | /** 是否隐藏标题,当只有一个子控制器的时候 */
39 | BOOL _titleHiddenForOneController;
40 | /** 缩放大小0~1 */
41 | CGFloat _titleZoomMaxScale;
42 | /** 是否启用底部线 */
43 | BOOL _bottomLineEnable;
44 | /** 底部线颜色 */
45 | UIColor *_bottomLineColor;
46 | /** 底部线高度 */
47 | CGFloat _bottomLineHeight;
48 | /** 是否启用颜色渐变 */
49 | BOOL _titleMillcolorGradEnable;
50 | /** 标题label超过长度忽略方式 */
51 | NSLineBreakMode _labelLineBreakMode;
52 | /** 内容视图是否能滚动 */
53 | BOOL _contentViewScrollEnabled;
54 | }
55 |
56 | - (instancetype)init
57 | {
58 | self = [super init];
59 | if (self) {
60 | _titleViewHeight = 42;
61 | _titleLabelFont = 15;
62 | _titleViewBgColor = [UIColor colorWithRed:240 / 255.0 green:240 / 255.0 blue:240 / 255.0 alpha:1];
63 | _titleLabelNormalColor = [UIColor lightTextColor];
64 | _titleLabelSelectColor = [UIColor blackColor];
65 | _titleLabelNormalBgColor = [UIColor clearColor];
66 | _titleLabelSelectBgColor = [UIColor clearColor];
67 | _isAverageTitleWidth = NO;
68 | _titleScrollEnable = YES;
69 | _isTitleClick = YES;
70 | _titleZoomEnable = NO;
71 | _titleZoomMaxScale = 0.1;
72 | _bottomLineEnable = NO;
73 | _bottomLineColor = [UIColor redColor];
74 | _bottomLineHeight = 2;
75 | _titleMillcolorGradEnable = NO;
76 | }
77 | return self;
78 | }
79 |
80 |
81 | - (void)initNormalStyle
82 | {
83 | _isAverageTitleWidth = YES;
84 | _titleLabelNormalColor = [UIColor whiteColor];
85 | _titleLabelSelectColor = [UIColor blackColor];
86 | _titleLabelNormalBgColor = [UIColor blueColor];
87 | _titleLabelSelectBgColor = [UIColor lightGrayColor];
88 | _titleScrollEnable = NO;
89 | }
90 | - (void)initDefaulStyle
91 | {
92 | // _titleViewBgColor = [UIColor whiteColor];
93 | _titleLabelNormalColor = [UIColor lightGrayColor];
94 | _titleLabelSelectColor = [UIColor blackColor];
95 | _titleLabelNormalBgColor = [UIColor clearColor];
96 | _titleLabelSelectBgColor = [UIColor clearColor];
97 | _titleScrollEnable = YES;
98 | }
99 | - (void)initGradientStyle
100 | {
101 | _titleViewBgColor = [UIColor clearColor];
102 | _titleLabelNormalColor = [UIColor lightGrayColor];
103 | _titleLabelSelectColor = [UIColor blackColor];
104 | _titleLabelNormalBgColor = [UIColor clearColor];
105 | _titleLabelSelectBgColor = [UIColor clearColor];
106 | _titleScrollEnable = YES;
107 | _bottomLineEnable = YES;
108 | _titleMillcolorGradEnable = YES;
109 | }
110 | #pragma mark - 标题的属性
111 | - (CGFloat)titleViewHeight
112 | {
113 | return _titleViewHeight;
114 | }
115 | - (CGFloat)titleLabelFont
116 | {
117 | return _titleLabelFont;
118 | }
119 | - (UIColor *)titleLabelNormalColor
120 | {
121 | return _titleLabelNormalColor;
122 | }
123 | - (UIColor *)titleLabelSelectColor
124 | {
125 | return _titleLabelSelectColor;
126 | }
127 | - (UIColor *)titleLabelNormalBgColor
128 | {
129 | return _titleLabelNormalBgColor;
130 | }
131 | - (UIColor *)titleLabelSelectBgColor
132 | {
133 | return _titleLabelSelectBgColor;
134 | }
135 | - (BOOL)isAverageTitleWidth
136 | {
137 | return _isAverageTitleWidth;
138 | }
139 | - (BOOL)titleScrollEnable
140 | {
141 | return _titleScrollEnable;
142 | }
143 | - (BOOL)isTitleClick
144 | {
145 | return _isTitleClick;
146 | }
147 | - (UIColor *)titleViewBgColor
148 | {
149 | return _titleViewBgColor;
150 | }
151 | #pragma mark - 标题缩放功能
152 | - (BOOL)titleZoomEnable
153 | {
154 | return _titleZoomEnable;
155 | }
156 | - (CGFloat)titleZoomMaxScale
157 | {
158 | return _titleZoomMaxScale;
159 | }
160 | - (BOOL)titleHiddenForOneController
161 | {
162 | return _titleHiddenForOneController;
163 | }
164 | #pragma mark - 底部线功能
165 | - (BOOL)bottomLineEnable
166 | {
167 | return _bottomLineEnable;
168 | }
169 | - (UIColor *)bottomLineColor
170 | {
171 | return _bottomLineColor;
172 | }
173 | - (CGFloat)bottomLineHeight
174 | {
175 | return _bottomLineHeight;
176 | }
177 | #pragma mark - 颜色渐变
178 | - (BOOL)titleMillcolorGradEnable
179 | {
180 | return _titleMillcolorGradEnable;
181 | }
182 |
183 | - (NSLineBreakMode)labelLineBreakMode
184 | {
185 | return _labelLineBreakMode;
186 | }
187 | - (BOOL)contentViewScrollEnabled
188 | {
189 | return _contentViewScrollEnabled;
190 | }
191 | @end
192 |
--------------------------------------------------------------------------------
/Source/LCPageView.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCPageView.m
3 | // LCPageView
4 | //
5 | // Created by lianchen on 2018/5/9.
6 | // Copyright © 2018年 lianchen. All rights reserved.
7 | //
8 |
9 | #import "LCPageView.h"
10 | #import "UIScrollView+Category.h"
11 | #import "UIView+LCFrame.h"
12 |
13 |
14 | static NSString *const kPageViewCollectionViewCellID = @"kPageViewCollectionViewCellID";
15 |
16 | @interface LCPageView ()
17 | /** 子控制器数组 */
18 | @property(nonatomic, strong)NSArray *childControllers;
19 | /** 父控制器 */
20 | @property(nonatomic, weak)UIViewController *parentController;
21 | /** 样式 */
22 | @property(nonatomic, strong)id style;
23 | /** 最底部滚动视图 */
24 | @property(nonatomic, strong)MainScrollView *mainScrollView;
25 | /** 外面传进来的主滚动视图 */
26 | @property(nonatomic, strong)UIScrollView *outsideScrollView;
27 | /** 主标题视图 */
28 | @property(nonatomic, strong)LCTitleView *titleView;
29 | /** 主标题底部视图(存放主标题视图) */
30 | @property(nonatomic, strong)UIView *titleBottomView;
31 | /** 标题视图的高度 */
32 | @property(nonatomic, assign)CGFloat titleHeight;
33 | /** 当前的index */
34 | @property(nonatomic, assign)NSInteger currentIndex;
35 | /** 标题内容的数组 */
36 | @property(nonatomic, strong)NSArray *titleLableArray;
37 | /** 内容视图 */
38 | @property(nonatomic, strong)UICollectionView *contentCollectionView;
39 | /** 头部视图 */
40 | @property(nonatomic, strong)UIView *headView;
41 | /** 头部视图的高度 */
42 | @property(nonatomic, assign)CGFloat headViewHeight;
43 | /** CollectionView ContentSize */
44 | @property (nonatomic, assign)CGSize collectionViewCellSize;
45 | @end
46 |
47 | @implementation LCPageView
48 | {
49 | /** 内容滚动的代理 */
50 | NSPointerArray* _delegates;
51 | }
52 |
53 | - (void)dealloc
54 | {
55 | for (UIViewController *vc in self.childControllers) {
56 | vc.lcScrollView.delegate = nil;
57 | }
58 | // NSLog(@"LCPageView销毁");
59 | [self removeAllDelegates];
60 | }
61 | #pragma mark - 接口
62 | /**
63 | 更新pageView的高度
64 | */
65 | - (void)updatePageViewHeight:(CGFloat)height
66 | {
67 | CGRect frame = CGRectMake(0, 0, self.lc_width, height);
68 | [self updatePageViewFrame:frame];
69 | }
70 | /**
71 | 更新pageView的Frame
72 | */
73 | - (void)updatePageViewFrame:(CGRect)frame
74 | {
75 | self.frame = frame;
76 | self.mainScrollView.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
77 | CGFloat Y = self.titleBottomView.lc_bottom;
78 | if ([self titleViewHeight] == 0) {
79 | Y = self.headView.lc_bottom;
80 | }
81 | self.collectionViewCellSize = CGSizeMake(frame.size.width, frame.size.height - [self titleViewHeight]);
82 | self.contentCollectionView.frame = CGRectMake(0, Y, self.collectionViewCellSize.width, self.collectionViewCellSize.height);
83 | [self.contentCollectionView reloadData];
84 | }
85 |
86 | - (void)moveToIndex:(NSInteger)index
87 | {
88 | [self.titleView scrollCollectionviewWithIndex:index];
89 | [self scrollViewDidEndDecelerating:self.contentCollectionView];
90 | }
91 | /**
92 | 更新标题内容
93 | */
94 | - (void)updatePageViewTitles:(NSArray *)titles
95 | {
96 | if (!titles) {
97 | return;
98 | }
99 | [self.titleView updateTitles:titles];
100 | }
101 | #pragma mark - 初始化操作
102 | - (instancetype)initWithFrame:(CGRect)frame
103 | headView:(UIView *)headView
104 | childControllers:(NSArray *)childControllers
105 | parentController:(UIViewController *)parentController
106 | customTitleView:(LCTitleView *)customTitleView
107 | pageViewStyle:(id )pageViewStyle
108 | {
109 | self = [super initWithFrame:frame];
110 | if (self) {
111 | self.backgroundColor = [UIColor clearColor];
112 | _delegates = [NSPointerArray weakObjectsPointerArray];
113 | self.headView = headView;
114 | self.childControllers = childControllers;
115 | self.parentController = parentController;
116 | self.style = pageViewStyle;
117 | self.collectionViewCellSize = CGSizeMake(self.bounds.size.width, self.bounds.size.height - [self titleViewHeight]);
118 | [self initHeadView];
119 | [self initTitleViewWithCustomTitleView:customTitleView];
120 | [self initContentView];
121 |
122 | }
123 | return self;
124 | }
125 | - (void)initHeadView
126 | {
127 | if (!self.headView || self.headView.bounds.size.height == 0) {
128 | // 没有头部视图
129 | self.mainScrollView.bounces = NO;
130 | return;
131 | }
132 | self.mainScrollView.bounces = YES;
133 | self.headViewHeight = self.headView.frame.size.height;
134 | [self.mainScrollView addSubview:self.headView];
135 | self.mainScrollView.contentSize = CGSizeMake(0, self.headView.bounds.size.height);
136 | }
137 | /// 初始化标题
138 | - (void)initTitleViewWithCustomTitleView:(LCTitleView *)customTitleView
139 | {
140 | if (self.childControllers.count <= 0) {
141 | /// 没有子控制器, 抛出异常
142 | return;
143 | }
144 | if (customTitleView) {
145 | if (self.titleViewHeight > 0) {
146 | self.titleView = customTitleView;
147 | [self.titleBottomView addSubview:customTitleView];
148 | }
149 | }else {
150 | NSMutableArray *array = [NSMutableArray array];
151 | for (UIViewController *vc in self.childControllers) {
152 | NSString *title = vc.title ? : vc.navigationItem.title;
153 | [array addObject:title ? : @" "];
154 | }
155 | self.titleView = [[LCTitleView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.titleViewHeight) titles:array pageViewStyle:self.style];
156 | [self.titleBottomView addSubview:self.titleView];
157 | }
158 | }
159 |
160 | /// 初始化内容
161 | - (void)initContentView
162 | {
163 | if (self.parentController == nil) {
164 | /// 没有父控制器, 抛出异常
165 | return;
166 | }
167 | if (self.childControllers.count == 0) {
168 | /// 没有子控制器
169 | return;
170 | }
171 | for (UIViewController *vc in self.childControllers) {
172 | [self.parentController addChildViewController:vc];
173 | __weak typeof(self) weakSelf = self;
174 | /// 确保vc的viewDidload方法执行以后, lcScrollView 才会有值
175 | if (vc.view) {}
176 | vc.automaticallyAdjustsScrollViewInsets = NO;
177 | /// 确保子控制器第一次加载的时候执行viewWillAppear:方法
178 | [vc beginAppearanceTransition:YES animated:YES];
179 | /// 外面scrollview的滚动方法
180 | vc.lcScrollView.scrollHandle = ^(UIScrollView *scrollView) {
181 | weakSelf.outsideScrollView = scrollView;
182 | if (weakSelf.mainScrollView.contentOffset.y == 0) {
183 | if (weakSelf.headViewHeight == 0) {
184 | scrollView.showsVerticalScrollIndicator = YES;
185 | }else {
186 | scrollView.showsVerticalScrollIndicator = NO;
187 | }
188 | }else if (weakSelf.mainScrollView.contentOffset.y < weakSelf.headViewHeight) {
189 | scrollView.contentOffset = CGPointZero;
190 | scrollView.showsVerticalScrollIndicator = NO;
191 | }else {
192 | scrollView.showsVerticalScrollIndicator = YES;
193 | }
194 | [weakSelf limitMainScrollViewContentSize:scrollView];
195 | };
196 | }
197 | [self.mainScrollView insertSubview:self.contentCollectionView belowSubview:self.titleBottomView];
198 | }
199 |
200 |
201 |
202 | #pragma mark - 事件逻辑
203 | /// 滚动(多次调用)
204 | - (void)scrollCollectionviewWithIndex:(NSInteger)index
205 | {
206 | NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];
207 | [self.contentCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
208 | }
209 |
210 | #pragma mark - titleView 代理方法
211 | - (void)scrollCollectionviewWithIndexNum:(NSNumber *)index
212 | {
213 | [self scrollCollectionviewWithIndex:[index integerValue]];
214 | UIViewController *vc = self.childControllers[index.integerValue];
215 | [self limitMainScrollViewContentSize:vc.lcScrollView];
216 | /// 里面的ScrollView滚到最前面
217 | for (UIViewController *vc in self.childControllers) {
218 | vc.lcScrollView.contentOffset = CGPointZero;
219 | }
220 | /// 代理传到外面
221 | if ([self.delegate respondsToSelector:@selector(pageView:moveToIndex:)]) {
222 | [self.delegate pageView:self moveToIndex:index.integerValue];
223 | }
224 | }
225 | #pragma mark - collectionview delegate datasource
226 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
227 | {
228 | return self.collectionViewCellSize;
229 | }
230 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
231 | {
232 | return self.childControllers.count;
233 | }
234 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
235 | {
236 | UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kPageViewCollectionViewCellID forIndexPath:indexPath];
237 | for (UIView *subV in cell.contentView.subviews) {
238 | [subV removeFromSuperview];
239 | }
240 | UIViewController *vc = self.childControllers[indexPath.item];
241 | vc.view.frame = cell.contentView.bounds;/// 这里因为层级深, frame布局不够准确
242 | [cell.contentView addSubview:vc.view];
243 | // [vc.view mas_makeConstraints:^(MASConstraintMaker *make) {
244 | // make.edges.equalTo(cell.contentView);
245 | // }];
246 | return cell;
247 | }
248 | - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
249 | {
250 | for (UIViewController *vc in self.childControllers) {
251 | [vc.view endEditing:YES];
252 | }
253 | }
254 | #pragma mark - UIScrollViewDelegate
255 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
256 | {
257 | [self scrollCollectionviewWithIndex:[self getCurrentSelectIndex]];
258 | if ([scrollView isKindOfClass:[self.contentCollectionView class]]) {
259 | [self performDelegateWithSel:@selector(lc_scrollViewDidEndDecelerating:) withObject:(UICollectionView *)scrollView];
260 | }
261 | }
262 | - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
263 | {
264 | [self scrollCollectionviewWithIndex:[self getCurrentSelectIndex]];
265 |
266 | if ([scrollView isKindOfClass:[self.contentCollectionView class]]) {
267 | [self performDelegateWithSel:@selector(lc_scrollViewDidEndScrollingAnimation:) withObject:(UICollectionView *)scrollView];
268 | }
269 | }
270 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
271 | {
272 | if ([scrollView isKindOfClass:[self.contentCollectionView class]]) {
273 | SuppressPerformSelectorLeakWarning([self performDelegateWithSel:@selector(lc_scrollViewDidEndDragging:) withObject1:(UICollectionView *)scrollView withobject2:@(decelerate)]);
274 | }
275 | }
276 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
277 | {
278 | if ([scrollView isKindOfClass:[self.contentCollectionView class]]) {
279 | [self performDelegateWithSel:@selector(lc_scrollViewWillBeginDragging:) withObject:scrollView];
280 | }
281 | }
282 |
283 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView
284 | {
285 | /// 限制主滚动视图滚动范围
286 | [self limitMainScrollViewContentOffset:scrollView];
287 |
288 | /// 代理
289 | if ([scrollView isKindOfClass:[self.contentCollectionView class]]) {
290 | [self performDelegateWithSel:@selector(lc_scrollViewDidHorizontalScroll:) withObject:scrollView];
291 | }else {
292 | [self performDelegateWithSel:@selector(lc_scrollViewDidVerticalScroll:) withObject:scrollView];
293 | }
294 | }
295 | #pragma mark - private
296 | /// 限制主scrollView的ContentOffset
297 | - (void)limitMainScrollViewContentOffset:(UIScrollView *)scrollView
298 | {
299 | /// 限制主滚动视图滚动范围
300 | UIViewController *vc = self.childControllers[[self getCurrentSelectIndex]];
301 | if ([self.outsideScrollView isEqual:vc.lcScrollView]) {
302 | if (self.outsideScrollView.contentOffset.y > 50 || scrollView.contentOffset.y > self.headViewHeight) {
303 | self.mainScrollView.contentOffset = CGPointMake(0, self.headViewHeight);
304 | }
305 | }else {
306 | if (scrollView.contentOffset.y > 50 || scrollView.contentOffset.y > self.headViewHeight) {
307 | self.mainScrollView.contentOffset = CGPointMake(0, self.headViewHeight);
308 | }
309 | }
310 | }
311 | ///限制主滚动视图的contentSize
312 | - (void)limitMainScrollViewContentSize:(UIScrollView *)scrollView
313 | {
314 | if (scrollView.contentSize.height < (self.lc_height - self.headViewHeight - self.titleViewHeight)) {
315 | self.mainScrollView.contentSize = CGSizeMake(0, scrollView.contentSize.height + self.headViewHeight + self.titleViewHeight);
316 | [self.mainScrollView setContentOffset:CGPointMake(0, 0) animated:YES];
317 | }else {
318 | self.mainScrollView.contentSize = CGSizeMake(self.lc_width, scrollView.contentSize.height + self.headView.lc_height + self.titleViewHeight);
319 | }
320 | }
321 | /// 获取当前显示的控制器索引
322 | - (NSInteger)getCurrentSelectIndex
323 | {
324 | NSInteger index = (NSInteger)(self.contentCollectionView.contentOffset.x / self.contentCollectionView.bounds.size.width);
325 | return index;
326 | }
327 | /// 获取标题的高度
328 | - (CGFloat)titleViewHeight
329 | {
330 | CGFloat height = self.style.titleViewHeight;
331 | if (self.childControllers.count == 1) {
332 | if (self.style.titleHiddenForOneController) {
333 | height = 0;
334 | }
335 | }
336 | return height;
337 | }
338 | #pragma mark - 懒加载
339 | - (UIScrollView *)mainScrollView
340 | {
341 | if (!_mainScrollView) {
342 | _mainScrollView = [[MainScrollView alloc] initWithFrame:self.bounds];
343 | _mainScrollView.bounces = NO;
344 | _mainScrollView.delegate = self;
345 | _mainScrollView.scrollsToTop = NO;
346 | _mainScrollView.showsVerticalScrollIndicator = NO;
347 | if (@available(iOS 11.0, *)) {
348 | _mainScrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
349 | } else {
350 | // Fallback on earlier versions
351 | }
352 | _mainScrollView.backgroundColor = [UIColor clearColor];
353 | [self addSubview:_mainScrollView];
354 | }
355 | return _mainScrollView;
356 | }
357 | - (UIView *)titleBottomView
358 | {
359 | if (!_titleBottomView) {
360 | _titleBottomView = [[UIView alloc] initWithFrame:CGRectMake(0, self.headView.bounds.size.height, self.bounds.size.width, self.titleViewHeight)];
361 | [self.mainScrollView addSubview:_titleBottomView];
362 | _titleBottomView.backgroundColor = self.style.titleViewBgColor;
363 | }
364 | return _titleBottomView;
365 | }
366 |
367 | - (UICollectionView *)contentCollectionView
368 | {
369 | if (!_contentCollectionView) {
370 | UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
371 | CGSize size = CGSizeMake(self.bounds.size.width, self.bounds.size.height - self.titleViewHeight);
372 | // layout.itemSize = size;
373 | layout.estimatedItemSize = CGSizeMake(0, 0);
374 | layout.minimumLineSpacing = 0;
375 | layout.minimumInteritemSpacing = 0;
376 | layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
377 | CGRect frame = CGRectMake(0, CGRectGetMaxY(self.titleBottomView.frame), size.width, size.height);
378 | _contentCollectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
379 | [_contentCollectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kPageViewCollectionViewCellID];
380 | _contentCollectionView.delegate = self;
381 | _contentCollectionView.dataSource = self;
382 | _contentCollectionView.pagingEnabled = YES;
383 | _contentCollectionView.bounces = NO;
384 | _contentCollectionView.scrollEnabled = self.style.contentViewScrollEnabled;
385 | _contentCollectionView.showsHorizontalScrollIndicator = NO;
386 | _contentCollectionView.backgroundColor = [UIColor clearColor];
387 | if (@available(iOS 11.0, *)) {
388 | _contentCollectionView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
389 | } else {
390 | // Fallback on earlier versions
391 | }
392 | }
393 | return _contentCollectionView;
394 | }
395 |
396 |
397 | #pragma mark - LCMultiDelegate
398 | - (void)addDelegate:(id)delegate {
399 | [_delegates addPointer:(__bridge void*)delegate];
400 | }
401 | - (void)removeDelegate:(id)delegate {
402 | NSUInteger index = [self indexOfDelegate:delegate];
403 | if (index != NSNotFound)
404 | [_delegates removePointerAtIndex:index];
405 | [_delegates compact];
406 | }
407 | - (void)removeAllDelegates {
408 | for (NSUInteger i = _delegates.count; i > 0; i -= 1)
409 | [_delegates removePointerAtIndex:i - 1];
410 | }
411 | - (void)performDelegateWithSel:(SEL)sel withObject:(id)object
412 | {
413 | for (NSUInteger i = 0; i < _delegates.count; i += 1) {
414 | id delegate = [_delegates pointerAtIndex:i];
415 | if ([delegate respondsToSelector:sel]) {
416 | SuppressPerformSelectorLeakWarning1([delegate performSelector:sel withObject:object]);
417 | }
418 | }
419 | }
420 | - (void)performDelegateWithSel:(SEL)sel withObject1:(id)object1 withobject2:(id)onject2
421 | {
422 | for (NSUInteger i = 0; i < _delegates.count; i += 1) {
423 | id delegate = [_delegates pointerAtIndex:i];
424 | if ([delegate respondsToSelector:sel]) {
425 | SuppressPerformSelectorLeakWarning1([delegate performSelector:sel withObject:object1 withObject:onject2]);
426 | }
427 | }
428 | }
429 | - (NSUInteger)indexOfDelegate:(id)delegate {
430 | for (NSUInteger i = 0; i < _delegates.count; i += 1) {
431 | if ([_delegates pointerAtIndex:i] == (__bridge void*)delegate) {
432 | return i;
433 | }
434 | }
435 | return NSNotFound;
436 | }
437 | - (BOOL)respondsToSelector:(SEL)selector {
438 | if ([super respondsToSelector:selector])
439 | return YES;
440 | for (id delegate in _delegates) {
441 | if (delegate && [delegate respondsToSelector:selector])
442 | return YES;
443 | }
444 | return NO;
445 | }
446 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
447 | NSMethodSignature* signature = [super methodSignatureForSelector:selector];
448 | if (signature)
449 | return signature;
450 |
451 | [_delegates compact];
452 | if (_delegates.count == 0) {
453 | return [self methodSignatureForSelector:@selector(description)];
454 | }
455 | for (id delegate in _delegates) {//存储了各个对象的代理
456 | if (!delegate)
457 | continue;
458 | signature = [delegate methodSignatureForSelector:selector];
459 | if (signature)
460 | break;
461 | }
462 | return signature;
463 | }
464 | - (void)forwardInvocation:(NSInvocation *)invocation {
465 | SEL selector = [invocation selector];
466 | BOOL responded = NO;
467 | for (id delegate in _delegates) {
468 | if (delegate && [delegate respondsToSelector:selector]) {
469 | [invocation invokeWithTarget:delegate];
470 | responded = YES;
471 | }
472 | }
473 | if (!responded)
474 | [self doesNotRecognizeSelector:selector];
475 | }
476 |
477 | @end
478 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C857065020ADAF9E00397593 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C857064F20ADAF9E00397593 /* AppDelegate.m */; };
11 | C857065620ADAF9E00397593 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C857065420ADAF9E00397593 /* Main.storyboard */; };
12 | C857065820ADAF9F00397593 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C857065720ADAF9F00397593 /* Assets.xcassets */; };
13 | C857065B20ADAF9F00397593 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C857065920ADAF9F00397593 /* LaunchScreen.storyboard */; };
14 | C857065E20ADAF9F00397593 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C857065D20ADAF9F00397593 /* main.m */; };
15 | C857068520ADB08A00397593 /* LCTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = C857067820ADB08900397593 /* LCTitleView.m */; };
16 | C857068620ADB08A00397593 /* LCPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = C857067920ADB08900397593 /* LCPageView.m */; };
17 | C857068720ADB08A00397593 /* UIViewController+Category.m in Sources */ = {isa = PBXBuildFile; fileRef = C857067A20ADB08900397593 /* UIViewController+Category.m */; };
18 | C857068820ADB08A00397593 /* LCPageViewStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = C857067B20ADB08900397593 /* LCPageViewStyle.m */; };
19 | C857068920ADB08A00397593 /* MainScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = C857067D20ADB08A00397593 /* MainScrollView.m */; };
20 | C857068A20ADB08A00397593 /* UIScrollView+Category.m in Sources */ = {isa = PBXBuildFile; fileRef = C857068120ADB08A00397593 /* UIScrollView+Category.m */; };
21 | C857069620ADB22400397593 /* TestViewController3.m in Sources */ = {isa = PBXBuildFile; fileRef = C857068E20ADB22400397593 /* TestViewController3.m */; };
22 | C857069720ADB22400397593 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C857068F20ADB22400397593 /* ViewController.m */; };
23 | C857069920ADB22400397593 /* TestViewController2.m in Sources */ = {isa = PBXBuildFile; fileRef = C857069120ADB22400397593 /* TestViewController2.m */; };
24 | C857069A20ADB22400397593 /* TestTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = C857069220ADB22400397593 /* TestTitleView.m */; };
25 | C878B4E422D482100027E862 /* UIView+LCFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = C878B4E222D4820F0027E862 /* UIView+LCFrame.m */; };
26 | /* End PBXBuildFile section */
27 |
28 | /* Begin PBXFileReference section */
29 | C857064B20ADAF9E00397593 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
30 | C857064E20ADAF9E00397593 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
31 | C857064F20ADAF9E00397593 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
32 | C857065520ADAF9E00397593 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
33 | C857065720ADAF9F00397593 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
34 | C857065A20ADAF9F00397593 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
35 | C857065C20ADAF9F00397593 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
36 | C857065D20ADAF9F00397593 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
37 | C857067820ADB08900397593 /* LCTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCTitleView.m; sourceTree = ""; };
38 | C857067920ADB08900397593 /* LCPageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCPageView.m; sourceTree = ""; };
39 | C857067A20ADB08900397593 /* UIViewController+Category.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Category.m"; sourceTree = ""; };
40 | C857067B20ADB08900397593 /* LCPageViewStyle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCPageViewStyle.m; sourceTree = ""; };
41 | C857067C20ADB08900397593 /* LCTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCTitleView.h; sourceTree = ""; };
42 | C857067D20ADB08A00397593 /* MainScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainScrollView.m; sourceTree = ""; };
43 | C857067F20ADB08A00397593 /* LCPageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCPageView.h; sourceTree = ""; };
44 | C857068020ADB08A00397593 /* UIScrollView+Category.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScrollView+Category.h"; sourceTree = ""; };
45 | C857068120ADB08A00397593 /* UIScrollView+Category.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScrollView+Category.m"; sourceTree = ""; };
46 | C857068220ADB08A00397593 /* UIViewController+Category.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Category.h"; sourceTree = ""; };
47 | C857068320ADB08A00397593 /* LCPageViewStyle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCPageViewStyle.h; sourceTree = ""; };
48 | C857068420ADB08A00397593 /* MainScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainScrollView.h; sourceTree = ""; };
49 | C857068D20ADB22400397593 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
50 | C857068E20ADB22400397593 /* TestViewController3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestViewController3.m; sourceTree = ""; };
51 | C857068F20ADB22400397593 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
52 | C857069120ADB22400397593 /* TestViewController2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestViewController2.m; sourceTree = ""; };
53 | C857069220ADB22400397593 /* TestTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestTitleView.m; sourceTree = ""; };
54 | C857069320ADB22400397593 /* TestTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestTitleView.h; sourceTree = ""; };
55 | C857069420ADB22400397593 /* TestViewController2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestViewController2.h; sourceTree = ""; };
56 | C857069520ADB22400397593 /* TestViewController3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestViewController3.h; sourceTree = ""; };
57 | C878B4E122D481850027E862 /* LCPageViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LCPageViewProtocol.h; sourceTree = ""; };
58 | C878B4E222D4820F0027E862 /* UIView+LCFrame.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+LCFrame.m"; sourceTree = ""; };
59 | C878B4E322D4820F0027E862 /* UIView+LCFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+LCFrame.h"; sourceTree = ""; };
60 | /* End PBXFileReference section */
61 |
62 | /* Begin PBXFrameworksBuildPhase section */
63 | C857064820ADAF9E00397593 /* Frameworks */ = {
64 | isa = PBXFrameworksBuildPhase;
65 | buildActionMask = 2147483647;
66 | files = (
67 | );
68 | runOnlyForDeploymentPostprocessing = 0;
69 | };
70 | /* End PBXFrameworksBuildPhase section */
71 |
72 | /* Begin PBXGroup section */
73 | C857064220ADAF9E00397593 = {
74 | isa = PBXGroup;
75 | children = (
76 | C857064D20ADAF9E00397593 /* Example */,
77 | C857064C20ADAF9E00397593 /* Products */,
78 | );
79 | sourceTree = "";
80 | };
81 | C857064C20ADAF9E00397593 /* Products */ = {
82 | isa = PBXGroup;
83 | children = (
84 | C857064B20ADAF9E00397593 /* Example.app */,
85 | );
86 | name = Products;
87 | sourceTree = "";
88 | };
89 | C857064D20ADAF9E00397593 /* Example */ = {
90 | isa = PBXGroup;
91 | children = (
92 | C857068B20ADB19800397593 /* Demo */,
93 | C857066420ADB04500397593 /* Source */,
94 | C857064E20ADAF9E00397593 /* AppDelegate.h */,
95 | C857064F20ADAF9E00397593 /* AppDelegate.m */,
96 | C857065420ADAF9E00397593 /* Main.storyboard */,
97 | C857065720ADAF9F00397593 /* Assets.xcassets */,
98 | C857065920ADAF9F00397593 /* LaunchScreen.storyboard */,
99 | C857065C20ADAF9F00397593 /* Info.plist */,
100 | C857065D20ADAF9F00397593 /* main.m */,
101 | );
102 | path = Example;
103 | sourceTree = "";
104 | };
105 | C857066420ADB04500397593 /* Source */ = {
106 | isa = PBXGroup;
107 | children = (
108 | C857067F20ADB08A00397593 /* LCPageView.h */,
109 | C857067920ADB08900397593 /* LCPageView.m */,
110 | C857068320ADB08A00397593 /* LCPageViewStyle.h */,
111 | C857067B20ADB08900397593 /* LCPageViewStyle.m */,
112 | C857067C20ADB08900397593 /* LCTitleView.h */,
113 | C857067820ADB08900397593 /* LCTitleView.m */,
114 | C857068420ADB08A00397593 /* MainScrollView.h */,
115 | C857067D20ADB08A00397593 /* MainScrollView.m */,
116 | C878B4E322D4820F0027E862 /* UIView+LCFrame.h */,
117 | C878B4E222D4820F0027E862 /* UIView+LCFrame.m */,
118 | C878B4E122D481850027E862 /* LCPageViewProtocol.h */,
119 | C857068020ADB08A00397593 /* UIScrollView+Category.h */,
120 | C857068120ADB08A00397593 /* UIScrollView+Category.m */,
121 | C857068220ADB08A00397593 /* UIViewController+Category.h */,
122 | C857067A20ADB08900397593 /* UIViewController+Category.m */,
123 | );
124 | name = Source;
125 | path = ../../Source;
126 | sourceTree = "";
127 | };
128 | C857068B20ADB19800397593 /* Demo */ = {
129 | isa = PBXGroup;
130 | children = (
131 | C857069320ADB22400397593 /* TestTitleView.h */,
132 | C857069220ADB22400397593 /* TestTitleView.m */,
133 | C857069420ADB22400397593 /* TestViewController2.h */,
134 | C857069120ADB22400397593 /* TestViewController2.m */,
135 | C857069520ADB22400397593 /* TestViewController3.h */,
136 | C857068E20ADB22400397593 /* TestViewController3.m */,
137 | C857068D20ADB22400397593 /* ViewController.h */,
138 | C857068F20ADB22400397593 /* ViewController.m */,
139 | );
140 | path = Demo;
141 | sourceTree = SOURCE_ROOT;
142 | };
143 | /* End PBXGroup section */
144 |
145 | /* Begin PBXNativeTarget section */
146 | C857064A20ADAF9E00397593 /* Example */ = {
147 | isa = PBXNativeTarget;
148 | buildConfigurationList = C857066120ADAF9F00397593 /* Build configuration list for PBXNativeTarget "Example" */;
149 | buildPhases = (
150 | C857064720ADAF9E00397593 /* Sources */,
151 | C857064820ADAF9E00397593 /* Frameworks */,
152 | C857064920ADAF9E00397593 /* Resources */,
153 | );
154 | buildRules = (
155 | );
156 | dependencies = (
157 | );
158 | name = Example;
159 | productName = Example;
160 | productReference = C857064B20ADAF9E00397593 /* Example.app */;
161 | productType = "com.apple.product-type.application";
162 | };
163 | /* End PBXNativeTarget section */
164 |
165 | /* Begin PBXProject section */
166 | C857064320ADAF9E00397593 /* Project object */ = {
167 | isa = PBXProject;
168 | attributes = {
169 | LastUpgradeCheck = 0930;
170 | ORGANIZATIONNAME = linechan;
171 | TargetAttributes = {
172 | C857064A20ADAF9E00397593 = {
173 | CreatedOnToolsVersion = 9.3;
174 | };
175 | };
176 | };
177 | buildConfigurationList = C857064620ADAF9E00397593 /* Build configuration list for PBXProject "Example" */;
178 | compatibilityVersion = "Xcode 9.3";
179 | developmentRegion = en;
180 | hasScannedForEncodings = 0;
181 | knownRegions = (
182 | en,
183 | Base,
184 | );
185 | mainGroup = C857064220ADAF9E00397593;
186 | productRefGroup = C857064C20ADAF9E00397593 /* Products */;
187 | projectDirPath = "";
188 | projectRoot = "";
189 | targets = (
190 | C857064A20ADAF9E00397593 /* Example */,
191 | );
192 | };
193 | /* End PBXProject section */
194 |
195 | /* Begin PBXResourcesBuildPhase section */
196 | C857064920ADAF9E00397593 /* Resources */ = {
197 | isa = PBXResourcesBuildPhase;
198 | buildActionMask = 2147483647;
199 | files = (
200 | C857065B20ADAF9F00397593 /* LaunchScreen.storyboard in Resources */,
201 | C857065820ADAF9F00397593 /* Assets.xcassets in Resources */,
202 | C857065620ADAF9E00397593 /* Main.storyboard in Resources */,
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | };
206 | /* End PBXResourcesBuildPhase section */
207 |
208 | /* Begin PBXSourcesBuildPhase section */
209 | C857064720ADAF9E00397593 /* Sources */ = {
210 | isa = PBXSourcesBuildPhase;
211 | buildActionMask = 2147483647;
212 | files = (
213 | C857069A20ADB22400397593 /* TestTitleView.m in Sources */,
214 | C857068520ADB08A00397593 /* LCTitleView.m in Sources */,
215 | C857069920ADB22400397593 /* TestViewController2.m in Sources */,
216 | C857068A20ADB08A00397593 /* UIScrollView+Category.m in Sources */,
217 | C857068920ADB08A00397593 /* MainScrollView.m in Sources */,
218 | C857065E20ADAF9F00397593 /* main.m in Sources */,
219 | C857069720ADB22400397593 /* ViewController.m in Sources */,
220 | C857069620ADB22400397593 /* TestViewController3.m in Sources */,
221 | C857068820ADB08A00397593 /* LCPageViewStyle.m in Sources */,
222 | C857068620ADB08A00397593 /* LCPageView.m in Sources */,
223 | C857068720ADB08A00397593 /* UIViewController+Category.m in Sources */,
224 | C878B4E422D482100027E862 /* UIView+LCFrame.m in Sources */,
225 | C857065020ADAF9E00397593 /* AppDelegate.m in Sources */,
226 | );
227 | runOnlyForDeploymentPostprocessing = 0;
228 | };
229 | /* End PBXSourcesBuildPhase section */
230 |
231 | /* Begin PBXVariantGroup section */
232 | C857065420ADAF9E00397593 /* Main.storyboard */ = {
233 | isa = PBXVariantGroup;
234 | children = (
235 | C857065520ADAF9E00397593 /* Base */,
236 | );
237 | name = Main.storyboard;
238 | sourceTree = "";
239 | };
240 | C857065920ADAF9F00397593 /* LaunchScreen.storyboard */ = {
241 | isa = PBXVariantGroup;
242 | children = (
243 | C857065A20ADAF9F00397593 /* Base */,
244 | );
245 | name = LaunchScreen.storyboard;
246 | sourceTree = "";
247 | };
248 | /* End PBXVariantGroup section */
249 |
250 | /* Begin XCBuildConfiguration section */
251 | C857065F20ADAF9F00397593 /* Debug */ = {
252 | isa = XCBuildConfiguration;
253 | buildSettings = {
254 | ALWAYS_SEARCH_USER_PATHS = NO;
255 | CLANG_ANALYZER_NONNULL = YES;
256 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
257 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
258 | CLANG_CXX_LIBRARY = "libc++";
259 | CLANG_ENABLE_MODULES = YES;
260 | CLANG_ENABLE_OBJC_ARC = YES;
261 | CLANG_ENABLE_OBJC_WEAK = YES;
262 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
263 | CLANG_WARN_BOOL_CONVERSION = YES;
264 | CLANG_WARN_COMMA = YES;
265 | CLANG_WARN_CONSTANT_CONVERSION = YES;
266 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
267 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
268 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
269 | CLANG_WARN_EMPTY_BODY = YES;
270 | CLANG_WARN_ENUM_CONVERSION = YES;
271 | CLANG_WARN_INFINITE_RECURSION = YES;
272 | CLANG_WARN_INT_CONVERSION = YES;
273 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
274 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
275 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
277 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
278 | CLANG_WARN_STRICT_PROTOTYPES = YES;
279 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
280 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
281 | CLANG_WARN_UNREACHABLE_CODE = YES;
282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
283 | CODE_SIGN_IDENTITY = "iPhone Developer";
284 | COPY_PHASE_STRIP = NO;
285 | DEBUG_INFORMATION_FORMAT = dwarf;
286 | ENABLE_STRICT_OBJC_MSGSEND = YES;
287 | ENABLE_TESTABILITY = YES;
288 | GCC_C_LANGUAGE_STANDARD = gnu11;
289 | GCC_DYNAMIC_NO_PIC = NO;
290 | GCC_NO_COMMON_BLOCKS = YES;
291 | GCC_OPTIMIZATION_LEVEL = 0;
292 | GCC_PREPROCESSOR_DEFINITIONS = (
293 | "DEBUG=1",
294 | "$(inherited)",
295 | );
296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
298 | GCC_WARN_UNDECLARED_SELECTOR = YES;
299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
300 | GCC_WARN_UNUSED_FUNCTION = YES;
301 | GCC_WARN_UNUSED_VARIABLE = YES;
302 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
303 | MTL_ENABLE_DEBUG_INFO = YES;
304 | ONLY_ACTIVE_ARCH = YES;
305 | SDKROOT = iphoneos;
306 | };
307 | name = Debug;
308 | };
309 | C857066020ADAF9F00397593 /* Release */ = {
310 | isa = XCBuildConfiguration;
311 | buildSettings = {
312 | ALWAYS_SEARCH_USER_PATHS = NO;
313 | CLANG_ANALYZER_NONNULL = YES;
314 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
315 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
316 | CLANG_CXX_LIBRARY = "libc++";
317 | CLANG_ENABLE_MODULES = YES;
318 | CLANG_ENABLE_OBJC_ARC = YES;
319 | CLANG_ENABLE_OBJC_WEAK = YES;
320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
321 | CLANG_WARN_BOOL_CONVERSION = YES;
322 | CLANG_WARN_COMMA = YES;
323 | CLANG_WARN_CONSTANT_CONVERSION = YES;
324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
326 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
327 | CLANG_WARN_EMPTY_BODY = YES;
328 | CLANG_WARN_ENUM_CONVERSION = YES;
329 | CLANG_WARN_INFINITE_RECURSION = YES;
330 | CLANG_WARN_INT_CONVERSION = YES;
331 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
332 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
333 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
335 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
336 | CLANG_WARN_STRICT_PROTOTYPES = YES;
337 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
338 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
339 | CLANG_WARN_UNREACHABLE_CODE = YES;
340 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
341 | CODE_SIGN_IDENTITY = "iPhone Developer";
342 | COPY_PHASE_STRIP = NO;
343 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
344 | ENABLE_NS_ASSERTIONS = NO;
345 | ENABLE_STRICT_OBJC_MSGSEND = YES;
346 | GCC_C_LANGUAGE_STANDARD = gnu11;
347 | GCC_NO_COMMON_BLOCKS = YES;
348 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
349 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
350 | GCC_WARN_UNDECLARED_SELECTOR = YES;
351 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
352 | GCC_WARN_UNUSED_FUNCTION = YES;
353 | GCC_WARN_UNUSED_VARIABLE = YES;
354 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
355 | MTL_ENABLE_DEBUG_INFO = NO;
356 | SDKROOT = iphoneos;
357 | VALIDATE_PRODUCT = YES;
358 | };
359 | name = Release;
360 | };
361 | C857066220ADAF9F00397593 /* Debug */ = {
362 | isa = XCBuildConfiguration;
363 | buildSettings = {
364 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
365 | CODE_SIGN_STYLE = Automatic;
366 | DEVELOPMENT_TEAM = YKJ9G6R72M;
367 | INFOPLIST_FILE = Example/Info.plist;
368 | LD_RUNPATH_SEARCH_PATHS = (
369 | "$(inherited)",
370 | "@executable_path/Frameworks",
371 | );
372 | PRODUCT_BUNDLE_IDENTIFIER = com.lianchen.com.Example;
373 | PRODUCT_NAME = "$(TARGET_NAME)";
374 | TARGETED_DEVICE_FAMILY = "1,2";
375 | };
376 | name = Debug;
377 | };
378 | C857066320ADAF9F00397593 /* Release */ = {
379 | isa = XCBuildConfiguration;
380 | buildSettings = {
381 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
382 | CODE_SIGN_STYLE = Automatic;
383 | DEVELOPMENT_TEAM = YKJ9G6R72M;
384 | INFOPLIST_FILE = Example/Info.plist;
385 | LD_RUNPATH_SEARCH_PATHS = (
386 | "$(inherited)",
387 | "@executable_path/Frameworks",
388 | );
389 | PRODUCT_BUNDLE_IDENTIFIER = com.lianchen.com.Example;
390 | PRODUCT_NAME = "$(TARGET_NAME)";
391 | TARGETED_DEVICE_FAMILY = "1,2";
392 | };
393 | name = Release;
394 | };
395 | /* End XCBuildConfiguration section */
396 |
397 | /* Begin XCConfigurationList section */
398 | C857064620ADAF9E00397593 /* Build configuration list for PBXProject "Example" */ = {
399 | isa = XCConfigurationList;
400 | buildConfigurations = (
401 | C857065F20ADAF9F00397593 /* Debug */,
402 | C857066020ADAF9F00397593 /* Release */,
403 | );
404 | defaultConfigurationIsVisible = 0;
405 | defaultConfigurationName = Release;
406 | };
407 | C857066120ADAF9F00397593 /* Build configuration list for PBXNativeTarget "Example" */ = {
408 | isa = XCConfigurationList;
409 | buildConfigurations = (
410 | C857066220ADAF9F00397593 /* Debug */,
411 | C857066320ADAF9F00397593 /* Release */,
412 | );
413 | defaultConfigurationIsVisible = 0;
414 | defaultConfigurationName = Release;
415 | };
416 | /* End XCConfigurationList section */
417 | };
418 | rootObject = C857064320ADAF9E00397593 /* Project object */;
419 | }
420 |
--------------------------------------------------------------------------------
/Source/LCTitleView.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCTitleView.m
3 | // LCPageView
4 | //
5 | // Created by lianchen on 2018/5/15.
6 | // Copyright © 2018年 lianchen. All rights reserved.
7 | //
8 |
9 | #import "LCTitleView.h"
10 | #import "LCPageView.h"
11 | #import "UIView+LCFrame.h"
12 |
13 | @interface LCTitleView ()
14 |
15 | /** 标题视图 */
16 | @property(nonatomic, strong)UIScrollView *titleScrollView;
17 | /** 标题视图的高度 */
18 | @property(nonatomic, assign)CGFloat titleHeight;
19 | /** 标题内容的数组 */
20 | @property(nonatomic, strong)NSArray *titleLableArray;
21 | /** 标题内容的数组 */
22 | @property(nonatomic, strong)NSMutableArray *titleLabels;
23 | /** 存放标题的宽度 */
24 | @property(nonatomic, strong)NSMutableArray *titleWidths;
25 | @property(nonatomic, assign)CGFloat totalWidth;
26 | @property(nonatomic, assign)CGFloat maxWidth;
27 | @property(nonatomic, assign)CGFloat titleMargin;
28 | @property(nonatomic, assign)NSInteger sourceIndex;
29 | @property(nonatomic, assign)NSInteger targetIndex;
30 | /** 当前的index */
31 | @property(nonatomic, assign)NSInteger oldLabelIndex;
32 | @property(nonatomic, assign)CGFloat startOffsetX;
33 | @property(nonatomic, assign)CGFloat didEndOffsetX;
34 | @property(nonatomic, assign)BOOL isWillBeginDragging;
35 | /** <#注释#> */
36 | @property(nonatomic, assign)CGFloat progress;
37 | /** <#注释#> */
38 | @property(nonatomic, assign)NSInteger page;
39 |
40 | #pragma mark - 底部指示条相关
41 | @property(nonatomic, weak)UIView *indicatorLine;
42 | @property(nonatomic, strong)UIColor *indicatorColor;
43 | @property(nonatomic, assign)CGFloat indicatorH;
44 |
45 |
46 | #pragma mark - 颜色相关
47 | @property(nonatomic, assign) CGFloat startR;
48 | @property(nonatomic, assign) CGFloat startG;
49 | @property(nonatomic, assign) CGFloat startB;
50 | @property(nonatomic, assign) CGFloat endR;
51 | @property(nonatomic, assign) CGFloat endG;
52 | @property(nonatomic, assign) CGFloat endB;
53 |
54 | #pragma mark - 效果
55 | @property(nonatomic, assign)BOOL isTitleZoom;
56 | @property(nonatomic, assign)BOOL isShowIndicatorLine;
57 | @property(nonatomic, assign)BOOL isShowNoticeView;
58 | @property(nonatomic, assign)BOOL isMillcolorGrad;
59 | @property(nonatomic, assign)BOOL isLineFollowMove;
60 |
61 | @end
62 |
63 | #define MinTitleMargin 20
64 | #define MinTitleCount 4
65 | @implementation LCTitleView
66 |
67 | - (instancetype)initWithFrame:(CGRect)frame
68 | titles:(NSArray *)titles
69 | pageViewStyle:(id )pageViewStyle
70 | {
71 | self = [super initWithFrame:frame];
72 | if (self) {
73 | _titles = titles;
74 | self.style = pageViewStyle;
75 | self.backgroundColor = pageViewStyle.titleViewBgColor;
76 |
77 |
78 | [self initConfig];
79 | [self calculateTitleWidths];
80 | [self initTitleView];
81 | }
82 | return self;
83 | }
84 | - (void)initSuperTitleView
85 | {
86 |
87 | }
88 | - (void)initConfig
89 | {
90 | [self setupStartColor:self.style.titleLabelNormalColor];
91 | [self setupEndColor:self.style.titleLabelSelectColor];
92 | }
93 | - (void)dealloc
94 | {
95 | // NSLog(@"LCTitleView释放");
96 | }
97 | - (void)didMoveToSuperview
98 | {
99 | [super didMoveToSuperview];
100 | UIView *superV = [self getLCPageViewWithCurrentView:self];
101 | if ([superV isKindOfClass:[LCPageView class]]) {
102 | SuppressPerformSelectorLeakWarning([superV performSelector:@selector(addDelegate:) withObject:self]);
103 | }
104 | }
105 | - (void)updateTitles:(NSArray *)titles
106 | {
107 | _titles = titles;
108 | [self calculateTitleWidths];
109 | [self updateTitlesFrame];
110 | }
111 | /// 初始化标题
112 | - (void)initTitleView
113 | {
114 | [self.titleLabels removeAllObjects];
115 | if (self.titles <= 0) {
116 | /// 没有子控制器, 抛出异常
117 | return;
118 | }
119 | CGFloat labX = 0;
120 | CGFloat labY = 0;
121 | CGFloat labW = 0;
122 | CGFloat bottomLineH = self.style.bottomLineEnable ? self.style.bottomLineHeight : 0;
123 | CGFloat labH = self.style.titleViewHeight - bottomLineH;
124 | [self addSubview:self.titleScrollView];
125 | for (int i = 0; i < _titles.count; i++) {
126 | UILabel *label = [[UILabel alloc] init];;
127 | label.tag = i;
128 | [label setText:_titles[i]];
129 | [label setTextAlignment:NSTextAlignmentCenter];
130 | [label setFont:[UIFont systemFontOfSize:self.style.titleLabelFont]];
131 | [label setTextColor:self.style.titleLabelNormalColor];
132 | [label setBackgroundColor:self.style.titleLabelNormalBgColor];
133 | label.userInteractionEnabled = YES;
134 | label.lineBreakMode = self.style.labelLineBreakMode;
135 | if (self.style.isAverageTitleWidth) {
136 | labW = _maxWidth;
137 | }else {
138 | labW = [self.titleWidths[i] integerValue] + _titleMargin * 2;
139 | }
140 | label.frame = CGRectMake(labX, labY, labW, labH);
141 | labX = labX + labW;
142 | [_titleLabels addObject:label];
143 | [_titleScrollView addSubview:label];
144 | if (i == 0) {
145 | if (self.style.bottomLineEnable) {
146 | self.indicatorLine.frame = CGRectMake(0, labH - bottomLineH, 0, bottomLineH);
147 | self.indicatorLine.lc_width = [self.titleWidths[i] integerValue];
148 | self.indicatorLine.lc_centerX = label.lc_centerX;
149 | }
150 | [self setLabelClickTitleState:label];
151 | }
152 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(titleLabelDidClick:)];
153 | [label addGestureRecognizer:tap];
154 |
155 | }
156 | if (self.style.bottomLineEnable) {
157 | [_titleScrollView bringSubviewToFront:_indicatorLine];
158 | }
159 | UILabel *lastLabel = self.titleLabels.lastObject;
160 | _titleScrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastLabel.frame), labH);
161 | _titleScrollView.scrollEnabled = self.style.titleScrollEnable;
162 | }
163 | - (void)updateTitlesFrame
164 | {
165 | if (self.titles <= 0) {
166 | /// 没有子控制器, 抛出异常
167 | return;
168 | }
169 | CGFloat labX = 0;
170 | CGFloat labY = 0;
171 | CGFloat labW = 0;
172 | CGFloat bottomLineH = self.style.bottomLineEnable ? self.style.bottomLineHeight : 0;
173 | CGFloat labH = self.style.titleViewHeight - bottomLineH;
174 | for (int i = 0; i < _titles.count; i++) {
175 | UILabel *label = self.titleLabels[i];
176 | [label setText:_titles[i]];
177 | if (self.style.isAverageTitleWidth) {
178 | labW = _maxWidth;
179 | }else {
180 | labW = [self.titleWidths[i] integerValue] + _titleMargin * 2;
181 | }
182 | label.frame = CGRectMake(labX, labY, labW, labH);
183 | labX = labX + labW;
184 | if (i == self.oldLabelIndex) {
185 | if (self.style.bottomLineEnable) {
186 | self.indicatorLine.lc_width = [self.titleWidths[i] integerValue];
187 | self.indicatorLine.lc_centerX = label.lc_centerX;
188 | }
189 | }
190 |
191 | }
192 | if (self.style.bottomLineEnable) {
193 | [_titleScrollView bringSubviewToFront:_indicatorLine];
194 | }
195 | UILabel *lastLabel = self.titleLabels.lastObject;
196 | _titleScrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastLabel.frame), labH);
197 | _titleScrollView.scrollEnabled = self.style.titleScrollEnable;
198 |
199 | }
200 | - (void)calculateTitleWidths
201 | {
202 | [self.titleWidths removeAllObjects];
203 | CGFloat totalWidth = 0;
204 | for (NSString *title in _titles) {
205 | CGFloat textWidth = [self calculateTitleSize:title withMaxSize:CGSizeMake(MAXFLOAT, 0) withTextFont:[UIFont systemFontOfSize:self.style.titleLabelFont]].width;
206 | if (textWidth > _maxWidth) {
207 | if (self.style.isAverageTitleWidth) {
208 | if (textWidth > self.lc_width / _titles.count) {
209 | textWidth = self.lc_width / _titles.count;
210 | }
211 | }
212 | _maxWidth = textWidth;
213 | }
214 | totalWidth += textWidth;
215 | [self.titleWidths addObject:@(textWidth + 4)];
216 | }
217 |
218 | if ([self.style isAverageTitleWidth]) {
219 | _totalWidth = _maxWidth * _titles.count;
220 | if (_totalWidth <= self.lc_width) {
221 | /// 如果总宽度小于屏幕的宽度, 就用平均宽度
222 | _maxWidth = self.lc_width / (CGFloat)_titles.count;
223 | }else {
224 | /// 如果大于, 就用label的宽度(18 是间距)
225 | _maxWidth = _maxWidth + 18;
226 | }
227 | }else {
228 | _totalWidth = totalWidth;
229 | CGFloat titleMargin = (self.lc_width - totalWidth) / (_titles.count + 1);
230 | CGFloat totalWidthScale = self.lc_width / kScreenW;
231 | if (totalWidth > self.lc_width) {
232 | _titleMargin = MinTitleMargin;
233 | }else{
234 | if (_titles.count <= MinTitleCount) {
235 | _titleMargin = (self.lc_width / _titles.count - _totalWidth * totalWidthScale / _titles.count) / 2;
236 | }else{
237 | _titleMargin = titleMargin > MinTitleMargin ? titleMargin : MinTitleMargin;
238 | }
239 | }
240 | }
241 | }
242 | -(CGSize)calculateTitleSize:(NSString *)title withMaxSize:(CGSize)maxSize withTextFont:(UIFont *)font{
243 | return [title boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil].size;
244 | }
245 | #pragma mark - 事件
246 | /// 点击label
247 | - (void)titleLabelDidClick:(UITapGestureRecognizer *)tap
248 | {
249 | _isWillBeginDragging = NO;
250 | if (!self.style.isTitleClick) {
251 | return;
252 | }
253 | UILabel *label = (UILabel *)tap.view;
254 | if (label.tag == self.oldLabelIndex) {
255 | return;
256 | }
257 | [self setLabelClickTitleState:label];
258 | self.oldLabelIndex = label.tag;
259 | self.sourceIndex = self.oldLabelIndex;
260 | self.targetIndex = self.oldLabelIndex;
261 |
262 | [self changeContentPage:@(label.tag)];
263 | }
264 |
265 | /**
266 | 点击设置标题的状态
267 | */
268 | - (void)setLabelClickTitleState:(UILabel *)label{
269 | [self setLabelTextColor:label];
270 | [self setLabelTextZoom:label];
271 | [self setLabelBottomLineScroll:label];
272 | [self setLabelAutoScroll:label];
273 | self.oldLabelIndex = label.tag;
274 | }
275 |
276 | - (void)setLabelTextColor:(UILabel *)lable
277 | {
278 | UILabel *oldLable = self.titleLabels[self.oldLabelIndex];
279 | oldLable.textColor = self.style.titleLabelNormalColor;
280 | lable.textColor = self.style.titleLabelSelectColor;
281 |
282 | oldLable.backgroundColor = self.style.titleLabelNormalBgColor;
283 | lable.backgroundColor = self.style.titleLabelSelectBgColor;
284 | }
285 | - (void)setLabelTextZoom:(UILabel *)lable
286 | {
287 | if (!self.style.titleZoomEnable) {
288 | return;
289 | }
290 | UILabel *oldLable = self.titleLabels[self.oldLabelIndex];
291 | oldLable.transform = CGAffineTransformMakeScale(1, 1);
292 | lable.transform = CGAffineTransformMakeScale(1 + self.style.titleZoomMaxScale, 1 + self.style.titleZoomMaxScale);
293 |
294 | }
295 | - (void)setLabelAutoScroll:(UILabel *)lable
296 | {
297 | CGFloat offsetX = lable.center.x - kScreenW * 0.5;
298 | if (offsetX < 0) {
299 | offsetX = 0;
300 | }
301 | CGFloat maxOffsetX = self.titleScrollView.contentSize.width - kScreenW;
302 | if (offsetX > maxOffsetX) {
303 | offsetX = maxOffsetX;
304 | }
305 | [self.titleScrollView setContentOffset:CGPointMake(offsetX, 0) animated:YES];
306 | }
307 |
308 | - (void)setLabelBottomLineScroll:(UILabel *)lable
309 | {
310 |
311 | [UIView animateWithDuration:0.25 animations:^{
312 | self.indicatorLine.lc_width = [self.titleWidths[lable.tag] doubleValue];
313 | self.indicatorLine.lc_centerX = lable.lc_centerX;
314 | }];
315 | }
316 |
317 |
318 | /// 滚动内容
319 | - (void)changeContentPage:(NSNumber *)index
320 | {
321 | UIView *superV = [self getLCPageViewWithCurrentView:self];
322 | if ([superV isKindOfClass:[LCPageView class]]) {
323 | SuppressPerformSelectorLeakWarning([superV performSelector:@selector(scrollCollectionviewWithIndexNum:) withObject:index]);
324 | }
325 | }
326 | /// 获取superView
327 | - (UIView *)getLCPageViewWithCurrentView:(UIView *)currentView
328 | {
329 | UIView *superV = [currentView superview];
330 | if (!superV) {
331 | return nil;
332 | }
333 | if (![superV isKindOfClass:[LCPageView class]]) {
334 | superV = [self getLCPageViewWithCurrentView:superV];
335 | }
336 | return superV;
337 | }
338 | - (void)scrollCollectionviewWithIndex:(NSInteger)index
339 | {
340 | [self changeContentPage:@(index)];
341 | }
342 |
343 | #pragma mark - 计算标题的偏移
344 |
345 | - (void)calculateTitleScrollDidScroll:(UIScrollView *)scrollView startScrollOffsetX:(CGFloat)startOffsetX endScrollOffsetX:(CGFloat)endOffsetX{
346 | if (!self.isWillBeginDragging) {
347 | return;
348 | }
349 |
350 | CGFloat progress = 0.0;
351 | NSInteger sourceIndex = 0.0;
352 | NSInteger targetIndex = 0.0;
353 | CGFloat currenOffsetX = scrollView.contentOffset.x;
354 | CGFloat scrollWidth = scrollView.frame.size.width;
355 | if (startOffsetX < currenOffsetX) { // 左滑
356 | progress = currenOffsetX / scrollWidth - floor(currenOffsetX / scrollWidth);
357 | sourceIndex = (NSInteger)currenOffsetX / scrollWidth;
358 |
359 | targetIndex = sourceIndex + 1;
360 | if (targetIndex >= self.titles.count) {
361 | targetIndex = self.titles.count - 1;
362 | }
363 | if (currenOffsetX - startOffsetX == scrollWidth) {
364 | progress = 1;
365 | targetIndex = sourceIndex;
366 | }
367 | }else if (startOffsetX > currenOffsetX){ // 右滑
368 | progress = 1 - (currenOffsetX / scrollWidth - floor(currenOffsetX / scrollWidth));
369 | targetIndex = (NSInteger)currenOffsetX / scrollWidth;
370 | sourceIndex = targetIndex + 1;
371 | if (sourceIndex >= self.titles.count) {
372 | sourceIndex = self.titles.count - 1;
373 | }
374 | if (currenOffsetX - startOffsetX == scrollWidth) {
375 | progress = 1;
376 | sourceIndex = targetIndex;
377 | }
378 | }else { // 在中间
379 | sourceIndex = self.oldLabelIndex;
380 | targetIndex = sourceIndex;
381 | progress = 1;
382 | }
383 | CGFloat score = (currenOffsetX / scrollWidth);
384 | NSInteger temp = (NSInteger)score;
385 | if (score == temp) {
386 | if (temp != self.page) {
387 |
388 | progress = 1;
389 | }
390 | self.page = temp;
391 |
392 | }
393 | // NSLog(@"最终++sourceIndex:%ld, targetIndex:%ld", sourceIndex, targetIndex);
394 | self.sourceIndex = sourceIndex;
395 | self.targetIndex = targetIndex;
396 | [self titleBarScrollProgres:progress sourceIndex:sourceIndex targetIndex:targetIndex];
397 |
398 | }
399 | - (void)titleBarScrollProgres:(CGFloat)progress sourceIndex:(NSInteger)sourceIndex targetIndex:(NSInteger)targetIndex{
400 | if (self.style.titleMillcolorGradEnable) {
401 | [self setTitleColorGradienWithProgress:progress sourceIndex:sourceIndex targetIndex:targetIndex];
402 | }
403 | if (self.style.titleZoomEnable) {
404 | [self setTitleZoomScaleProgress:progress sourceIndex:sourceIndex targetIndex:targetIndex];
405 | }
406 | if (self.style.bottomLineEnable) {
407 | [self setLineFollowMoveScaleProgress:progress sourceIndex:sourceIndex targetIndex:targetIndex];
408 | }
409 |
410 | if (progress == 1) {
411 | UILabel *label = self.titleLabels[sourceIndex];
412 | [self setLabelAutoScroll:label];
413 | // self.oldLabelIndex = targetIndex;
414 | }
415 |
416 | }
417 |
418 |
419 |
420 | - (CGFloat)getTitleDistanceWithSourceLabel:(NSInteger)sourceLabel targetLabel:(NSInteger)targetLabel{
421 | CGFloat titleTargetW = [self.titleWidths[targetLabel] floatValue];
422 | CGFloat titleSourceW = [self.titleWidths[sourceLabel] floatValue];
423 | return titleTargetW - titleSourceW;
424 | }
425 | /**
426 | 设置底部线跟着移动
427 | */
428 | - (void)setLineFollowMoveScaleProgress:(CGFloat)progress sourceIndex:(NSInteger)sourceIndex targetIndex:(NSInteger)targetIndex{
429 | UILabel *sourceLabel = self.titleLabels[sourceIndex];
430 | UILabel *targetLabel = self.titleLabels[targetIndex];
431 | CGFloat sourceTitleW = [self.titleWidths[sourceIndex] floatValue];
432 | CGFloat targetTitleW = [self.titleWidths[targetIndex] floatValue];
433 | CGFloat moveTotalX = targetLabel.lc_centerX - sourceLabel.lc_centerX;
434 | CGFloat moveX = moveTotalX * progress;
435 | CGFloat moveTotalW = targetTitleW - sourceTitleW;
436 | CGFloat moveW = moveTotalW * progress;
437 | self.indicatorLine.lc_width = sourceTitleW + moveW;
438 | self.indicatorLine.lc_centerX = sourceLabel.lc_centerX + moveX;
439 | // if (!self.isClickTitle) {
440 | // }
441 | }
442 |
443 | /**
444 | 设置标题大小缩放
445 | */
446 | - (void)setTitleZoomScaleProgress:(CGFloat)progress sourceIndex:(NSInteger)sourceIndex targetIndex:(NSInteger)targetIndex{
447 | UILabel *sourceLabel = self.titleLabels[sourceIndex];
448 | UILabel *targetLabel = self.titleLabels[targetIndex];
449 | CGFloat sourceScale = 1 - progress;
450 | sourceLabel.transform = CGAffineTransformMakeScale(sourceScale * self.style.titleZoomMaxScale + 1, sourceScale * self.style.titleZoomMaxScale + 1);
451 | targetLabel.transform = CGAffineTransformMakeScale(progress * self.style.titleZoomMaxScale + 1, progress * self.style.titleZoomMaxScale + 1);
452 | }
453 |
454 | /**
455 | 设置标题颜色渐变
456 | */
457 | - (void)setTitleColorGradienWithProgress:(CGFloat)progress sourceIndex:(NSInteger)sourceIndex targetIndex:(NSInteger)targetIndex{
458 | CGFloat r = _endR - _startR;
459 | CGFloat g = _endG - _startG;
460 | CGFloat b = _endB - _startB;
461 | UIColor *sourceColor = [UIColor colorWithRed:_endR - r * progress green:_endG - g * progress blue:_endB - b * progress alpha:1];
462 | UIColor *targetColor = [UIColor colorWithRed:_startR + r * progress green:_startG + g * progress blue:_startB + b * progress alpha:1];
463 | UILabel *sourceLabel = self.titleLabels[sourceIndex];
464 | UILabel *targetLabel = self.titleLabels[targetIndex];
465 | sourceLabel.textColor = sourceColor;
466 | targetLabel.textColor = targetColor;
467 | }
468 | // 获取两个标题按钮宽度差值
469 | - (CGFloat)widthDeltaWithTargetLabel:(UILabel *)targetLabel sourceLabel:(UILabel *)sourceLabel {
470 | CGFloat titleTargetW = [self.titleWidths[targetLabel.tag] floatValue];
471 | CGFloat titleSourceW = [self.titleWidths[sourceLabel.tag] floatValue];
472 | return titleTargetW - titleSourceW;
473 | }
474 | // 获取label里面title的X值
475 | - (CGFloat)getLabelTitleX:(UILabel *)label {
476 | CGFloat titleWidth = [self.titleWidths[label.tag] floatValue];
477 | return (label.lc_width - titleWidth) * 0.5;
478 | }
479 |
480 | #pragma mark - 代理方法
481 | /// 即将开始拖拽
482 | - (void)lc_scrollViewWillBeginDragging:(UIScrollView *)scrollView
483 | {
484 | [self collectionViewWillBeginDragging:(UICollectionView *)scrollView];
485 | _startOffsetX = scrollView.contentOffset.x;
486 | _isWillBeginDragging = YES;
487 |
488 | }
489 |
490 | /// 已经停止拖拽
491 | - (void)lc_scrollViewDidEndDragging:(UICollectionView *)collectionView willDecelerate:(BOOL)decelerate
492 | {
493 | [self collectionViewDidEndDragging:collectionView willDecelerate:decelerate];
494 | // NSInteger index = (NSInteger)(collectionView.contentOffset.x / collectionView.bounds.size.width);
495 | }
496 |
497 | /// 已经结束拖拽
498 | - (void)lc_scrollViewDidEndDecelerating:(UICollectionView *)collectionView
499 | {
500 | [self collectionViewDidEndDecelerating:collectionView];
501 | NSInteger index = (NSInteger)(collectionView.contentOffset.x / collectionView.bounds.size.width);
502 | self.didEndOffsetX = collectionView.contentOffset.x;
503 | [self calculateTitleScrollDidScroll:(UIScrollView *)collectionView startScrollOffsetX:_startOffsetX endScrollOffsetX:_didEndOffsetX];
504 | [self setLabelClickTitleState:self.titleLabels[index]];
505 |
506 | }
507 |
508 | /// 已经滚动结束
509 | - (void)lc_scrollViewDidEndScrollingAnimation:(UICollectionView *)collectionView
510 | {
511 | [self collectionViewDidEndScrollingAnimation:collectionView];
512 | }
513 |
514 | /// 正在滚动
515 | - (void)lc_scrollViewDidHorizontalScroll:(UIScrollView *)scrollView
516 | {
517 | [self collectionViewDidScroll:(UICollectionView *)scrollView];
518 | [self calculateTitleScrollDidScroll:scrollView startScrollOffsetX:_startOffsetX endScrollOffsetX:_didEndOffsetX];
519 | }
520 | - (void)lc_scrollViewDidVerticalScroll:(UIScrollView *)scrollView{}
521 |
522 | #pragma mark - 子类重写的方法
523 | /**
524 | 正在滚动
525 |
526 | @param collectionView 内容滚动视图
527 | */
528 | - (void)collectionViewDidScroll:(UICollectionView *)collectionView {}
529 |
530 | /// 即将开始拖拽
531 | - (void)collectionViewWillBeginDragging:(UICollectionView *)collectionView {}
532 |
533 | /// 已经停止拖拽
534 | - (void)collectionViewDidEndDragging:(UICollectionView *)collectionView willDecelerate:(BOOL)decelerate {}
535 |
536 | /// 已经结束拖拽
537 | - (void)collectionViewDidEndDecelerating:(UICollectionView *)collectionView {}
538 |
539 | /// 已经滚动结束
540 | - (void)collectionViewDidEndScrollingAnimation:(UICollectionView *)collectionView {}
541 |
542 |
543 | #pragma mark - getter setter
544 | - (void)setupStartColor:(UIColor *)color
545 | {
546 | CGFloat components[3];
547 | [self getRGBComponents:components forColor:color];
548 | _startR = components[0];
549 | _startG = components[1];
550 | _startB = components[2];
551 | }
552 |
553 | - (void)setupEndColor:(UIColor *)color
554 | {
555 | CGFloat components[3];
556 | [self getRGBComponents:components forColor:color];
557 | _endR = components[0];
558 | _endG = components[1];
559 | _endB = components[2];
560 | }
561 | - (UIScrollView *)titleScrollView
562 | {
563 | if (!_titleScrollView) {
564 | _titleScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)];
565 | _titleScrollView.showsHorizontalScrollIndicator = NO;
566 | _titleScrollView.scrollsToTop = NO;
567 | _titleScrollView.backgroundColor = [UIColor clearColor];
568 | }
569 | return _titleScrollView;
570 | }
571 |
572 | /**
573 | * 滚动指示条
574 | */
575 | - (UIView *)indicatorLine {
576 | if (!_indicatorLine) {
577 | UIView *indicatorLine = [[UIView alloc] init];
578 | _indicatorLine = indicatorLine;
579 | indicatorLine.backgroundColor = self.style.bottomLineColor;
580 | [self.titleScrollView addSubview:_indicatorLine];
581 | }
582 | return _indicatorLine;
583 | }
584 | /**
585 | * 存放每个标题的字体宽度
586 | */
587 | - (NSMutableArray *)titleWidths {
588 | if (!_titleWidths) {
589 | _titleWidths = [NSMutableArray array];
590 | }
591 | return _titleWidths;
592 | }
593 | /**
594 | * 存放标题
595 | */
596 | - (NSMutableArray *)titleLabels {
597 | if (!_titleLabels) {
598 | _titleLabels = [NSMutableArray array];
599 | }
600 | return _titleLabels;
601 | }
602 |
603 |
604 | #pragma mark - 固定视图的高度
605 | -(void)layoutSubviews{
606 | [super layoutSubviews];
607 | // self.lc_height = LCTitleBarH;
608 | }
609 |
610 |
611 |
612 |
613 | /**
614 | * 指定颜色,获取颜色的RGB值
615 | *
616 | * @param components RGB数组
617 | * @param color 颜色
618 | */
619 | - (void)getRGBComponents:(CGFloat [3])components forColor:(UIColor *)color {
620 | CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
621 | unsigned char resultingPixel[4];
622 | CGContextRef context = CGBitmapContextCreate(&resultingPixel,
623 | 1,
624 | 1,
625 | 8,
626 | 4,
627 | rgbColorSpace,
628 | 1);
629 | CGContextSetFillColorWithColor(context, [color CGColor]);
630 | CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
631 | CGContextRelease(context);
632 | CGColorSpaceRelease(rgbColorSpace);
633 | for (int component = 0; component < 3; component++) {
634 | components[component] = resultingPixel[component] / 255.0f;
635 | }
636 | }
637 |
638 |
639 | @end
640 |
--------------------------------------------------------------------------------