├── .swift-version
├── YLTagsChooser
├── YLTagsChooser
│ ├── zh-Hans.lproj
│ │ ├── LaunchScreen.strings
│ │ └── Main.strings
│ ├── ViewController.h
│ ├── AppDelegate.h
│ ├── TagsChooser
│ │ ├── NSArray+YLBoundsCheck.h
│ │ ├── YLCollectionReusableView.h
│ │ ├── NSArray+YLBoundsCheck.m
│ │ ├── YLTag.h
│ │ ├── YLTag.m
│ │ ├── YLWaterFlowLayout.h
│ │ ├── YLCollectionReusableView.m
│ │ ├── YLTagsChooser.h
│ │ ├── YLWaterFlowLayout.m
│ │ └── YLTagsChooser.m
│ ├── main.m
│ ├── Info.plist
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── AppDelegate.m
│ └── ViewController.m
├── .DS_Store
├── YLTagsChooser.xcodeproj
│ ├── xcuserdata
│ │ ├── TK-001289.xcuserdatad
│ │ │ ├── xcdebugger
│ │ │ │ └── Breakpoints_v2.xcbkptlist
│ │ │ └── xcschemes
│ │ │ │ ├── xcschememanagement.plist
│ │ │ │ └── YLTagsChooser.xcscheme
│ │ ├── lumin.xcuserdatad
│ │ │ ├── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ │ └── xcdebugger
│ │ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── Lambert.xcuserdatad
│ │ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ │ └── xcschemes
│ │ │ ├── xcschememanagement.plist
│ │ │ └── YLTagsChooser.xcscheme
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcuserdata
│ │ │ ├── lumin.xcuserdatad
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ │ ├── Lambert.xcuserdatad
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ │ └── TK-001289.xcuserdatad
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
├── YLTagsChooserTests
│ ├── Info.plist
│ └── YLTagsChooserTests.m
└── YLTagsChooserUITests
│ ├── Info.plist
│ └── YLTagsChooserUITests.m
├── .DS_Store
├── .gitignore
├── fix_bug3.gif
├── YLTagsChooser.gif
├── ReappearBugCode
├── 说明.md
├── YLWaterFlowLayout_Bug.h
└── YLWaterFlowLayout_Bug.m
├── YLTagsChooser.podspec
├── LICENSE
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/zh-Hans.lproj/LaunchScreen.strings:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ApesTalk/YLTagsChooser/HEAD/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 | .DS_Store
4 | .DS_Store
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/fix_bug3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ApesTalk/YLTagsChooser/HEAD/fix_bug3.gif
--------------------------------------------------------------------------------
/YLTagsChooser.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ApesTalk/YLTagsChooser/HEAD/YLTagsChooser.gif
--------------------------------------------------------------------------------
/ReappearBugCode/说明.md:
--------------------------------------------------------------------------------
1 | 使用这里的YLWaterFlowLayout_Bug替代项目中的YLWaterFlowLayout,即可复现在滑动过程中有些cell一会显示一会消失的效果。
2 |
--------------------------------------------------------------------------------
/YLTagsChooser/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ApesTalk/YLTagsChooser/HEAD/YLTagsChooser/.DS_Store
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/zh-Hans.lproj/Main.strings:
--------------------------------------------------------------------------------
1 |
2 | /* Class = "UIButton"; normalTitle = "选择标签"; ObjectID = "9Nb-dT-wv5"; */
3 | "9Nb-dT-wv5.normalTitle" = "选择标签";
4 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/xcuserdata/TK-001289.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/project.xcworkspace/xcuserdata/lumin.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ApesTalk/YLTagsChooser/HEAD/YLTagsChooser/YLTagsChooser.xcodeproj/project.xcworkspace/xcuserdata/lumin.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/project.xcworkspace/xcuserdata/Lambert.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ApesTalk/YLTagsChooser/HEAD/YLTagsChooser/YLTagsChooser.xcodeproj/project.xcworkspace/xcuserdata/Lambert.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/project.xcworkspace/xcuserdata/TK-001289.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ApesTalk/YLTagsChooser/HEAD/YLTagsChooser/YLTagsChooser.xcodeproj/project.xcworkspace/xcuserdata/TK-001289.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 | @property (weak, nonatomic) IBOutlet UILabel *textLabel;
13 |
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. 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 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/NSArray+YLBoundsCheck.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+YLBoundsCheck.h
3 | // YLLanJiQuan
4 | //
5 | // Created by TK-001289 on 16/1/20.
6 | // Copyright (c) 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSArray (YLBoundsCheck)
12 |
13 | /**对数组边界进行检查,避免数组越界*/
14 | - (id)yl_objectAtIndex:(NSUInteger)index;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. 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 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/YLCollectionReusableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // YLCollectionReusableView.h
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 2016/10/31.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | // test UICollectionView section header
12 | @interface YLCollectionReusableView : UICollectionReusableView
13 | - (void)setTitle:(NSString *)title;
14 | @end
15 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/NSArray+YLBoundsCheck.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+YLBoundsCheck.m
3 | // YLLanJiQuan
4 | //
5 | // Created by TK-001289 on 16/1/20.
6 | // Copyright (c) 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import "NSArray+YLBoundsCheck.h"
10 |
11 | @implementation NSArray (YLBoundsCheck)
12 | - (id)yl_objectAtIndex:(NSUInteger)index
13 | {
14 | if(index
10 |
11 | @interface YLTag : NSObject
12 | @property(nonatomic,assign)NSInteger objId;
13 | @property(nonatomic,copy)NSString *name;
14 | @property(nonatomic,assign)BOOL selected;///< default is NO
15 |
16 | - (instancetype)initWithId:(NSInteger)oId name:(NSString *)name;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/xcuserdata/lumin.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | YLTagsChooser.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 | YLTagsChooser.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/YLTagsChooser.podspec:
--------------------------------------------------------------------------------
1 |
2 | Pod::Spec.new do |s|
3 |
4 | s.name = "YLTagsChooser"
5 | s.version = "1.0"
6 | s.summary = "用UICollectionView实现的兴趣标签选择器 (A interest tag selector implemented with the UICollectionView.)"
7 | s.homepage = "https://github.com/lqcjdx/YLTagsChooser"
8 | s.license = "MIT"
9 | s.author = { "lgcjdx" => "lgcjdx@163.com" }
10 | s.platform = :ios, '7.0'
11 | s.source = { :git => "https://github.com/lqcjdx/YLTagsChooser.git", :tag => "#{s.version}" }
12 | s.source_files = "YLTagsChooser/YLTagsChooser/TagsChooser/*.{h,m}"
13 | s.requires_arc = true
14 | end
15 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/xcuserdata/lumin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/xcuserdata/Lambert.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ReappearBugCode/YLWaterFlowLayout_Bug.h:
--------------------------------------------------------------------------------
1 | //
2 | // YLWaterFlowLayout.h
3 | // YLLanJiQuan
4 | //
5 | // Created by TK-001289 on 16/5/10.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class YLWaterFlowLayout_Bug;
12 | @protocol YLWaterFlowLayoutDelegate
13 | /**通过代理获得每个cell的宽度*/
14 | - (CGFloat)waterFlowLayout:(YLWaterFlowLayout *)layout widthAtIndexPath:(NSIndexPath *)indexPath;
15 |
16 | @end
17 |
18 | @interface YLWaterFlowLayout_Bug : UICollectionViewFlowLayout
19 | @property (nonatomic,assign) id delegate;
20 | @property(nonatomic,assign)CGFloat rowHeight;///< 固定行高
21 |
22 | @end
23 |
24 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/YLTag.m:
--------------------------------------------------------------------------------
1 | //
2 | // YLTag.m
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 2017/6/15.
6 | // Copyright © 2017年 YL. All rights reserved.
7 | //
8 |
9 | #import "YLTag.h"
10 |
11 | @implementation YLTag
12 | - (instancetype)initWithId:(NSInteger)oId name:(NSString *)name
13 | {
14 | if(self = [super init]){
15 | self.objId = oId;
16 | self.name = name;
17 | }
18 | return self;
19 | }
20 |
21 | - (BOOL)isEqual:(id)object
22 | {
23 | if(!object || ![object isKindOfClass:[self class]]){
24 | return NO;
25 | }
26 | YLTag *otherTag = (YLTag *)object;
27 | return self.objId == otherTag.objId && [self.name isEqualToString:otherTag.name];
28 | }
29 |
30 | - (NSString *)description
31 | {
32 | return self.name;
33 | }
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooserTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooserUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/xcuserdata/Lambert.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | YLTagsChooser.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 4570422C1D0E580400128041
16 |
17 | primary
18 |
19 |
20 | 457042451D0E580400128041
21 |
22 | primary
23 |
24 |
25 | 457042501D0E580400128041
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/xcuserdata/TK-001289.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | YLTagsChooser.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 4570422C1D0E580400128041
16 |
17 | primary
18 |
19 |
20 | 457042451D0E580400128041
21 |
22 | primary
23 |
24 |
25 | 457042501D0E580400128041
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/YLWaterFlowLayout.h:
--------------------------------------------------------------------------------
1 | //
2 | // YLWaterFlowLayout.h
3 | // YLLanJiQuan
4 | //
5 | // Created by TK-001289 on 16/5/10.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class YLWaterFlowLayout;
12 | @protocol YLWaterFlowLayoutDelegate
13 | /**通过代理获得每个cell的宽度*/
14 | - (CGFloat)waterFlowLayout:(YLWaterFlowLayout *)layout widthAtIndexPath:(NSIndexPath *)indexPath;
15 |
16 | @end
17 |
18 | @interface YLWaterFlowLayout : UICollectionViewFlowLayout
19 | @property (nonatomic, weak) id delegate;
20 | @property (nonatomic, assign) CGFloat rowHeight;///< 固定行高
21 |
22 | /**
23 | 如果你的数据源是会改变的,你应该在数据源发生改变后,先调用该方法刷新frames再调用reloadData。
24 | Call this method when your datasource changed and then call UICollectionView's reloadData.
25 | */
26 | - (void)reCalculateFrames;
27 |
28 | @end
29 |
30 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/YLCollectionReusableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // YLCollectionReusableView.m
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 2016/10/31.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import "YLCollectionReusableView.h"
10 |
11 | @interface YLCollectionReusableView ()
12 | @property(nonatomic,strong)UILabel *textLabel;
13 | @end
14 |
15 | @implementation YLCollectionReusableView
16 | - (void)prepareForReuse
17 | {
18 |
19 | }
20 |
21 | -(instancetype)initWithFrame:(CGRect)frame
22 | {
23 | if(self = [super initWithFrame:frame]){
24 | self.backgroundColor = [UIColor whiteColor];
25 | _textLabel = [[UILabel alloc]initWithFrame:self.bounds];
26 | _textLabel.textAlignment = NSTextAlignmentCenter;
27 | _textLabel.text = @"This is a section header/footer";
28 | [self addSubview:_textLabel];
29 | }
30 | return self;
31 | }
32 |
33 | - (void)setTitle:(NSString *)title
34 | {
35 | _textLabel.text = title;
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooserTests/YLTagsChooserTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // YLTagsChooserTests.m
3 | // YLTagsChooserTests
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface YLTagsChooserTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation YLTagsChooserTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Mr Lu
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 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooserUITests/YLTagsChooserUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // YLTagsChooserUITests.m
3 | // YLTagsChooserUITests
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface YLTagsChooserUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation YLTagsChooserUITests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 |
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 |
22 | // In UI tests it is usually best to stop immediately when a failure occurs.
23 | self.continueAfterFailure = NO;
24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
25 | [[[XCUIApplication alloc] init] launch];
26 |
27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
28 | }
29 |
30 | - (void)tearDown {
31 | // Put teardown code here. This method is called after the invocation of each test method in the class.
32 | [super tearDown];
33 | }
34 |
35 | - (void)testExample {
36 | // Use recording to get started writing UI tests.
37 | // Use XCTAssert and related functions to verify your tests produce the correct results.
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/YLTagsChooser.h:
--------------------------------------------------------------------------------
1 | //
2 | // YLTagsChooser.h
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol YLTagsChooserDelegate;
12 |
13 | @interface YLTagsChooser : UIView
14 | @property(nonatomic,weak)iddelegate;
15 | @property(nonatomic,assign) CGFloat bottomHeight; ///< 指定白色背景高度
16 | @property(nonatomic,assign) NSInteger maxSelectCount; ///< 最多可选择数量
17 |
18 | /**
19 | 必须调用的初始化方法
20 | you must call this init method
21 | */
22 | -(instancetype)initWithBottomHeight:(CGFloat)bHeight
23 | maxSelectCount:(CGFloat)maxCount
24 | delegate:(id)aDelegate;
25 |
26 | /**
27 | 刷新
28 |
29 | @param tags 原数据。包含多个数组对象的数组,决定有几个section
30 | @param selectedTags 已选择的数据
31 | */
32 | -(void)refreshWithTags:(NSArray *)tags selectedTags:(NSArray *)selectedTags;
33 | - (void)showInView:(UIView *)view;
34 | - (void)dismiss;
35 |
36 | @end
37 |
38 |
39 | //代理
40 | @protocol YLTagsChooserDelegate
41 |
42 | - (void)tagsChooser:(YLTagsChooser *)sheet selectedTags:(NSArray *)sTags;
43 |
44 |
45 | @end
46 |
47 | #pragma mark---标签cell
48 | @interface YLTagsCollectionViewCell : UICollectionViewCell
49 | @property(nonatomic,strong)UILabel *textLabel;
50 |
51 | - (void)refreshWithObject:(NSObject *)obj;
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.6
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1510
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/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 | "info" : {
90 | "version" : 1,
91 | "author" : "xcode"
92 | }
93 | }
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. 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 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // 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.
25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
26 | }
27 |
28 | - (void)applicationDidEnterBackground:(UIApplication *)application {
29 | // 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.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | - (void)applicationWillEnterForeground:(UIApplication *)application {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application {
38 | // 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.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "YLTagsChooser.h"
11 | #import "YLTag.h"
12 |
13 | @interface ViewController ()
14 | {
15 | NSMutableArray *selectedTags;
16 | }
17 | @end
18 |
19 | @implementation ViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 | // Do any additional setup after loading the view, typically from a nib.
24 | selectedTags = [NSMutableArray array];
25 | }
26 |
27 | - (void)didReceiveMemoryWarning {
28 | [super didReceiveMemoryWarning];
29 | // Dispose of any resources that can be recreated.
30 | }
31 |
32 | - (IBAction)chooseTags:(id)sender {
33 | _textLabel.text = nil;
34 | YLTagsChooser *chooser = [[YLTagsChooser alloc]initWithBottomHeight:500 maxSelectCount:8 delegate:self];
35 | NSMutableArray *orignDataArray = [NSMutableArray array];
36 | NSArray *tags = @[@"篮球",
37 | @"足球",
38 | @"羽毛球",
39 | @"乒乓球",
40 | @"排球",
41 | @"网球",
42 | @"高尔夫球",
43 | @"冰球",
44 | @"沙滩排球",
45 | @"棒球",
46 | @"垒球",
47 | @"藤球",
48 | @"毽球",
49 | @"台球",
50 | @"鞠蹴",
51 | @"板球",
52 | @"壁球",
53 | @"沙壶",
54 | @"克郎球",
55 | @"橄榄球",
56 | @"曲棍球",
57 | @"水球",
58 | @"马球",
59 | @"保龄球",
60 | @"健身球",
61 | @"门球",
62 | @"弹球"
63 | ];
64 | NSInteger index = tags.count;
65 | NSMutableArray *testTags0 = [NSMutableArray arrayWithCapacity:tags.count];
66 | for(NSInteger i = 0; i < index; i++){
67 | YLTag *tag = [[YLTag alloc]initWithId:i name:tags[i]];
68 | [testTags0 addObject:tag];
69 | }
70 | [orignDataArray addObject:testTags0];
71 |
72 | //一个section
73 | // [chooser showInView:self.view];
74 | // [chooser refreshWithTags:orignDataArray selectedTags:selectedTags];
75 | // return;
76 |
77 | NSMutableArray *testTags1 = [NSMutableArray arrayWithCapacity:40];
78 | for(NSInteger i = index; i < index + 40; i++){
79 | NSString *name;
80 | if(i % 4 == 0){
81 | name = [NSString stringWithFormat:@"Remember%li",(long)i];
82 | }else if (i % 4 == 1){
83 | name = [NSString stringWithFormat:@"Remember Give%li",(long)i];
84 | }else if(i % 4 == 2){
85 | name = [NSString stringWithFormat:@"Remember Give Me a@%li",(long)i];
86 | }else{
87 | name = [NSString stringWithFormat:@"Remember Give Me a Star@%li",(long)i];
88 | }
89 | YLTag *tag = [[YLTag alloc]initWithId:i name:name];
90 | [testTags1 addObject:tag];
91 | }
92 | [orignDataArray addObject:testTags1];
93 |
94 |
95 | index += 40;
96 | NSMutableArray *testTags2 = [NSMutableArray arrayWithCapacity:20];
97 | for(NSInteger i = index; i < index + 20; i++){
98 | NSString *name;
99 | if(i % 3 == 0){
100 | name = [NSString stringWithFormat:@"标签选择器%li",(long)i];
101 | }else if (i % 3 == 1){
102 | name = [NSString stringWithFormat:@"Lambert%li",(long)i];
103 | }else{
104 | name = [NSString stringWithFormat:@"CodeNinja%li",(long)i];
105 | }
106 | YLTag *tag = [[YLTag alloc]initWithId:i name:name];
107 | [testTags2 addObject:tag];
108 | }
109 | [orignDataArray addObject:testTags2];
110 |
111 | //多个section
112 | [chooser showInView:self.view];
113 | [chooser refreshWithTags:orignDataArray selectedTags:selectedTags];
114 | }
115 |
116 |
117 | #pragma mark---YLTagsChooserDelegate
118 | - (void)tagsChooser:(YLTagsChooser *)sheet selectedTags:(NSArray *)sTags
119 | {
120 | [selectedTags removeAllObjects];
121 | [selectedTags addObjectsFromArray:sTags];
122 | NSString *tagStr = [sTags componentsJoinedByString:@"\n"];
123 | NSString *result = [NSString stringWithFormat:@"你选择了以下标签:\n%@",tagStr];
124 | _textLabel.text = result;
125 | }
126 |
127 | @end
128 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/xcuserdata/Lambert.xcuserdatad/xcschemes/YLTagsChooser.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
95 |
101 |
102 |
103 |
104 |
106 |
107 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/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 |
32 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/xcuserdata/TK-001289.xcuserdatad/xcschemes/YLTagsChooser.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
89 |
90 |
91 |
92 |
93 |
94 |
100 |
102 |
108 |
109 |
110 |
111 |
113 |
114 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # YLTagsChooser
2 | 用UICollectionView实现的兴趣标签选择器,可传入最多可选择标签数量和上一次选中的标签。
3 |
4 | **注意:UICollectionView在cell较多的情况下,在滑动的过程中会出现某些cell一会显示一会不显示的问题已解决,欢迎star和提出优化方案。**
5 |
6 | 在这里我用UICollectionView实现了高度固定,宽度不固定,每行显示的标签个数不定的瀑布流效果。本示例只简单展示了类似UIActionSheet弹出效果。可以模仿实现其他类似效果。
7 |
8 | 核心思想就是在- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath方法中通过计算上一个cell的frame和当前cell的frame来判断将当前cell显示在什么位置。具体实现见代码。
9 |
10 | ---
11 | A interest tag selector implemented with the UICollectionView, which allows you to pass in the maximum number of selectable tags and the last selected tags.
12 |
13 | Here I use UICollectionView to achieve a high degree of fixed, the width is not fixed, each row shows the number of labels on the waterfall flow effect. This example only shows a similar UIActionSheet pop-up effect. It also Can be imitated to achieve other similar effects.
14 |
15 |
16 | ## 效果图 Demonstration :
17 |
18 | 
19 |
20 |
21 | ## 2016-11-1 update
22 |
23 | 一、修复数据源较多的情况下部分cell不显示的bug:[标签很多的时候,中间cell会不显示,而且会出现很多空白的地方 #1](https://github.com/lqcjdx/YLTagsChooser/issues/1) (fixed bug #1)
24 |
25 | 二、实现可添加sectionheader的效果。(Show how to add a UICollectionReusableView.)
26 |
27 | 因为有人反映自定义UICollectionViewFlowLayout的情况下,方法
28 |
29 | ```OBJC
30 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
31 | viewForSupplementaryElementOfKind:(NSString *)kind
32 | atIndexPath:(NSIndexPath *)indexPath
33 | ```
34 |
35 | 不调用的问题。我也做了尝试,确实是很容易就造成这个方法不被调用。
36 |
37 | 实现步骤总结如下:
38 |
39 | 1、设置自定义UICollectionViewFlowLayout对象的headerReferenceSize属性或者实现UICollectionViewDelegateFlowLayout的
40 |
41 | ```OBJC
42 | - (CGSize)collectionView:(UICollectionView *)collectionView
43 | layout:(UICollectionViewLayout*)collectionViewLayout
44 | referenceSizeForHeaderInSection:(NSInteger)section
45 | ```
46 |
47 | 方法返回sectionheader的高度;
48 |
49 | 2、注册sectionheader:
50 |
51 | ```OBJC
52 | [_myCollectionView registerClass:[YLCollectionReusableView class]
53 | forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
54 | withReuseIdentifier:@"YLCollectionReusableView”]
55 | ```
56 |
57 |
58 | 3、在方法
59 |
60 | ```OBJC
61 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
62 | viewForSupplementaryElementOfKind:(NSString *)kind
63 | atIndexPath:(NSIndexPath *)indexPath
64 | ```
65 |
66 | 中返回自定义sectionheader;
67 |
68 |
69 | 需要注意的是在``- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect``方法中应该加上判断当前UICollectionViewLayoutAttributes的representedElementCategory属性是否是UICollectionElementCategoryCell类型的,不能在这里改变了sectionheader的高度导致显示异常。因为所有view和cell的属性都会调用该方法。
70 |
71 | ## 2017-06-15 update
72 |
73 | 一、新增默认选中的标签,下次进入页面会默认显示上次选中的标签。[Can we retain selected tag ?](https://github.com/lqcjdx/YLTagsChooser/issues/2)(fixed bug #2)
74 |
75 | 可能同一个APP内会有多个页面会使用到标签选择器,并且每次选择的标签类型不太一样,所以建议将标签封装为一个对象:YLTag,每次显示YLTagsChooser的时候传入上次选中的值。
76 |
77 | Maybe you have multiple pages in your APP will use the tags selector, and each time you select the tags, the type is not the same, it is recommended to package the tag as an object: YLTag, each time you display YLTagsChooser, you can pass the last selected The value to it.
78 |
79 |
80 | ## 2017-09-07 update
81 |
82 | 一、支持多Section,每个section一个header,解决高度计算问题。使用方法:
83 | 将每个分区的数据单独放在一个数组中,最后这些数组都放在orignDataArray里面,调用以下方法传入数据即可。
84 |
85 | ```OBJC
86 | [chooser refreshWithTags:orignDataArray selectedTags:selectedTags];
87 | ```
88 |
89 | ## 2017-09-08 update
90 |
91 | 一、解决UICollectionView在滑动过程中有些cell一会显示一会不显示的问题。[UICollectionView的重大bug?真相竟然是......](http://www.jianshu.com/p/c07e28d3563d)
92 |
93 | 搜索``UICollectionView some cell not appear``或``UICollectionViewCell not appear``可见相关问题。
94 |
95 | 这个bug主要是cell较多并且cell的frame属性自定义的情况下很容易发生。
96 |
97 | 因为我们的每个item的属性都是完全自定义的,所以在``layoutAttributesForElementsInRect:``方法如果直接调用``[super layoutAttributesForElementsInRect: rect]``,拿到的这个集合其实是不对的,所以或导致有些cell在滑动过程中一会消失一会显示。
98 |
99 |
100 | ### 下面简单讲一下解决思路:
101 |
102 | 自定义UICollectionViewLayout必须覆盖的方法且执行顺序如下:
103 |
104 | ```OBJC
105 | - (void)prepareLayout;
106 | - (CGSize)collectionViewContentSize;
107 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
108 | ```
109 |
110 | - prepareLayout
111 |
112 | 布局快要生效的时候被调用,在这里将每个item的frame属性计算出来并缓存起来。
113 |
114 | - collectionViewContentSize
115 |
116 | UICollectionView通过它获取可滑动范围,在这里根据上面计算的值获取最大可滚动范围。
117 |
118 | - layoutAttributesForElementsInRect:
119 |
120 | 返回指定区域内的item的布局属性(UICollectionViewLayoutAttributes对象)集合。
121 |
122 | 解决这个bug的要点是在``layoutAttributesForElementsInRect:``方法中先获取UICollectionView当前的可见范围,然后根据第一步计算并缓存起来的item属性来判断当前可见范围内有哪些item应该是可见的,把这些item对应的UICollectionViewLayoutAttributes对象放到一个数组中返回。
123 |
124 | ## 2018-2-7 update
125 |
126 | Fix bug3, support set UICollectionView's contentInset and also support set setionHeader and sectionFooter at the same time.
127 |
128 | 修复bug:[设置 collectionView.contentInset 时候,就会有问题
129 | #3](https://github.com/lqcjdx/YLTagsChooser/issues/3) (fixed bug #3)
130 |
131 | - 新增支持设置UICollectionview的contentInset。
132 |
133 | 举个栗子:
134 |
135 | 设置了``_myCollectionView.contentInset = UIEdgeInsetsMake(30, 20, 30, 20);``之后,需要在YLWaterFlowLayout中的如下方法返回UICollectionView的可滚动范围,否则你会发现它竟然可以左右滚动。
136 |
137 | ```OBJC
138 | #pragma mark - CollectionView的滚动范围
139 | //step2
140 | - (CGSize)collectionViewContentSize
141 | {
142 | //support set collectionview's contentInset
143 | CGFloat width = self.collectionView.frame.size.width - self.collectionView.contentInset.left - self.collectionView.contentInset.right;
144 | return CGSizeMake(width, _contentHeight);
145 | }
146 | ```
147 |
148 | - 新增支持UICollectionview同时设置sectionHeader和sectionFooter。
149 |
150 | 已优化YLWaterFlowLayout的代码,支持同时设置UICollectionView的contentInset、layout的sectionInset、layout的headerReferenceSize和layout的footerReferenceSize,也可以正常地全部展示header、footer和cell。
151 |
152 | 效果图如下:
153 |
154 | 
155 |
156 |
157 |
158 | 当然,这里只是提供一种解决UICollectionView滚动过程中部分cell一会显示一会不显示的问题思路。如果你需要实现其他效果,你可以自己尝试用我这种思路去实现,或者提出来,大家一起讨论一下怎么实现。如果帮到了你,请留下一个Star表示支持,谢谢!
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/ReappearBugCode/YLWaterFlowLayout_Bug.m:
--------------------------------------------------------------------------------
1 | //
2 | // YLWaterFlowLayout.m
3 | // YLLanJiQuan
4 | //
5 | // Created by TK-001289 on 16/5/10.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import "YLWaterFlowLayout_Bug.h"
10 | #import "NSArray+YLBoundsCheck.h"
11 |
12 | @interface YLWaterFlowLayout_Bug()
13 | @property(nonatomic,strong)NSMutableArray *framesArray;
14 | @end
15 |
16 |
17 | @implementation YLWaterFlowLayout_Bug
18 | #pragma mark - 初始化属性
19 | - (instancetype)init {
20 | self = [super init];
21 | if (self) {
22 | [self setupLayout];
23 | _framesArray = [NSMutableArray array];
24 | }
25 | return self;
26 | }
27 |
28 | - (void)setupLayout
29 | {
30 | self.minimumInteritemSpacing = 10;//同一行不同cell间距
31 | self.minimumLineSpacing = 10;//行间距
32 | self.headerReferenceSize = CGSizeMake(0, 50);//设置section header 固定高度,如果需要的话
33 | self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
34 | self.scrollDirection = UICollectionViewScrollDirectionVertical;
35 | }
36 |
37 | #pragma mark - 重写父类的方法,实现瀑布流布局
38 | #pragma mark - 当尺寸有所变化时,重新刷新
39 | - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
40 | return YES;
41 | }
42 |
43 | - (void)prepareLayout {
44 | [super prepareLayout];
45 | }
46 |
47 | #pragma mark - CollectionView的滚动范围
48 | - (CGSize)collectionViewContentSize
49 | {
50 | CGFloat width = self.collectionView.frame.size.width;
51 | CGFloat maxY = [self maxOrignYInSection:_framesArray.count - 1];
52 | return CGSizeMake(width, maxY + _rowHeight + self.sectionInset.bottom);
53 | }
54 |
55 | #pragma mark - 所有cell和view的布局属性
56 | //sectionheader sectionfooter decorationview collectionviewcell的属性都会走这个方法
57 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
58 | {
59 | NSArray *tmpArray = [super layoutAttributesForElementsInRect:rect];
60 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:tmpArray.count];
61 | for(NSInteger i = 0; i < tmpArray.count; i++){
62 | UICollectionViewLayoutAttributes *attrs = [tmpArray objectAtIndex:i];
63 | UICollectionElementCategory category = attrs.representedElementCategory;
64 | if(category == UICollectionElementCategoryCell){
65 | [array addObject:[self layoutAttributesForItemAtIndexPath:attrs.indexPath]];
66 | }else if (category == UICollectionElementCategorySupplementaryView){
67 | UICollectionViewLayoutAttributes *theAttrs = [self layoutAttributesForSupplementaryViewOfKind:attrs.representedElementKind atIndexPath:attrs.indexPath];
68 | [array addObject:theAttrs];
69 | }
70 | }
71 | return array;
72 | }
73 |
74 | #pragma mark - 指定cell的布局属性
75 | - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
76 | {
77 | UICollectionViewLayoutAttributes *attrs = [[super layoutAttributesForSupplementaryViewOfKind:elementKind atIndexPath:indexPath] copy];
78 | NSInteger section = attrs.indexPath.section;
79 | if(section > 0 && _framesArray.count > section - 1){
80 | CGFloat y = [self maxOrignYInSection:section - 1] + _rowHeight + self.sectionInset.bottom;
81 | CGRect frame = attrs.frame;
82 | frame.origin.y = y;
83 | attrs.frame = frame;
84 | }
85 | return attrs;
86 | }
87 |
88 | - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
89 | {
90 | NSInteger section = indexPath.section;
91 | NSInteger row = indexPath.row;
92 | UICollectionViewLayoutAttributes *attrs = [[super layoutAttributesForItemAtIndexPath:indexPath] copy];
93 | //已经计算过了
94 | if(_framesArray.count > section){
95 | NSArray *sectionFramesArray = _framesArray[section];
96 | if(sectionFramesArray.count > row){
97 | attrs.frame = [sectionFramesArray[row] CGRectValue];
98 | return attrs;
99 | }
100 | }
101 |
102 | //计算新的
103 | CGFloat x = self.sectionInset.left;
104 | //如果有sectionheader需要加上sectionheader高度
105 | CGFloat y = self.headerReferenceSize.height + self.sectionInset.top;
106 |
107 | if(section > 0 && _framesArray.count > section - 1){
108 | y = [self maxOrignYInSection:section - 1] + _rowHeight + self.sectionInset.bottom + self.headerReferenceSize.height + self.sectionInset.top;
109 | }
110 |
111 | NSMutableArray *sectionFramesArray = [_framesArray yl_objectAtIndex:section];
112 | if(!sectionFramesArray){
113 | sectionFramesArray = [NSMutableArray array];
114 | _framesArray[section] = sectionFramesArray;
115 | }
116 |
117 | //判断获得前一个cell的frame
118 | NSInteger preRow = row - 1;
119 | if(preRow >= 0 && sectionFramesArray.count > preRow){
120 | CGRect preCellFrame = [sectionFramesArray[preRow] CGRectValue];
121 | x = preCellFrame.origin.x + preCellFrame.size.width + self.minimumInteritemSpacing;
122 | y = preCellFrame.origin.y;
123 | }
124 |
125 | CGFloat currentWidth = [self.delegate waterFlowLayout:self widthAtIndexPath:indexPath];
126 | //保证一个cell不超过最大宽度
127 | currentWidth = MIN(currentWidth, self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right);
128 | if(x + currentWidth > self.collectionView.frame.size.width - self.sectionInset.right){
129 | //超出范围,换行
130 | x = self.sectionInset.left;
131 | y += _rowHeight + self.minimumLineSpacing;
132 | }
133 | // 创建属性
134 | CGRect currentCellFrame = CGRectMake(x, y, currentWidth, _rowHeight);
135 | attrs.frame = currentCellFrame;
136 | sectionFramesArray[row] = [NSValue valueWithCGRect:currentCellFrame];
137 | return attrs;
138 | }
139 |
140 | #pragma mark---other methods
141 | - (CGFloat)maxOrignYInSection:(NSInteger)section
142 | {
143 | __block CGFloat maxY = 0;
144 | if(section >= 0 && _framesArray.count > section){
145 | NSArray *sectionYArray = [_framesArray yl_objectAtIndex:section];
146 | [sectionYArray enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger idx, BOOL * _Nonnull stop) {
147 | CGRect frame = [value CGRectValue];
148 | CGFloat y = frame.origin.y;
149 | if (y > maxY) {
150 | maxY = y;
151 | }
152 | }];
153 | }
154 | return maxY;
155 | }
156 |
157 | @end
158 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/YLWaterFlowLayout.m:
--------------------------------------------------------------------------------
1 | //
2 | // YLWaterFlowLayout.m
3 | // YLLanJiQuan
4 | //
5 | // Created by TK-001289 on 16/5/10.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import "YLWaterFlowLayout.h"
10 | #import "NSArray+YLBoundsCheck.h"
11 | #import
12 |
13 | @interface YLWaterFlowLayout()
14 | @property(nonatomic,strong)NSMutableArray *framesArray;
15 | @property(nonatomic,assign)CGFloat contentHeight;
16 | @end
17 |
18 |
19 | @implementation YLWaterFlowLayout
20 | #pragma mark - 初始化属性
21 | - (instancetype)init {
22 | self = [super init];
23 | if (self) {
24 | [self setupLayout];
25 | _framesArray = [NSMutableArray array];
26 | }
27 | return self;
28 | }
29 |
30 | - (void)setupLayout
31 | {
32 | self.minimumInteritemSpacing = 10;//同一行不同cell间距
33 | self.minimumLineSpacing = 10;//行间距
34 | //support set sectionheader and sectionfooter at the same time
35 | self.headerReferenceSize = CGSizeMake(CGRectGetWidth(self.collectionView.frame), 50);//设置section header 固定高度,如果需要的话
36 | self.footerReferenceSize = CGSizeMake(CGRectGetWidth(self.collectionView.frame), 30);//设置section footer 固定高度,如果需要的话
37 | self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
38 | self.scrollDirection = UICollectionViewScrollDirectionVertical;
39 | }
40 |
41 | #pragma mark - 重写父类的方法,实现瀑布流布局
42 | //step1
43 | - (void)prepareLayout {
44 | [super prepareLayout];
45 | [self calculateFrames];
46 | }
47 |
48 | #pragma mark - CollectionView的滚动范围
49 | //step2
50 | - (CGSize)collectionViewContentSize
51 | {
52 | //support set collectionview's contentInset
53 | CGFloat width = self.collectionView.frame.size.width - self.collectionView.contentInset.left - self.collectionView.contentInset.right;
54 | return CGSizeMake(width, _contentHeight);
55 | }
56 |
57 | #pragma mark - 当尺寸有所变化时,重新刷新
58 | - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
59 | return YES;
60 | }
61 |
62 | #pragma mark - 所有cell和view的布局属性
63 | //sectionheader sectionfooter decorationview collectionviewcell的属性都会走这个方法
64 | //step3
65 | - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
66 | {
67 | NSMutableArray *attributesArray = [NSMutableArray array];
68 | CGPoint offset = self.collectionView.contentOffset;
69 | CGRect visibleRect = CGRectMake(0, offset.y, CGRectGetWidth(self.collectionView.frame), CGRectGetHeight(self.collectionView.frame));
70 | for(NSInteger section = 0; section < _framesArray.count; section++){
71 | CGFloat currentSectionHeight = [self contentHeightInSection:section];
72 | CGFloat preSectionHeight = 0;
73 | if(section > 0){
74 | preSectionHeight = [self contentHeightInSection:section - 1];
75 | }
76 |
77 | NSArray *currentSectionFrames = _framesArray[section];
78 | if(currentSectionFrames.count == 0){
79 | //section header should show
80 | CGFloat y = section == 0 ? self.collectionView.contentInset.top : preSectionHeight;
81 | UICollectionViewLayoutAttributes *ha = [self headerAttri:[NSIndexPath indexPathForRow:0 inSection:section] withY:y];
82 | if(ha){
83 | [attributesArray addObject:ha];
84 | }
85 |
86 | //current section footer should show
87 | NSIndexPath *footerIndexPath = [NSIndexPath indexPathForRow:0 inSection:section];
88 | y = currentSectionHeight - self.footerReferenceSize.height;
89 | UICollectionViewLayoutAttributes *fa = [self footerAttri:footerIndexPath withY:y];
90 | if(fa){
91 | [attributesArray addObject:fa];
92 | }
93 |
94 | if(section > 0 && section + 1 < _framesArray.count &&
95 | y + self.footerReferenceSize.height <= visibleRect.origin.y + visibleRect.size.height){
96 | //next section header should show
97 | UICollectionViewLayoutAttributes *ha = [self headerAttri:[NSIndexPath indexPathForRow:0 inSection:section + 1] withY:y + self.footerReferenceSize.height];
98 | if(ha){
99 | [attributesArray addObject:ha];
100 | }
101 | }
102 | }else{
103 | for(NSInteger row = 0; row < currentSectionFrames.count; row++){
104 | CGRect currentFrame = [currentSectionFrames[row] CGRectValue];
105 | NSIndexPath *currentIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
106 | if(currentFrame.origin.y + currentFrame.size.height >= visibleRect.origin.y &&
107 | currentFrame.origin.y <= visibleRect.origin.y + visibleRect.size.height){
108 | //when first row appear
109 | if(row == 0){
110 | //section header should show
111 | CGFloat y = section == 0 ? self.collectionView.contentInset.top : preSectionHeight;
112 | UICollectionViewLayoutAttributes *fa = [self headerAttri:currentIndexPath withY:y];
113 | if(fa){
114 | [attributesArray addObject:fa];
115 | }
116 |
117 | if(section > 0){
118 | //pre section's footer should show
119 | NSInteger preSection = section - 1;
120 | NSArray *preSectionFrames = _framesArray[preSection];
121 | NSIndexPath *footerIndexPath = [NSIndexPath indexPathForRow:preSectionFrames.count inSection:preSection];
122 | CGFloat y = preSectionHeight - self.footerReferenceSize.height;
123 | UICollectionViewLayoutAttributes *fa = [self footerAttri:footerIndexPath withY:y];
124 | if(fa){
125 | [attributesArray addObject:fa];
126 | }
127 | }
128 | }
129 |
130 | //cells should show
131 | UICollectionViewLayoutAttributes *cellAttrs = [[self layoutAttributesForItemAtIndexPath:currentIndexPath] copy];
132 | cellAttrs.frame = currentFrame;
133 | [attributesArray addObject:cellAttrs];
134 |
135 | //when last cell appear
136 | if(row == currentSectionFrames.count - 1 &&
137 | currentFrame.origin.y + currentFrame.size.height + self.sectionInset.bottom <= visibleRect.origin.y + visibleRect.size.height){
138 | //current section footer should show
139 | NSIndexPath *footerIndexPath = [NSIndexPath indexPathForRow:currentSectionFrames.count inSection:section];
140 | CGFloat y = currentSectionHeight - self.footerReferenceSize.height;
141 | UICollectionViewLayoutAttributes *fa = [self footerAttri:footerIndexPath withY:y];
142 | if(fa){
143 | [attributesArray addObject:fa];
144 | }
145 |
146 | //next section header should show
147 | NSInteger nextSection = section + 1;
148 | if(nextSection < _framesArray.count){
149 | NSIndexPath *headerIndexPath = [NSIndexPath indexPathForRow:0 inSection:nextSection];
150 | UICollectionViewLayoutAttributes *ha = [self headerAttri:headerIndexPath withY:currentSectionHeight];
151 | if(ha){
152 | [attributesArray addObject:ha];
153 | }
154 | }
155 | }
156 | }
157 | }
158 | }
159 | }
160 | return attributesArray;
161 | }
162 |
163 | #pragma mark---other methods
164 | - (UICollectionViewLayoutAttributes *)headerAttri:(NSIndexPath *)indexPath withY:(CGFloat)y
165 | {
166 | UICollectionViewLayoutAttributes *headerAttr = [[self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
167 | atIndexPath:indexPath] copy];
168 | CGRect frame = headerAttr.frame;
169 | frame.origin.y = y;
170 | headerAttr.frame = frame;
171 | return headerAttr;
172 | }
173 |
174 | - (UICollectionViewLayoutAttributes *)footerAttri:(NSIndexPath *)indexPath withY:(CGFloat)y
175 | {
176 | UICollectionViewLayoutAttributes *footerAttr = [[self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter
177 | atIndexPath:indexPath] copy];
178 | CGRect frame = footerAttr.frame;
179 | frame.origin.y = y;
180 | footerAttr.frame = frame;
181 | return footerAttr;
182 | }
183 |
184 | - (void)reCalculateFrames
185 | {
186 | [_framesArray removeAllObjects];
187 | [self calculateFrames];
188 | }
189 |
190 | - (void)calculateFrames
191 | {
192 | if(_framesArray.count > 0){
193 | return;
194 | }
195 |
196 | NSInteger sectionCount = self.collectionView.numberOfSections;
197 | for(NSInteger section = 0; section < sectionCount; section++){
198 | CGFloat preSectionHeight = 0;
199 | if(section > 0){
200 | preSectionHeight = [self contentHeightInSection:section - 1];
201 | }
202 |
203 | NSMutableArray *currentSectionFrames = [NSMutableArray arrayWithCapacity:sectionCount];
204 | [_framesArray addObject:currentSectionFrames];
205 |
206 | NSInteger numberOfRows = [self.collectionView numberOfItemsInSection:section];
207 | for(NSInteger row = 0; row < numberOfRows; row++){
208 | CGFloat x = self.sectionInset.left;
209 | //如果有sectionheader需要加上sectionheader高度
210 | CGFloat y = self.headerReferenceSize.height + self.sectionInset.top + self.collectionView.contentInset.top;
211 |
212 | if(section > 0 && _framesArray.count > section - 1){
213 | y = preSectionHeight + self.headerReferenceSize.height + self.sectionInset.top;
214 | }
215 | //判断获得前一个cell的frame
216 | NSInteger preRow = row - 1;
217 | if(preRow >= 0 && currentSectionFrames.count > preRow){
218 | CGRect preCellFrame = [currentSectionFrames[preRow] CGRectValue];
219 | x = preCellFrame.origin.x + preCellFrame.size.width + self.minimumInteritemSpacing;
220 | y = preCellFrame.origin.y;
221 | }
222 |
223 | NSIndexPath *currentIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
224 | CGFloat currentWidth = [self.delegate waterFlowLayout:self widthAtIndexPath:currentIndexPath];
225 | //保证一个cell不超过最大宽度
226 | CGFloat maxCellWidth = self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right - self.collectionView.contentInset.left - self.collectionView.contentInset.right;
227 | currentWidth = MIN(currentWidth, maxCellWidth);
228 | if(x + currentWidth > self.collectionView.frame.size.width - self.sectionInset.right - self.collectionView.contentInset.right){
229 | //超出范围,换行
230 | x = self.sectionInset.left;
231 | y += _rowHeight + self.minimumLineSpacing;
232 | }
233 | // 创建属性
234 | CGRect currentCellFrame = CGRectMake(x, y, currentWidth, _rowHeight);
235 | [currentSectionFrames addObject:[NSValue valueWithCGRect:currentCellFrame]];
236 | }
237 | }
238 | _contentHeight = [self contentHeightInSection:_framesArray.count - 1] + self.collectionView.contentInset.bottom;
239 | }
240 |
241 | - (CGFloat)contentHeightInSection:(NSInteger)section
242 | {
243 | __block CGFloat height = 0;
244 | if(section >= 0 && _framesArray.count > section){
245 | NSArray *sectionYArray = [_framesArray objectAtIndex:section];
246 | if(sectionYArray.count == 0){
247 | height = self.headerReferenceSize.height + self.sectionInset.top + self.sectionInset.bottom + self.footerReferenceSize.height;
248 | if(section == 0){
249 | height += self.collectionView.contentInset.top;
250 | }else{
251 | height += [self contentHeightInSection:section - 1];
252 | }
253 | }else{
254 | [sectionYArray enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger idx, BOOL * _Nonnull stop) {
255 | CGRect frame = [value CGRectValue];
256 | CGFloat y = frame.origin.y;
257 | if (y > height) {
258 | height = y;
259 | }
260 | }];
261 | height += _rowHeight + self.sectionInset.bottom + self.footerReferenceSize.height;
262 | }
263 | }
264 | return height;
265 | }
266 |
267 | @end
268 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser/TagsChooser/YLTagsChooser.m:
--------------------------------------------------------------------------------
1 | //
2 | // YLTagsChooser.m
3 | // YLTagsChooser
4 | //
5 | // Created by TK-001289 on 16/6/13.
6 | // Copyright © 2016年 YL. All rights reserved.
7 | //
8 |
9 | #import "YLTagsChooser.h"
10 | #import "NSArray+YLBoundsCheck.h"
11 | #import "YLWaterFlowLayout.h"
12 | #import "YLCollectionReusableView.h"
13 | #import "YLTag.h"
14 |
15 | #define HEXCOLOR(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
16 |
17 | // 屏幕尺寸
18 | #define kFrameWidth [UIScreen mainScreen].bounds.size.width
19 | #define kFrameHeight [UIScreen mainScreen].bounds.size.height
20 |
21 | static NSString *headerIdentifier = @"YLCollectionReusableView";
22 | static NSString *footerIdentifier = @"YLCollectionReusableView";
23 | static NSString *cellIdentifier = @"YLTagsCollectionViewCell";
24 |
25 | static NSTimeInterval const kSheetAnimationDuration = 0.25;
26 | static CGFloat const kBottomBtnHeight = 44.f;
27 | static CGFloat const kBottomGap = 24.f;
28 | static CGFloat const kYGap = 10.f;
29 |
30 | @interface YLTagsChooser()
31 | {
32 |
33 | }
34 | @property (nonatomic,strong) UIView *bottomView;
35 | @property (nonatomic,strong) UICollectionView *myCollectionView;
36 | @property (nonatomic,strong) UIButton *ensureBtn;
37 | @property (nonatomic,strong) NSMutableArray *orignalTags;
38 | @property (nonatomic,strong) NSMutableArray *selectedTags;
39 |
40 | @end
41 |
42 |
43 | @implementation YLTagsChooser
44 | -(instancetype)initWithBottomHeight:(CGFloat)bHeight
45 | maxSelectCount:(CGFloat)maxCount
46 | delegate:(id)aDelegate
47 | {
48 | if(self = [super initWithFrame:CGRectMake(0, 0, kFrameWidth, kFrameHeight)]){
49 | _orignalTags = [NSMutableArray array];
50 | _selectedTags = [NSMutableArray array];
51 | self.alpha = 0.f;
52 | self.backgroundColor = [[UIColor blackColor]colorWithAlphaComponent:0.5];
53 |
54 | self.bottomHeight = bHeight;
55 | self.maxSelectCount = maxCount;
56 | self.delegate = aDelegate;
57 |
58 | [self.bottomView addSubview:self.myCollectionView];
59 | [self.bottomView addSubview:self.ensureBtn];
60 | [self addSubview:self.bottomView];
61 | }
62 | return self;
63 | }
64 |
65 | -(void)refreshWithTags:(NSArray *)tags selectedTags:(NSArray *)selectedTags
66 | {
67 | [_orignalTags removeAllObjects];
68 | [_orignalTags addObjectsFromArray:tags];
69 |
70 | [_selectedTags removeAllObjects];
71 | [_selectedTags addObjectsFromArray:selectedTags];
72 |
73 | for(NSArray *array in _orignalTags){
74 | for(YLTag *tag in array){
75 | tag.selected = [_selectedTags containsObject:tag];
76 | }
77 | }
78 | [self.myCollectionView reloadData];
79 | }
80 |
81 | -(UIView *)bottomView
82 | {
83 | if(!_bottomView){
84 | _bottomView = [[UIView alloc]init];
85 | _bottomView.backgroundColor = [UIColor groupTableViewBackgroundColor];
86 | _bottomView.frame = CGRectMake(0, kFrameHeight, kFrameWidth, _bottomHeight);
87 | }
88 | return _bottomView;
89 | }
90 |
91 | -(UICollectionView *)myCollectionView
92 | {
93 | if(!_myCollectionView){
94 | YLWaterFlowLayout *layout = [[YLWaterFlowLayout alloc]init];
95 | layout.rowHeight = 30.f;
96 | layout.delegate = self;
97 |
98 | _myCollectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, kYGap, kFrameWidth, _bottomHeight - 2 * kYGap - kBottomGap - kBottomBtnHeight)
99 | collectionViewLayout:layout];
100 | _myCollectionView.backgroundColor = [UIColor clearColor];
101 | [_myCollectionView registerClass:[YLCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerIdentifier];
102 | [_myCollectionView registerClass:[YLCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:footerIdentifier];
103 | [_myCollectionView registerClass:[YLTagsCollectionViewCell class] forCellWithReuseIdentifier:cellIdentifier];
104 | _myCollectionView.dataSource = self;
105 | _myCollectionView.delegate = self;
106 | _myCollectionView.showsHorizontalScrollIndicator = NO;
107 | //support set collectionview's contentInset
108 | // _myCollectionView.contentInset = UIEdgeInsetsMake(30, 20, 30, 20);
109 | }
110 | return _myCollectionView;
111 | }
112 |
113 | -(UIButton *)ensureBtn
114 | {
115 | if(!_ensureBtn){
116 | _ensureBtn = [UIButton buttonWithType:UIButtonTypeCustom];
117 | _ensureBtn.backgroundColor = HEXCOLOR(0x25c5b6);
118 | _ensureBtn.layer.cornerRadius = 5;
119 | _ensureBtn.layer.masksToBounds = YES;
120 | _ensureBtn.titleLabel.font = [UIFont systemFontOfSize:16];
121 | [_ensureBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
122 | [_ensureBtn setTitleColor:[[UIColor whiteColor]colorWithAlphaComponent:0.6] forState:UIControlStateHighlighted];
123 | [_ensureBtn setTitle:@"确定" forState:UIControlStateNormal];
124 | [_ensureBtn addTarget:self action:@selector(ensureAction) forControlEvents:UIControlEventTouchUpInside];
125 | _ensureBtn.frame = CGRectMake(10, _bottomHeight - kBottomGap - kBottomBtnHeight, kFrameWidth - 20, kBottomBtnHeight);
126 | }
127 | return _ensureBtn;
128 | }
129 |
130 | #pragma mark---UICollectionViewDataSource
131 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
132 | {
133 | return _orignalTags.count;
134 | }
135 |
136 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
137 | {
138 | NSArray *sectionData = [_orignalTags yl_objectAtIndex:section];
139 | if([sectionData isKindOfClass:[NSArray class]]){
140 | return sectionData.count;
141 | }
142 | return 0;
143 | }
144 |
145 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
146 | {
147 | YLTagsCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier
148 | forIndexPath:indexPath];
149 | NSArray *sectionData = [_orignalTags yl_objectAtIndex:indexPath.section];
150 | YLTag *tag = [sectionData yl_objectAtIndex:indexPath.row];
151 | [cell refreshWithObject:tag];
152 | return cell;
153 | }
154 |
155 | - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
156 | {
157 | if([kind isEqualToString:UICollectionElementKindSectionHeader]){
158 | YLCollectionReusableView *header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerIdentifier forIndexPath:indexPath];
159 | [header setTitle:[NSString stringWithFormat:@"Section Header %li",(long)indexPath.section]];
160 | return header;
161 | }else{
162 | YLCollectionReusableView *footer = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:footerIdentifier forIndexPath:indexPath];
163 | [footer setTitle:[NSString stringWithFormat:@"Section Footer %li",(long)indexPath.section]];
164 | return footer;
165 | }
166 | }
167 |
168 | #pragma mark---YLWaterFlowLayoutDelegate
169 | - (CGFloat)waterFlowLayout:(YLWaterFlowLayout *)layout widthAtIndexPath:(NSIndexPath *)indexPath
170 | {
171 | NSArray *sectionData = [_orignalTags yl_objectAtIndex:indexPath.section];
172 | YLTag *tag = [sectionData yl_objectAtIndex:indexPath.row];
173 | CGSize size = CGSizeMake(kFrameWidth - layout.sectionInset.left - layout.sectionInset.right,CGFLOAT_MAX);
174 | CGRect textRect = [tag.name
175 | boundingRectWithSize:size
176 | options:NSStringDrawingUsesLineFragmentOrigin
177 | attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:14]}
178 | context:nil];
179 | CGFloat width = textRect.size.width + 15;
180 | return width;
181 | }
182 |
183 | #pragma mark---UICollectionViewDelegate
184 | - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath
185 | {
186 | return YES;
187 | }
188 |
189 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
190 | {
191 | NSArray *sectionData = [_orignalTags yl_objectAtIndex:indexPath.section];
192 | YLTag *tag = [sectionData yl_objectAtIndex:indexPath.row];
193 | if(![_selectedTags containsObject:tag]){
194 | if(_selectedTags.count >= _maxSelectCount){
195 | //提示用户
196 | UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示"
197 | message:[NSString stringWithFormat:@"最多选择%li个",(long)_maxSelectCount]
198 | delegate:nil
199 | cancelButtonTitle:@"确定"
200 | otherButtonTitles:nil, nil];
201 | [alert show];
202 | }else{
203 | tag.selected = YES;
204 | [_selectedTags addObject:tag];
205 | }
206 | }else{
207 | tag.selected = NO;
208 | [_selectedTags removeObject:tag];
209 | }
210 |
211 | [collectionView reloadData];
212 | //!!!!:Don't use this method! Should be optimized
213 | // [collectionView reloadItemsAtIndexPaths:@[indexPath]];
214 | }
215 |
216 | #pragma mark---touch
217 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
218 | {
219 | [super touchesBegan:touches withEvent:event];
220 | UITouch *lastTouch = [touches anyObject];
221 | CGPoint point = [lastTouch locationInView:self];
222 | if(!CGRectContainsPoint(self.bottomView.frame, point)){
223 | [self dismiss];
224 | }
225 | }
226 |
227 | #pragma mark---animation method
228 | - (void)showInView:(UIView *)view
229 | {
230 | [view addSubview:self];
231 |
232 | CGRect frame = self.bottomView.frame;
233 | frame.origin.y = kFrameHeight - _bottomHeight;
234 | [UIView animateWithDuration:kSheetAnimationDuration
235 | delay:0.f
236 | options:UIViewAnimationOptionCurveEaseInOut
237 | animations:^{
238 | self.bottomView.frame = frame;
239 | self.alpha = 1.f;
240 | } completion:^(BOOL finished) {
241 |
242 | }];
243 | }
244 |
245 | - (void)dismiss
246 | {
247 | CGRect frame = self.bottomView.frame;
248 | frame.origin.y = kFrameHeight;
249 | [UIView animateWithDuration:kSheetAnimationDuration
250 | delay:0.f
251 | options:UIViewAnimationOptionCurveEaseInOut
252 | animations:^{
253 | self.bottomView.frame = frame;
254 | self.alpha = 0;
255 | } completion:^(BOOL finished) {
256 | [self removeFromSuperview];
257 | }];
258 | }
259 |
260 |
261 | #pragma mark---other methods
262 | -(void)ensureAction
263 | {
264 | if([_delegate respondsToSelector:@selector(tagsChooser:selectedTags:)]){
265 | [_delegate tagsChooser:self selectedTags:_selectedTags];
266 | }
267 | [self dismiss];
268 | }
269 |
270 | @end
271 |
272 | #pragma mark---标签cell
273 | @implementation YLTagsCollectionViewCell
274 | -(instancetype)initWithFrame:(CGRect)frame
275 | {
276 | if(self = [super initWithFrame:frame]){
277 | self.backgroundColor = [UIColor clearColor];
278 | _textLabel = [[UILabel alloc]init];
279 | //此处可以根据需要自己使用自动布局代码实现
280 | _textLabel.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
281 | _textLabel.backgroundColor = [UIColor whiteColor];
282 | _textLabel.font = [UIFont systemFontOfSize:14];
283 | _textLabel.layer.borderWidth = 1.f;
284 | _textLabel.layer.cornerRadius = frame.size.height * 0.5;
285 | _textLabel.layer.masksToBounds = YES;
286 | _textLabel.textColor = HEXCOLOR(0x666666);
287 | _textLabel.textAlignment = NSTextAlignmentCenter;
288 | _textLabel.layer.borderColor = HEXCOLOR(0xdddddd).CGColor;
289 | [self.contentView addSubview:_textLabel];
290 | }
291 | return self;
292 | }
293 |
294 | - (void)refreshWithObject:(NSObject *)obj
295 | {
296 | if([obj isKindOfClass:[YLTag class]]){
297 | YLTag *tag = (YLTag *)obj;
298 | UIColor *borderColor = tag.selected ? HEXCOLOR(0xffb400) : HEXCOLOR(0xdddddd);
299 | UIColor *titleColor = tag.selected ? HEXCOLOR(0xffb400) : HEXCOLOR(0x666666);
300 | _textLabel.layer.borderColor = borderColor.CGColor;
301 | _textLabel.textColor = titleColor;
302 | _textLabel.text = tag.name;
303 | }
304 | }
305 |
306 | -(void)layoutSubviews
307 | {
308 | [super layoutSubviews];
309 | _textLabel.frame = self.bounds;
310 | }
311 |
312 | -(void)setSelected:(BOOL)selected
313 | {
314 | [super setSelected:selected];
315 | UIColor *borderColor = selected ? HEXCOLOR(0xffb400) : HEXCOLOR(0xdddddd);
316 | UIColor *titleColor = selected ? HEXCOLOR(0xffb400) : HEXCOLOR(0x666666);
317 | _textLabel.layer.borderColor = borderColor.CGColor;
318 | _textLabel.textColor = titleColor;
319 | }
320 |
321 | -(void)setHighlighted:(BOOL)highlighted
322 | {
323 | [super setHighlighted:highlighted];
324 | UIColor *borderColor = highlighted ? HEXCOLOR(0xffb400) : HEXCOLOR(0xdddddd);
325 | UIColor *titleColor = highlighted ? HEXCOLOR(0xffb400) : HEXCOLOR(0x666666);
326 | _textLabel.layer.borderColor = borderColor.CGColor;
327 | _textLabel.textColor = titleColor;
328 | }
329 |
330 | @end
331 |
--------------------------------------------------------------------------------
/YLTagsChooser/YLTagsChooser.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 457042321D0E580400128041 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 457042311D0E580400128041 /* main.m */; };
11 | 457042351D0E580400128041 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 457042341D0E580400128041 /* AppDelegate.m */; };
12 | 457042381D0E580400128041 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 457042371D0E580400128041 /* ViewController.m */; };
13 | 4570423B1D0E580400128041 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 457042391D0E580400128041 /* Main.storyboard */; };
14 | 4570423D1D0E580400128041 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4570423C1D0E580400128041 /* Assets.xcassets */; };
15 | 457042401D0E580400128041 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4570423E1D0E580400128041 /* LaunchScreen.storyboard */; };
16 | 4570424B1D0E580400128041 /* YLTagsChooserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4570424A1D0E580400128041 /* YLTagsChooserTests.m */; };
17 | 457042561D0E580400128041 /* YLTagsChooserUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 457042551D0E580400128041 /* YLTagsChooserUITests.m */; };
18 | 457042771D0E62B900128041 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 457042761D0E62B900128041 /* CoreGraphics.framework */; };
19 | 457042791D0E633300128041 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 457042781D0E633300128041 /* UIKit.framework */; };
20 | 6D3855B62023503900A15073 /* NSArray+YLBoundsCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D3855AD2023503900A15073 /* NSArray+YLBoundsCheck.m */; };
21 | 6D3855B72023503900A15073 /* YLCollectionReusableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D3855AF2023503900A15073 /* YLCollectionReusableView.m */; };
22 | 6D3855B82023503900A15073 /* YLTag.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D3855B12023503900A15073 /* YLTag.m */; };
23 | 6D3855B92023503900A15073 /* YLTagsChooser.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D3855B32023503900A15073 /* YLTagsChooser.m */; };
24 | 6D3855BA2023503900A15073 /* YLWaterFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 6D3855B52023503900A15073 /* YLWaterFlowLayout.m */; };
25 | /* End PBXBuildFile section */
26 |
27 | /* Begin PBXContainerItemProxy section */
28 | 457042471D0E580400128041 /* PBXContainerItemProxy */ = {
29 | isa = PBXContainerItemProxy;
30 | containerPortal = 457042251D0E580400128041 /* Project object */;
31 | proxyType = 1;
32 | remoteGlobalIDString = 4570422C1D0E580400128041;
33 | remoteInfo = YLTagsChooser;
34 | };
35 | 457042521D0E580400128041 /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = 457042251D0E580400128041 /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = 4570422C1D0E580400128041;
40 | remoteInfo = YLTagsChooser;
41 | };
42 | /* End PBXContainerItemProxy section */
43 |
44 | /* Begin PBXFileReference section */
45 | 456324F41EF239DA000AD9AF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; };
46 | 456324F51EF239DA000AD9AF /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.strings"; sourceTree = ""; };
47 | 4570422D1D0E580400128041 /* YLTagsChooser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YLTagsChooser.app; sourceTree = BUILT_PRODUCTS_DIR; };
48 | 457042311D0E580400128041 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
49 | 457042331D0E580400128041 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
50 | 457042341D0E580400128041 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
51 | 457042361D0E580400128041 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
52 | 457042371D0E580400128041 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
53 | 4570423A1D0E580400128041 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
54 | 4570423C1D0E580400128041 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
55 | 4570423F1D0E580400128041 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
56 | 457042411D0E580400128041 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
57 | 457042461D0E580400128041 /* YLTagsChooserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YLTagsChooserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
58 | 4570424A1D0E580400128041 /* YLTagsChooserTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YLTagsChooserTests.m; sourceTree = ""; };
59 | 4570424C1D0E580400128041 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
60 | 457042511D0E580400128041 /* YLTagsChooserUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YLTagsChooserUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
61 | 457042551D0E580400128041 /* YLTagsChooserUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YLTagsChooserUITests.m; sourceTree = ""; };
62 | 457042571D0E580400128041 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
63 | 457042761D0E62B900128041 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
64 | 457042781D0E633300128041 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
65 | 6D3855AC2023503900A15073 /* NSArray+YLBoundsCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+YLBoundsCheck.h"; sourceTree = ""; };
66 | 6D3855AD2023503900A15073 /* NSArray+YLBoundsCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+YLBoundsCheck.m"; sourceTree = ""; };
67 | 6D3855AE2023503900A15073 /* YLCollectionReusableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLCollectionReusableView.h; sourceTree = ""; };
68 | 6D3855AF2023503900A15073 /* YLCollectionReusableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLCollectionReusableView.m; sourceTree = ""; };
69 | 6D3855B02023503900A15073 /* YLTag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTag.h; sourceTree = ""; };
70 | 6D3855B12023503900A15073 /* YLTag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTag.m; sourceTree = ""; };
71 | 6D3855B22023503900A15073 /* YLTagsChooser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLTagsChooser.h; sourceTree = ""; };
72 | 6D3855B32023503900A15073 /* YLTagsChooser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLTagsChooser.m; sourceTree = ""; };
73 | 6D3855B42023503900A15073 /* YLWaterFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YLWaterFlowLayout.h; sourceTree = ""; };
74 | 6D3855B52023503900A15073 /* YLWaterFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YLWaterFlowLayout.m; sourceTree = ""; };
75 | /* End PBXFileReference section */
76 |
77 | /* Begin PBXFrameworksBuildPhase section */
78 | 4570422A1D0E580400128041 /* Frameworks */ = {
79 | isa = PBXFrameworksBuildPhase;
80 | buildActionMask = 2147483647;
81 | files = (
82 | 457042791D0E633300128041 /* UIKit.framework in Frameworks */,
83 | 457042771D0E62B900128041 /* CoreGraphics.framework in Frameworks */,
84 | );
85 | runOnlyForDeploymentPostprocessing = 0;
86 | };
87 | 457042431D0E580400128041 /* Frameworks */ = {
88 | isa = PBXFrameworksBuildPhase;
89 | buildActionMask = 2147483647;
90 | files = (
91 | );
92 | runOnlyForDeploymentPostprocessing = 0;
93 | };
94 | 4570424E1D0E580400128041 /* Frameworks */ = {
95 | isa = PBXFrameworksBuildPhase;
96 | buildActionMask = 2147483647;
97 | files = (
98 | );
99 | runOnlyForDeploymentPostprocessing = 0;
100 | };
101 | /* End PBXFrameworksBuildPhase section */
102 |
103 | /* Begin PBXGroup section */
104 | 457042241D0E580400128041 = {
105 | isa = PBXGroup;
106 | children = (
107 | 457042781D0E633300128041 /* UIKit.framework */,
108 | 457042761D0E62B900128041 /* CoreGraphics.framework */,
109 | 4570422F1D0E580400128041 /* YLTagsChooser */,
110 | 457042491D0E580400128041 /* YLTagsChooserTests */,
111 | 457042541D0E580400128041 /* YLTagsChooserUITests */,
112 | 4570422E1D0E580400128041 /* Products */,
113 | );
114 | sourceTree = "";
115 | };
116 | 4570422E1D0E580400128041 /* Products */ = {
117 | isa = PBXGroup;
118 | children = (
119 | 4570422D1D0E580400128041 /* YLTagsChooser.app */,
120 | 457042461D0E580400128041 /* YLTagsChooserTests.xctest */,
121 | 457042511D0E580400128041 /* YLTagsChooserUITests.xctest */,
122 | );
123 | name = Products;
124 | sourceTree = "";
125 | };
126 | 4570422F1D0E580400128041 /* YLTagsChooser */ = {
127 | isa = PBXGroup;
128 | children = (
129 | 6D3855AB2023503900A15073 /* TagsChooser */,
130 | 457042331D0E580400128041 /* AppDelegate.h */,
131 | 457042341D0E580400128041 /* AppDelegate.m */,
132 | 457042361D0E580400128041 /* ViewController.h */,
133 | 457042371D0E580400128041 /* ViewController.m */,
134 | 457042391D0E580400128041 /* Main.storyboard */,
135 | 4570423C1D0E580400128041 /* Assets.xcassets */,
136 | 4570423E1D0E580400128041 /* LaunchScreen.storyboard */,
137 | 457042411D0E580400128041 /* Info.plist */,
138 | 457042301D0E580400128041 /* Supporting Files */,
139 | );
140 | path = YLTagsChooser;
141 | sourceTree = "";
142 | };
143 | 457042301D0E580400128041 /* Supporting Files */ = {
144 | isa = PBXGroup;
145 | children = (
146 | 457042311D0E580400128041 /* main.m */,
147 | );
148 | name = "Supporting Files";
149 | sourceTree = "";
150 | };
151 | 457042491D0E580400128041 /* YLTagsChooserTests */ = {
152 | isa = PBXGroup;
153 | children = (
154 | 4570424A1D0E580400128041 /* YLTagsChooserTests.m */,
155 | 4570424C1D0E580400128041 /* Info.plist */,
156 | );
157 | path = YLTagsChooserTests;
158 | sourceTree = "";
159 | };
160 | 457042541D0E580400128041 /* YLTagsChooserUITests */ = {
161 | isa = PBXGroup;
162 | children = (
163 | 457042551D0E580400128041 /* YLTagsChooserUITests.m */,
164 | 457042571D0E580400128041 /* Info.plist */,
165 | );
166 | path = YLTagsChooserUITests;
167 | sourceTree = "";
168 | };
169 | 6D3855AB2023503900A15073 /* TagsChooser */ = {
170 | isa = PBXGroup;
171 | children = (
172 | 6D3855AC2023503900A15073 /* NSArray+YLBoundsCheck.h */,
173 | 6D3855AD2023503900A15073 /* NSArray+YLBoundsCheck.m */,
174 | 6D3855AE2023503900A15073 /* YLCollectionReusableView.h */,
175 | 6D3855AF2023503900A15073 /* YLCollectionReusableView.m */,
176 | 6D3855B02023503900A15073 /* YLTag.h */,
177 | 6D3855B12023503900A15073 /* YLTag.m */,
178 | 6D3855B22023503900A15073 /* YLTagsChooser.h */,
179 | 6D3855B32023503900A15073 /* YLTagsChooser.m */,
180 | 6D3855B42023503900A15073 /* YLWaterFlowLayout.h */,
181 | 6D3855B52023503900A15073 /* YLWaterFlowLayout.m */,
182 | );
183 | path = TagsChooser;
184 | sourceTree = "";
185 | };
186 | /* End PBXGroup section */
187 |
188 | /* Begin PBXNativeTarget section */
189 | 4570422C1D0E580400128041 /* YLTagsChooser */ = {
190 | isa = PBXNativeTarget;
191 | buildConfigurationList = 4570425A1D0E580400128041 /* Build configuration list for PBXNativeTarget "YLTagsChooser" */;
192 | buildPhases = (
193 | 457042291D0E580400128041 /* Sources */,
194 | 4570422A1D0E580400128041 /* Frameworks */,
195 | 4570422B1D0E580400128041 /* Resources */,
196 | );
197 | buildRules = (
198 | );
199 | dependencies = (
200 | );
201 | name = YLTagsChooser;
202 | productName = YLTagsChooser;
203 | productReference = 4570422D1D0E580400128041 /* YLTagsChooser.app */;
204 | productType = "com.apple.product-type.application";
205 | };
206 | 457042451D0E580400128041 /* YLTagsChooserTests */ = {
207 | isa = PBXNativeTarget;
208 | buildConfigurationList = 4570425D1D0E580400128041 /* Build configuration list for PBXNativeTarget "YLTagsChooserTests" */;
209 | buildPhases = (
210 | 457042421D0E580400128041 /* Sources */,
211 | 457042431D0E580400128041 /* Frameworks */,
212 | 457042441D0E580400128041 /* Resources */,
213 | );
214 | buildRules = (
215 | );
216 | dependencies = (
217 | 457042481D0E580400128041 /* PBXTargetDependency */,
218 | );
219 | name = YLTagsChooserTests;
220 | productName = YLTagsChooserTests;
221 | productReference = 457042461D0E580400128041 /* YLTagsChooserTests.xctest */;
222 | productType = "com.apple.product-type.bundle.unit-test";
223 | };
224 | 457042501D0E580400128041 /* YLTagsChooserUITests */ = {
225 | isa = PBXNativeTarget;
226 | buildConfigurationList = 457042601D0E580400128041 /* Build configuration list for PBXNativeTarget "YLTagsChooserUITests" */;
227 | buildPhases = (
228 | 4570424D1D0E580400128041 /* Sources */,
229 | 4570424E1D0E580400128041 /* Frameworks */,
230 | 4570424F1D0E580400128041 /* Resources */,
231 | );
232 | buildRules = (
233 | );
234 | dependencies = (
235 | 457042531D0E580400128041 /* PBXTargetDependency */,
236 | );
237 | name = YLTagsChooserUITests;
238 | productName = YLTagsChooserUITests;
239 | productReference = 457042511D0E580400128041 /* YLTagsChooserUITests.xctest */;
240 | productType = "com.apple.product-type.bundle.ui-testing";
241 | };
242 | /* End PBXNativeTarget section */
243 |
244 | /* Begin PBXProject section */
245 | 457042251D0E580400128041 /* Project object */ = {
246 | isa = PBXProject;
247 | attributes = {
248 | LastUpgradeCheck = 0730;
249 | ORGANIZATIONNAME = YL;
250 | TargetAttributes = {
251 | 4570422C1D0E580400128041 = {
252 | CreatedOnToolsVersion = 7.3.1;
253 | DevelopmentTeam = M3Y7JKHK66;
254 | };
255 | 457042451D0E580400128041 = {
256 | CreatedOnToolsVersion = 7.3.1;
257 | TestTargetID = 4570422C1D0E580400128041;
258 | };
259 | 457042501D0E580400128041 = {
260 | CreatedOnToolsVersion = 7.3.1;
261 | TestTargetID = 4570422C1D0E580400128041;
262 | };
263 | };
264 | };
265 | buildConfigurationList = 457042281D0E580400128041 /* Build configuration list for PBXProject "YLTagsChooser" */;
266 | compatibilityVersion = "Xcode 3.2";
267 | developmentRegion = English;
268 | hasScannedForEncodings = 0;
269 | knownRegions = (
270 | English,
271 | en,
272 | Base,
273 | "zh-Hans",
274 | );
275 | mainGroup = 457042241D0E580400128041;
276 | productRefGroup = 4570422E1D0E580400128041 /* Products */;
277 | projectDirPath = "";
278 | projectRoot = "";
279 | targets = (
280 | 4570422C1D0E580400128041 /* YLTagsChooser */,
281 | 457042451D0E580400128041 /* YLTagsChooserTests */,
282 | 457042501D0E580400128041 /* YLTagsChooserUITests */,
283 | );
284 | };
285 | /* End PBXProject section */
286 |
287 | /* Begin PBXResourcesBuildPhase section */
288 | 4570422B1D0E580400128041 /* Resources */ = {
289 | isa = PBXResourcesBuildPhase;
290 | buildActionMask = 2147483647;
291 | files = (
292 | 457042401D0E580400128041 /* LaunchScreen.storyboard in Resources */,
293 | 4570423D1D0E580400128041 /* Assets.xcassets in Resources */,
294 | 4570423B1D0E580400128041 /* Main.storyboard in Resources */,
295 | );
296 | runOnlyForDeploymentPostprocessing = 0;
297 | };
298 | 457042441D0E580400128041 /* Resources */ = {
299 | isa = PBXResourcesBuildPhase;
300 | buildActionMask = 2147483647;
301 | files = (
302 | );
303 | runOnlyForDeploymentPostprocessing = 0;
304 | };
305 | 4570424F1D0E580400128041 /* Resources */ = {
306 | isa = PBXResourcesBuildPhase;
307 | buildActionMask = 2147483647;
308 | files = (
309 | );
310 | runOnlyForDeploymentPostprocessing = 0;
311 | };
312 | /* End PBXResourcesBuildPhase section */
313 |
314 | /* Begin PBXSourcesBuildPhase section */
315 | 457042291D0E580400128041 /* Sources */ = {
316 | isa = PBXSourcesBuildPhase;
317 | buildActionMask = 2147483647;
318 | files = (
319 | 457042381D0E580400128041 /* ViewController.m in Sources */,
320 | 457042351D0E580400128041 /* AppDelegate.m in Sources */,
321 | 457042321D0E580400128041 /* main.m in Sources */,
322 | 6D3855B82023503900A15073 /* YLTag.m in Sources */,
323 | 6D3855BA2023503900A15073 /* YLWaterFlowLayout.m in Sources */,
324 | 6D3855B62023503900A15073 /* NSArray+YLBoundsCheck.m in Sources */,
325 | 6D3855B72023503900A15073 /* YLCollectionReusableView.m in Sources */,
326 | 6D3855B92023503900A15073 /* YLTagsChooser.m in Sources */,
327 | );
328 | runOnlyForDeploymentPostprocessing = 0;
329 | };
330 | 457042421D0E580400128041 /* Sources */ = {
331 | isa = PBXSourcesBuildPhase;
332 | buildActionMask = 2147483647;
333 | files = (
334 | 4570424B1D0E580400128041 /* YLTagsChooserTests.m in Sources */,
335 | );
336 | runOnlyForDeploymentPostprocessing = 0;
337 | };
338 | 4570424D1D0E580400128041 /* Sources */ = {
339 | isa = PBXSourcesBuildPhase;
340 | buildActionMask = 2147483647;
341 | files = (
342 | 457042561D0E580400128041 /* YLTagsChooserUITests.m in Sources */,
343 | );
344 | runOnlyForDeploymentPostprocessing = 0;
345 | };
346 | /* End PBXSourcesBuildPhase section */
347 |
348 | /* Begin PBXTargetDependency section */
349 | 457042481D0E580400128041 /* PBXTargetDependency */ = {
350 | isa = PBXTargetDependency;
351 | target = 4570422C1D0E580400128041 /* YLTagsChooser */;
352 | targetProxy = 457042471D0E580400128041 /* PBXContainerItemProxy */;
353 | };
354 | 457042531D0E580400128041 /* PBXTargetDependency */ = {
355 | isa = PBXTargetDependency;
356 | target = 4570422C1D0E580400128041 /* YLTagsChooser */;
357 | targetProxy = 457042521D0E580400128041 /* PBXContainerItemProxy */;
358 | };
359 | /* End PBXTargetDependency section */
360 |
361 | /* Begin PBXVariantGroup section */
362 | 457042391D0E580400128041 /* Main.storyboard */ = {
363 | isa = PBXVariantGroup;
364 | children = (
365 | 4570423A1D0E580400128041 /* Base */,
366 | 456324F41EF239DA000AD9AF /* zh-Hans */,
367 | );
368 | name = Main.storyboard;
369 | sourceTree = "";
370 | };
371 | 4570423E1D0E580400128041 /* LaunchScreen.storyboard */ = {
372 | isa = PBXVariantGroup;
373 | children = (
374 | 4570423F1D0E580400128041 /* Base */,
375 | 456324F51EF239DA000AD9AF /* zh-Hans */,
376 | );
377 | name = LaunchScreen.storyboard;
378 | sourceTree = "";
379 | };
380 | /* End PBXVariantGroup section */
381 |
382 | /* Begin XCBuildConfiguration section */
383 | 457042581D0E580400128041 /* Debug */ = {
384 | isa = XCBuildConfiguration;
385 | buildSettings = {
386 | ALWAYS_SEARCH_USER_PATHS = NO;
387 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
388 | CLANG_ANALYZER_NONNULL = YES;
389 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
390 | CLANG_CXX_LIBRARY = "libc++";
391 | CLANG_ENABLE_MODULES = YES;
392 | CLANG_ENABLE_OBJC_ARC = YES;
393 | CLANG_WARN_BOOL_CONVERSION = YES;
394 | CLANG_WARN_CONSTANT_CONVERSION = YES;
395 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
396 | CLANG_WARN_EMPTY_BODY = YES;
397 | CLANG_WARN_ENUM_CONVERSION = YES;
398 | CLANG_WARN_INT_CONVERSION = YES;
399 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
400 | CLANG_WARN_UNREACHABLE_CODE = YES;
401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
402 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
403 | COPY_PHASE_STRIP = NO;
404 | DEBUG_INFORMATION_FORMAT = dwarf;
405 | ENABLE_STRICT_OBJC_MSGSEND = YES;
406 | ENABLE_TESTABILITY = YES;
407 | GCC_C_LANGUAGE_STANDARD = gnu99;
408 | GCC_DYNAMIC_NO_PIC = NO;
409 | GCC_NO_COMMON_BLOCKS = YES;
410 | GCC_OPTIMIZATION_LEVEL = 0;
411 | GCC_PREPROCESSOR_DEFINITIONS = (
412 | "DEBUG=1",
413 | "$(inherited)",
414 | );
415 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
416 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
417 | GCC_WARN_UNDECLARED_SELECTOR = YES;
418 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
419 | GCC_WARN_UNUSED_FUNCTION = YES;
420 | GCC_WARN_UNUSED_VARIABLE = YES;
421 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
422 | MTL_ENABLE_DEBUG_INFO = YES;
423 | ONLY_ACTIVE_ARCH = YES;
424 | SDKROOT = iphoneos;
425 | TARGETED_DEVICE_FAMILY = "1,2";
426 | };
427 | name = Debug;
428 | };
429 | 457042591D0E580400128041 /* Release */ = {
430 | isa = XCBuildConfiguration;
431 | buildSettings = {
432 | ALWAYS_SEARCH_USER_PATHS = NO;
433 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
434 | CLANG_ANALYZER_NONNULL = YES;
435 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
436 | CLANG_CXX_LIBRARY = "libc++";
437 | CLANG_ENABLE_MODULES = YES;
438 | CLANG_ENABLE_OBJC_ARC = YES;
439 | CLANG_WARN_BOOL_CONVERSION = YES;
440 | CLANG_WARN_CONSTANT_CONVERSION = YES;
441 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
442 | CLANG_WARN_EMPTY_BODY = YES;
443 | CLANG_WARN_ENUM_CONVERSION = YES;
444 | CLANG_WARN_INT_CONVERSION = YES;
445 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
446 | CLANG_WARN_UNREACHABLE_CODE = YES;
447 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
448 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
449 | COPY_PHASE_STRIP = NO;
450 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
451 | ENABLE_NS_ASSERTIONS = NO;
452 | ENABLE_STRICT_OBJC_MSGSEND = YES;
453 | GCC_C_LANGUAGE_STANDARD = gnu99;
454 | GCC_NO_COMMON_BLOCKS = YES;
455 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
456 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
457 | GCC_WARN_UNDECLARED_SELECTOR = YES;
458 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
459 | GCC_WARN_UNUSED_FUNCTION = YES;
460 | GCC_WARN_UNUSED_VARIABLE = YES;
461 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
462 | MTL_ENABLE_DEBUG_INFO = NO;
463 | SDKROOT = iphoneos;
464 | TARGETED_DEVICE_FAMILY = "1,2";
465 | VALIDATE_PRODUCT = YES;
466 | };
467 | name = Release;
468 | };
469 | 4570425B1D0E580400128041 /* Debug */ = {
470 | isa = XCBuildConfiguration;
471 | buildSettings = {
472 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
473 | DEVELOPMENT_TEAM = M3Y7JKHK66;
474 | INFOPLIST_FILE = YLTagsChooser/Info.plist;
475 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
476 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
477 | PRODUCT_BUNDLE_IDENTIFIER = com.Lambert.YLTagsChooser;
478 | PRODUCT_NAME = "$(TARGET_NAME)";
479 | };
480 | name = Debug;
481 | };
482 | 4570425C1D0E580400128041 /* Release */ = {
483 | isa = XCBuildConfiguration;
484 | buildSettings = {
485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
486 | DEVELOPMENT_TEAM = M3Y7JKHK66;
487 | INFOPLIST_FILE = YLTagsChooser/Info.plist;
488 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
489 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
490 | PRODUCT_BUNDLE_IDENTIFIER = com.Lambert.YLTagsChooser;
491 | PRODUCT_NAME = "$(TARGET_NAME)";
492 | };
493 | name = Release;
494 | };
495 | 4570425E1D0E580400128041 /* Debug */ = {
496 | isa = XCBuildConfiguration;
497 | buildSettings = {
498 | BUNDLE_LOADER = "$(TEST_HOST)";
499 | INFOPLIST_FILE = YLTagsChooserTests/Info.plist;
500 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
501 | PRODUCT_BUNDLE_IDENTIFIER = yl.YLTagsChooserTests;
502 | PRODUCT_NAME = "$(TARGET_NAME)";
503 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YLTagsChooser.app/YLTagsChooser";
504 | };
505 | name = Debug;
506 | };
507 | 4570425F1D0E580400128041 /* Release */ = {
508 | isa = XCBuildConfiguration;
509 | buildSettings = {
510 | BUNDLE_LOADER = "$(TEST_HOST)";
511 | INFOPLIST_FILE = YLTagsChooserTests/Info.plist;
512 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
513 | PRODUCT_BUNDLE_IDENTIFIER = yl.YLTagsChooserTests;
514 | PRODUCT_NAME = "$(TARGET_NAME)";
515 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/YLTagsChooser.app/YLTagsChooser";
516 | };
517 | name = Release;
518 | };
519 | 457042611D0E580400128041 /* Debug */ = {
520 | isa = XCBuildConfiguration;
521 | buildSettings = {
522 | INFOPLIST_FILE = YLTagsChooserUITests/Info.plist;
523 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
524 | PRODUCT_BUNDLE_IDENTIFIER = yl.YLTagsChooserUITests;
525 | PRODUCT_NAME = "$(TARGET_NAME)";
526 | TEST_TARGET_NAME = YLTagsChooser;
527 | };
528 | name = Debug;
529 | };
530 | 457042621D0E580400128041 /* Release */ = {
531 | isa = XCBuildConfiguration;
532 | buildSettings = {
533 | INFOPLIST_FILE = YLTagsChooserUITests/Info.plist;
534 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
535 | PRODUCT_BUNDLE_IDENTIFIER = yl.YLTagsChooserUITests;
536 | PRODUCT_NAME = "$(TARGET_NAME)";
537 | TEST_TARGET_NAME = YLTagsChooser;
538 | };
539 | name = Release;
540 | };
541 | /* End XCBuildConfiguration section */
542 |
543 | /* Begin XCConfigurationList section */
544 | 457042281D0E580400128041 /* Build configuration list for PBXProject "YLTagsChooser" */ = {
545 | isa = XCConfigurationList;
546 | buildConfigurations = (
547 | 457042581D0E580400128041 /* Debug */,
548 | 457042591D0E580400128041 /* Release */,
549 | );
550 | defaultConfigurationIsVisible = 0;
551 | defaultConfigurationName = Release;
552 | };
553 | 4570425A1D0E580400128041 /* Build configuration list for PBXNativeTarget "YLTagsChooser" */ = {
554 | isa = XCConfigurationList;
555 | buildConfigurations = (
556 | 4570425B1D0E580400128041 /* Debug */,
557 | 4570425C1D0E580400128041 /* Release */,
558 | );
559 | defaultConfigurationIsVisible = 0;
560 | defaultConfigurationName = Release;
561 | };
562 | 4570425D1D0E580400128041 /* Build configuration list for PBXNativeTarget "YLTagsChooserTests" */ = {
563 | isa = XCConfigurationList;
564 | buildConfigurations = (
565 | 4570425E1D0E580400128041 /* Debug */,
566 | 4570425F1D0E580400128041 /* Release */,
567 | );
568 | defaultConfigurationIsVisible = 0;
569 | defaultConfigurationName = Release;
570 | };
571 | 457042601D0E580400128041 /* Build configuration list for PBXNativeTarget "YLTagsChooserUITests" */ = {
572 | isa = XCConfigurationList;
573 | buildConfigurations = (
574 | 457042611D0E580400128041 /* Debug */,
575 | 457042621D0E580400128041 /* Release */,
576 | );
577 | defaultConfigurationIsVisible = 0;
578 | defaultConfigurationName = Release;
579 | };
580 | /* End XCConfigurationList section */
581 | };
582 | rootObject = 457042251D0E580400128041 /* Project object */;
583 | }
584 |
--------------------------------------------------------------------------------