├── Example
├── CJLabelTest
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── CJLabel.imageset
│ │ │ ├── CJLabel@2x.png
│ │ │ ├── CJLabel@3x.png
│ │ │ └── Contents.json
│ │ ├── backImage.imageset
│ │ │ ├── backImage@2x.png
│ │ │ ├── backImage@3x.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── CJLabelTest.entitlements
│ ├── ViewController.h
│ ├── AppDelegate.h
│ ├── AttributedTableViewCell.h
│ ├── main.m
│ ├── FirstDetailViewController.h
│ ├── SecondDetailViewController.h
│ ├── Common.h
│ ├── AttributedTableViewCell.m
│ ├── Example.json
│ ├── FirstDetailViewController.xib
│ ├── Info.plist
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── AppDelegate.m
│ ├── SecondDetailViewController.xib
│ ├── AttributedTableViewCell.xib
│ ├── ViewController.m
│ ├── FirstDetailViewController.m
│ └── SecondDetailViewController.m
├── CJLabelTest.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── xcuserdata
│ │ ├── CK.xcuserdatad
│ │ │ ├── xcdebugger
│ │ │ │ └── Breakpoints_v2.xcbkptlist
│ │ │ └── xcschemes
│ │ │ │ ├── xcschememanagement.plist
│ │ │ │ └── CJLabelTest.xcscheme
│ │ └── Y.C.Lian.xcuserdatad
│ │ │ ├── xcschemes
│ │ │ ├── xcschememanagement.plist
│ │ │ └── CJLabelTest.xcscheme
│ │ │ └── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ └── project.pbxproj
├── CJLabelTestTests
│ ├── Info.plist
│ └── CJLabelTestTests.m
└── CJLabelTestUITests
│ ├── Info.plist
│ └── CJLabelTestUITests.m
├── CJLabel
├── CJLabel+UIMenu.h
├── CJLabel+UIMenu.m
├── CJLabel.h
└── CJLabelConfigure.h
├── LICENSE
├── CJLabel.podspec
└── README.md
/Example/CJLabelTest/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/CJLabel@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lele8446/CJLabel/HEAD/Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/CJLabel@2x.png
--------------------------------------------------------------------------------
/Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/CJLabel@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lele8446/CJLabel/HEAD/Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/CJLabel@3x.png
--------------------------------------------------------------------------------
/Example/CJLabelTest/Assets.xcassets/backImage.imageset/backImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lele8446/CJLabel/HEAD/Example/CJLabelTest/Assets.xcassets/backImage.imageset/backImage@2x.png
--------------------------------------------------------------------------------
/Example/CJLabelTest/Assets.xcassets/backImage.imageset/backImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lele8446/CJLabel/HEAD/Example/CJLabelTest/Assets.xcassets/backImage.imageset/backImage@3x.png
--------------------------------------------------------------------------------
/Example/CJLabelTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/CJLabelTest.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // tableViewLabelDemo
4 | //
5 | // Created by YiChe on 16/6/13.
6 | // Copyright © 2016年 YiChe. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // CJLabelTest
4 | //
5 | // Created by C.K.Lian on 15/12/11.
6 | // Copyright © 2015年 C.K.Lian. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/AttributedTableViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // AttributedTableViewCell.h
3 | // tableViewLabel
4 | //
5 | // Created by YiChe on 16/4/19.
6 | // Copyright © 2016年 YiChe. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "CJLabel.h"
11 |
12 | @interface AttributedTableViewCell : UITableViewCell
13 | @property (nonatomic,weak)IBOutlet CJLabel *label;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // tableViewLabelDemo
4 | //
5 | // Created by YiChe on 16/6/13.
6 | // Copyright © 2016年 YiChe. 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 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/FirstDetailViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SecondDetailViewController.h
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 2017/11/9.
6 | // Copyright © 2017年 C.K.Lian. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface FirstDetailViewController : UIViewController
12 |
13 | @property (nonatomic, assign) NSInteger index;
14 | @property (nonatomic, strong) NSMutableAttributedString *content;
15 | @end
16 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/SecondDetailViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // DetailViewController.h
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 2017/4/28.
6 | // Copyright © 2017年 C.K.Lian. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SecondDetailViewController : UIViewController
12 |
13 | @property (nonatomic, assign) NSInteger index;
14 | @property (nonatomic, strong) NSMutableAttributedString *content;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/Assets.xcassets/CJLabel.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "CJLabel@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "CJLabel@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Example/CJLabelTest/Assets.xcassets/backImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "backImage@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "backImage@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Example/CJLabelTest.xcodeproj/xcuserdata/CK.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/Common.h:
--------------------------------------------------------------------------------
1 | //
2 | // Common.h
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 2017/11/9.
6 | // Copyright © 2017年 C.K.Lian. All rights reserved.
7 | //
8 |
9 | #ifndef Common_h
10 | #define Common_h
11 |
12 | #define ScreenWidth [[UIScreen mainScreen] bounds].size.width
13 |
14 | #define ScreenHeight [[UIScreen mainScreen] bounds].size.height
15 |
16 | #define UIRGBColor(r,g,b,a) ([UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a])
17 | #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
18 | green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
19 | blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
20 |
21 | #endif /* Common_h */
22 |
--------------------------------------------------------------------------------
/Example/CJLabelTestTests/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 |
--------------------------------------------------------------------------------
/Example/CJLabelTestUITests/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 |
--------------------------------------------------------------------------------
/Example/CJLabelTest.xcodeproj/xcuserdata/CK.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CJLabelTest.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 438257801C1A6F5F00A3DA24
16 |
17 | primary
18 |
19 |
20 | 438257991C1A6F5F00A3DA24
21 |
22 | primary
23 |
24 |
25 | 438257A41C1A6F5F00A3DA24
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Example/CJLabelTest.xcodeproj/xcuserdata/Y.C.Lian.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CJLabelTest.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 438257801C1A6F5F00A3DA24
16 |
17 | primary
18 |
19 |
20 | 438257991C1A6F5F00A3DA24
21 |
22 | primary
23 |
24 |
25 | 438257A41C1A6F5F00A3DA24
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/AttributedTableViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // AttributedTableViewCell.m
3 | // tableViewLabel
4 | //
5 | // Created by YiChe on 16/4/19.
6 | // Copyright © 2016年 YiChe. All rights reserved.
7 | //
8 |
9 | #import "AttributedTableViewCell.h"
10 |
11 | @implementation AttributedTableViewCell
12 |
13 | - (void)awakeFromNib {
14 | [super awakeFromNib];
15 | self.label.preferredMaxLayoutWidth = [[UIScreen mainScreen] bounds].size.width - 20;
16 | self.label.numberOfLines = 4;
17 | self.label.textInsets = UIEdgeInsetsMake(5, 5, 5, 0);
18 | self.label.enableCopy = YES;
19 | // self.label.font = [UIFont systemFontOfSize:12];
20 | // self.label.attributedTruncationToken = [[NSAttributedString alloc]initWithString:@""];
21 | }
22 |
23 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
24 | [super setSelected:selected animated:animated];
25 |
26 | // Configure the view for the selected state
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/CJLabel/CJLabel+UIMenu.h:
--------------------------------------------------------------------------------
1 | //
2 | // CJLabel+UIMenu.h
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 2021/1/29.
6 | // Copyright © 2021 cjl. All rights reserved.
7 | //
8 |
9 | #import "CJLabel.h"
10 |
11 | @interface CJLabel (UIMenu)
12 | /**
13 | 长按全选文本后弹出的UIMenu菜单(类似微信朋友圈全选复制功能)
14 | */
15 | @property (nonatomic, strong, readonly) NSMutableArray *menuItems;
16 | /**
17 | 长按全选文本的背景色
18 | */
19 | @property (nonatomic, strong, readonly) UIColor *selectTextBackColor;
20 |
21 | /// 长按全选文本后弹出自定义UIMenu菜单
22 | /// @param menus 自定义UIMenu菜单
23 | /// @param selectTextBackColor 全选文本背景色
24 | /// @param colorAlpha 背景色透明度
25 | /// @param clickMenuCompletion 点击自定义UIMenu菜单回调
26 | - (void)showSelectAllTextWithMenus:(NSArray *)menus
27 | selectTextBackColor:(UIColor *)selectTextBackColor
28 | colorAlpha:(CGFloat)colorAlpha
29 | clickMenuCompletion:(void (^)(NSString *menuTitle, CJLabel *label))clickMenuCompletion;
30 | @end
31 |
--------------------------------------------------------------------------------
/Example/CJLabelTestTests/CJLabelTestTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // CJLabelTestTests.m
3 | // CJLabelTestTests
4 | //
5 | // Created by C.K.Lian on 15/12/11.
6 | // Copyright © 2015年 C.K.Lian. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface CJLabelTestTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation CJLabelTestTests
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 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/Example.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "text": "示例一:富文本展示\n\n`CJLabel`支持富文本展示:包括设置不同字体💯,插入图片设置字体背景色、字体边框线、边框线圆角、对指定文本添加删除线;另外设置文本内边距为{10,10,10,0}。"
4 | },
5 | {
6 | "text": "示例二:垂直对齐\n\n`CJLabel`设置整体文本垂直居中,🐹点击可调整文本在垂直方向的对齐方式。\n支持复制,双击或者长按可唤起😁UIMenuController进行选择复制文本操作。"
7 | },
8 | {
9 | "text": "示例三:选择复制\n\n支持复制,😆双击或者长按可唤起😁UIMenuController进行选择复制文本操作。\n设置`CJLabel`为可点击链点,并指定其字体大小粗体15,字体颜色蓝色,边框线颜色为橙黄色,边框线粗细为1,边框线圆角取默认值5,背景填充颜色为浅灰色;👻点击高亮时字体颜色红色,边框线为红色,点击背景色橘黄色👏。"
10 | },
11 | {
12 | "text": "示例四:点击事件\n\n设置多段重复内容都为可点击链点😊,第一个`CJLabel`响应点击和长按事件,第二个`CJLabel`只响应点击事件,👀第三个`CJLabel`只响应长按事件并且与其他链点的样式不相同;整体文本在垂直方向为底部对齐。"
13 | },
14 | {
15 | "text": "示例五:图文混排\n\n😛`CJLabel`在示例文本中插入图片 😁设置图片边框颜色为红色,边框线粗细取默认值1,并设置图片为可点击链点,同时可调整图片所在行在垂直方向的对齐方式。"
16 | },
17 | {
18 | "text": "示例六:自定义截断字符`TruncationToken`为:“……全文”,默认字体颜色为黑色,最多显示3行,超出3行部分自动截断,截断方式为行尾截断,截断字符以蓝色高亮显示,点击展开所有内容。支持复制,双击或者长按可唤起UIMenuController进行选择复制文本操作。 #CJLabel# 继承自 UILabel,支持富文本展示、图文混排、添加自定义点击链点以及选择复制等功能。"
19 | },
20 | {
21 | "text": "示例七:不可换行标签,#CJLabel#,#CJLabel#,标签#CJLabel#,又一个标签#CJLabel#,#CJLabel#,#CJLabel#,#CJLabel#"
22 | },
23 | ]
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 ChiJinLian
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 |
23 |
--------------------------------------------------------------------------------
/Example/CJLabelTestUITests/CJLabelTestUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // CJLabelTestUITests.m
3 | // CJLabelTestUITests
4 | //
5 | // Created by C.K.Lian on 15/12/11.
6 | // Copyright © 2015年 C.K.Lian. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface CJLabelTestUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation CJLabelTestUITests
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 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/FirstDetailViewController.xib:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | zh_CN
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | 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 |
--------------------------------------------------------------------------------
/CJLabel.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod spec lint CJLabel.podspec' to ensure this is a
3 | # valid spec and to remove all comments including this before submitting the spec.
4 | #
5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
7 | #
8 |
9 | Pod::Spec.new do |s|
10 |
11 | s.name = "CJLabel"
12 | s.version = "4.7.3"
13 | s.summary = "A drop-in replacement for UILabel that supports NSAttributedString, rich text, links, select copy and more."
14 | s.homepage = "https://github.com/lele8446/CJLabelTest"
15 | # s.license = "MIT"
16 | s.license = { :type => 'Apache License, Version 2.0', :text => <<-LICENSE
17 | Licensed under the Apache License, Version 2.0 (the "License");
18 | you may not use this file except in compliance with the License.
19 | You may obtain a copy of the License at
20 |
21 | http://www.apache.org/licenses/LICENSE-2.0
22 |
23 | Unless required by applicable law or agreed to in writing, software
24 | distributed under the License is distributed on an "AS IS" BASIS,
25 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 | See the License for the specific language governing permissions and
27 | limitations under the License.
28 | LICENSE
29 | }
30 | s.author = { "ChiJinLian" => "lele8446@foxmail.com" }
31 | s.platform = :ios, "7.0"
32 | s.source = { :git => "https://github.com/lele8446/CJLabelTest.git", :tag => "#{s.version}" }
33 | s.source_files = "CJLabel/*"
34 |
35 | end
36 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/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 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/Example/CJLabelTest/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // CJLabelTest
4 | //
5 | // Created by C.K.Lian on 15/12/11.
6 | // Copyright © 2015年 C.K.Lian. 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 |
--------------------------------------------------------------------------------
/Example/CJLabelTest.xcodeproj/xcuserdata/Y.C.Lian.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
14 |
15 |
16 |
18 |
30 |
31 |
32 |
34 |
46 |
47 |
48 |
50 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/SecondDetailViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/Example/CJLabelTest.xcodeproj/xcuserdata/CK.xcuserdatad/xcschemes/CJLabelTest.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 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/AttributedTableViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/CJLabel/CJLabel+UIMenu.m:
--------------------------------------------------------------------------------
1 | //
2 | // CJLabel+UIMenu.m
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 2021/1/29.
6 | // Copyright © 2021 cjl. All rights reserved.
7 | //
8 |
9 | #import "CJLabel+UIMenu.h"
10 | #import
11 |
12 | #define UIMenuSELPrefix @"CJLabel_Menu_"
13 |
14 | @interface CJLabel ()
15 | @property (nonatomic, strong) UIColor *selectTextBackColor;
16 | @property (nonatomic, strong) NSMutableArray *menuItems;
17 | @property (nonatomic, copy) void(^clickMenuCompletion)(NSString *menuTitle, CJLabel *label);
18 | @end
19 |
20 | @interface SelectMenuForwardingTarget : NSObject
21 | @end
22 | @implementation SelectMenuForwardingTarget
23 |
24 | - (void)forwardingSelector:(NSString *)selectorName label:(CJLabel *)label {
25 | NSString *menuTitle = [selectorName stringByReplacingOccurrencesOfString:UIMenuSELPrefix withString:@""];
26 | if (label.clickMenuCompletion) {
27 | label.clickMenuCompletion(menuTitle, label);
28 | }
29 | }
30 | + (BOOL)resolveInstanceMethod:(SEL)sel {
31 | NSString *selectorName = NSStringFromSelector(sel);
32 | if ([selectorName hasPrefix:UIMenuSELPrefix]) {
33 | class_addMethod([self class], sel, imp_implementationWithBlock(^(id self, NSString *str) {
34 | // NSLog(@"imp block, sel = %@", selectorName);
35 | }), "v@");
36 | }
37 | return [super resolveInstanceMethod:sel];
38 | }
39 | @end
40 |
41 |
42 | @implementation CJLabel (UIMenu)
43 |
44 | static char selectTextBackColorKey;
45 | - (void)setSelectTextBackColor:(UIColor *)selectTextBackColor {
46 | objc_setAssociatedObject(self, &selectTextBackColorKey, selectTextBackColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
47 | }
48 | - (UIColor *)selectTextBackColor {
49 | UIColor *color = objc_getAssociatedObject(self, &selectTextBackColorKey);
50 | if (!color) {
51 | color = CJUIRGBColor(0,84,166,0.2);
52 | }
53 | return color;
54 | }
55 |
56 | static char menuItemsKey;
57 | - (void)setMenuItems:(NSMutableArray *)menuItems {
58 | objc_setAssociatedObject(self, &menuItemsKey, menuItems, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
59 | }
60 | - (NSMutableArray *)menuItems {
61 | return objc_getAssociatedObject(self, &menuItemsKey);
62 | }
63 |
64 | static char clickMenuCompletionKey;
65 | - (void)setClickMenuCompletion:(void (^)(NSString *, CJLabel *))clickMenuCompletion {
66 | objc_setAssociatedObject(self, &clickMenuCompletionKey, clickMenuCompletion, OBJC_ASSOCIATION_COPY_NONATOMIC);
67 | }
68 | - (void (^)(NSString *, CJLabel *))clickMenuCompletion {
69 | return objc_getAssociatedObject(self, &clickMenuCompletionKey);
70 | }
71 |
72 | - (void)showSelectAllTextWithMenus:(NSArray *)menus selectTextBackColor:(UIColor *)selectTextBackColor colorAlpha:(CGFloat)colorAlpha clickMenuCompletion:(void (^)(NSString *menuTitle, CJLabel *label))clickMenuCompletion {
73 | self.enableCopy = NO;
74 | self.clickMenuCompletion = clickMenuCompletion;
75 |
76 | if (colorAlpha == 0) {
77 | colorAlpha = 0.2;
78 | }
79 | self.selectTextBackColor = [selectTextBackColor colorWithAlphaComponent:colorAlpha];
80 | NSMutableArray *menuItems = [NSMutableArray array];
81 | for (NSString *str in menus) {
82 | NSString *selectorName = [NSString stringWithFormat:@"%@%@",UIMenuSELPrefix,str];
83 | UIMenuItem *menuItem = [[UIMenuItem alloc] initWithTitle:str action: NSSelectorFromString(selectorName)];
84 | [menuItems addObject:menuItem];
85 | }
86 | self.menuItems = menuItems;
87 | }
88 |
89 | #pragma mark - UIResponder
90 | - (BOOL)canBecomeFirstResponder {
91 | return YES;
92 | }
93 |
94 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
95 | // 需要有文字才能支持选择复制
96 | if (self.attributedText || self.text) {
97 | NSString *selectorName = NSStringFromSelector(action);
98 | if ([selectorName hasPrefix:UIMenuSELPrefix]) {
99 | return YES;
100 | }
101 | }
102 | return [super canPerformAction:action withSender:sender];
103 | }
104 |
105 | - (id)forwardingTargetForSelector:(SEL)aSelector {
106 | NSString *selectorName = NSStringFromSelector(aSelector);
107 | if ([selectorName hasPrefix:UIMenuSELPrefix]) {
108 | SelectMenuForwardingTarget *target = [SelectMenuForwardingTarget new];
109 | __weak typeof(self)wSelf = self;
110 | [target forwardingSelector:selectorName label:wSelf];
111 | return target;
112 | }
113 | else{
114 | return [super forwardingTargetForSelector:aSelector];
115 | }
116 | }
117 |
118 | - (void)menuItemClick {
119 | [self hideMenuItems];
120 | }
121 |
122 | - (void)hideMenuItems {
123 | if (self.menuItems.count > 0) {
124 | NSMutableArray *newMenuItems = [NSMutableArray array];
125 | NSArray *customMenuTitle = [self.menuItems valueForKeyPath:@"title"];
126 | for (UIMenuItem *menuItem in [UIMenuController sharedMenuController].menuItems) {
127 | if (![customMenuTitle containsObject:menuItem.title]) {
128 | [newMenuItems addObject:menuItem];
129 | }
130 | }
131 | [UIMenuController sharedMenuController].menuItems = newMenuItems;
132 | }
133 | UIView *allTextSelectBackView = [self viewWithTag:[@"allTextSelectBackView" hash]];
134 | [allTextSelectBackView removeFromSuperview];
135 | allTextSelectBackView = nil;
136 | }
137 | @end
138 |
--------------------------------------------------------------------------------
/Example/CJLabelTest.xcodeproj/xcuserdata/Y.C.Lian.xcuserdatad/xcschemes/CJLabelTest.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
44 |
50 |
51 |
52 |
53 |
54 |
60 |
61 |
62 |
63 |
64 |
65 |
76 |
78 |
84 |
85 |
86 |
87 |
91 |
92 |
96 |
97 |
101 |
102 |
103 |
104 |
105 |
106 |
112 |
114 |
120 |
121 |
122 |
123 |
125 |
126 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // tableViewLabelDemo
4 | //
5 | // Created by YiChe on 16/6/13.
6 | // Copyright © 2016年 YiChe. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "Common.h"
11 | #import "AttributedTableViewCell.h"
12 | #import "FirstDetailViewController.h"
13 | #import "SecondDetailViewController.h"
14 |
15 |
16 | @interface ViewController ()
17 | @property (nonatomic, strong) UITableView *tableView;
18 | @property (nonatomic, strong) NSMutableArray *espressos;
19 |
20 | @property (nonatomic, strong) AttributedTableViewCell *tempCell;
21 |
22 | @end
23 |
24 | @implementation ViewController
25 |
26 | - (AttributedTableViewCell *)tempCell {
27 | if (!_tempCell) {
28 | _tempCell = [[[NSBundle mainBundle] loadNibNamed:@"AttributedTableViewCell" owner:self options:Nil] lastObject];
29 | }
30 | return _tempCell;
31 | }
32 |
33 |
34 | - (void)viewDidLoad {
35 | [super viewDidLoad];
36 |
37 | self.edgesForExtendedLayout = UIRectEdgeNone;
38 | self.automaticallyAdjustsScrollViewInsets = NO;
39 |
40 | self.tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain];
41 | self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
42 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
43 | self.tableView.delegate = self;
44 | self.tableView.dataSource = self;
45 | self.tableView.tableFooterView = [[UIView alloc] init];
46 | [self.view addSubview:self.tableView];
47 |
48 | [self readFile];
49 | }
50 |
51 | - (void)didReceiveMemoryWarning {
52 | [super didReceiveMemoryWarning];
53 | // Dispose of any resources that can be recreated.
54 | }
55 |
56 | - (void)readFile {
57 | NSString *path = [[NSBundle mainBundle] pathForResource:@"Example" ofType:@"json"];
58 | NSString *content = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
59 |
60 | NSData *JSONData = [content dataUsingEncoding:NSUTF8StringEncoding];
61 | NSArray *responseJSON = nil;
62 | if (JSONData) {
63 | responseJSON = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingMutableContainers error:nil];
64 | }
65 |
66 | /* 设置默认换行模式为:NSLineBreakByCharWrapping
67 | * 当Label的宽度不够显示内容或图片的时候就自动换行, 如果不自动换行, 超出一行的部分图片将不显示
68 | */
69 | NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
70 | paragraph.lineBreakMode = NSLineBreakByCharWrapping;
71 | paragraph.lineSpacing = 6;
72 |
73 | self.espressos = [NSMutableArray array];
74 | for (int i = 0; i显示图片请调用:
10 | `+ initWithView:viewSize:lineAlignment:configure:`或者
11 | `+ insertViewAtAttrString:view:viewSize:atIndex:lineAlignment:configure:`方法初始化`NSAttributedString`后显示
12 | 5. `extendsLinkTouchArea`设置是否扩大链点点击识别范围
13 | 6. `shadowRadius`设置文本阴影模糊半径
14 | 7. `textInsets` 设置文本内边距
15 | 8. `verticalAlignment` 设置垂直方向的文本对齐方式。注意与显示图片时候的`imagelineAlignment`作区分,`self.verticalAlignment`对应的是整体文本在垂直方向的对齐方式,而`imagelineAlignment`只对图片所在行的垂直对齐方式有效
16 | 9. `delegate` 点击链点代理
17 | 10. `attributedTruncationToken`自定义截断字符,默认"...",只针对`self.lineBreakMode`的以下三种值有效,假如`attributedTruncationToken`=`***`,则:
18 | `NSLineBreakByTruncatingHead, // 头部截断: "***wxyz"`
19 | `NSLineBreakByTruncatingTail, // 中间截断: "abcd***"`
20 | `NSLineBreakByTruncatingMiddle // 尾部截断: "ab***yz"`
21 | 11. `kCJBackgroundFillColorAttributeName` 背景填充颜色,属性优先级低于`NSBackgroundColorAttributeName`如果设置`NSBackgroundColorAttributeName`会忽略`kCJBackgroundFillColorAttributeName`的设置
22 | 12. `kCJBackgroundStrokeColorAttributeName ` 背景边框线颜色
23 | 13. `kCJBackgroundLineWidthAttributeName ` 背景边框线宽度
24 | 14. `kCJBackgroundLineCornerRadiusAttributeName ` 背景边框线圆角弧度
25 | 15. `kCJActiveBackgroundFillColorAttributeName ` 点击时候的背景填充颜色属性优先级同
26 | `kCJBackgroundFillColorAttributeName`
27 | 16. `kCJActiveBackgroundStrokeColorAttributeName ` 点击时候的背景边框线颜色
28 | 17. 支持添加自定义样式、可点击(长按)的文本点击链点
29 | 18. 支持 Interface Builder
30 |
31 |
32 | ##### CJLabel 已知 Bug
33 |
34 | `numberOfLines`大于0且小于实际`label.numberOfLines`,同时`verticalAlignment`不等于`CJContentVerticalAlignmentTop`时,文本显示位置有偏差。如下图所示:
35 |
36 |
37 |
38 |
39 | ## CJLabel引用
40 | ##### 1. 直接导入
41 | 下载demo,将CJLabel文件夹导入项目,引用头文件 `#import "CJLabel.h"`
42 | ##### 2. CocoaPods安装
43 | ```ruby
44 | pod 'CJLabel'
45 |
46 | ```
47 |
48 | ## 用法
49 | * 根据NSAttributedString计算CJLabel的size大小
50 |
51 | ```objective-c
52 | CGSize size = [CJLabel sizeWithAttributedString:str withConstraints:CGSizeMake(320, CGFLOAT_MAX) limitedToNumberOfLines:0];
53 | ```
54 | * 指定内边距以及限定行数计算CJLabel的size大小
55 | ```objective-c
56 | CGSize size = [CJLabel sizeWithAttributedString:str withConstraints:CGSizeMake(320, CGFLOAT_MAX) limitedToNumberOfLines:0 textInsets:3];
57 | ```
58 |
59 | * 设置富文本展示
60 |
61 |
62 |
63 |
64 | ```objective-c
65 | //初始化配置
66 | CJLabelConfigure *configure = [CJLabel configureAttributes:nil isLink:NO activeLinkAttributes:nil parameter:nil clickLinkBlock:nil longPressBlock:nil];
67 | //设置配置属性
68 | configure.attributes = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:18]};
69 | //设置指定字符属性
70 | attStr = [CJLabel configureAttrString:attStr withString:@"不同字体" sameStringEnable:NO configure:configure];
71 | NSRange imgRange = [attStr.string rangeOfString:@"插入图片"];
72 | //移除指定属性
73 | [configure removeAttributesForKey:kCJBackgroundStrokeColorAttributeName];
74 | //指定位置插入图片
75 | UIImageView *imgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"CJLabel.png"]];
76 | imgView.contentMode = UIViewContentModeScaleAspectFill;
77 | imgView.clipsToBounds = YES;
78 | attStr = [CJLabel insertViewAtAttrString:attStr view:imgView viewSize:CGSizeMake(55, 45) atIndex:(imgRange.location+imgRange.length) lineAlignment:CJVerticalAlignmentBottom configure:configure];
79 | //设置内边距
80 | self.label.textInsets = UIEdgeInsetsMake(10, 10, 10, 0);
81 | self.label.attributedText = attStr;
82 | ```
83 | * 垂直对齐、选择复制
84 |
85 |
86 |
87 |
88 | ```objective-c
89 | //设置垂直对齐方式
90 | self.label.verticalAlignment = CJVerticalAlignmentCenter;
91 | self.label.text = self.attStr;
92 | //支持选择复制
93 | self.label.enableCopy = YES;
94 | ```
95 | * 设置文字、图片点击链点
96 |
97 |
98 |
99 |
100 | ```objective-c
101 | //设置点击链点属性
102 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor blueColor]};
103 | //设置点击高亮属性
104 | configure.activeLinkAttributes = @{NSForegroundColorAttributeName:[UIColor redColor]};
105 | //链点自定义参数
106 | configure.parameter = @"参数为字符串";
107 | //点击回调(也可通过设置self.label.delegate = self代理,返回点击回调事件)
108 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) {
109 | //do something
110 | };
111 | //长按回调
112 | configure.longPressBlock = ^(CJLabelLinkModel *linkModel) {
113 | //do something
114 | };
115 | //设置为可点击链点
116 | configure.isLink = YES;
117 | //设置点击链点
118 | attStr = [CJLabel configureAttrString:attStr
119 | withString:@"CJLabel"
120 | sameStringEnable:YES
121 | configure:configure];
122 | //设置图片点击链点属性
123 | NSRange imageRange = [attStr.string rangeOfString:@"图片"];
124 | CJLabelConfigure *imgConfigure =
125 | [CJLabel configureAttributes:@{kCJBackgroundStrokeColorAttributeName:[UIColor redColor]}
126 | isLink:YES
127 | activeLinkAttributes:@{kCJActiveBackgroundStrokeColorAttributeName:[UIColor lightGrayColor]}
128 | parameter:@"图片参数"
129 | clickLinkBlock:^(CJLabelLinkModel *linkModel){
130 | [self clickLink:linkModel isImage:YES];
131 | }
132 | longPressBlock:^(CJLabelLinkModel *linkModel){
133 | [self clicklongPressLink:linkModel isImage:YES];
134 | }];
135 | attStr = [CJLabel insertViewAtAttrString:attStr view:@"CJLabel.png" viewSize:CGSizeMake(45, 35) atIndex:(imageRange.location+imageRange.length) lineAlignment:verticalAlignment configure:imgConfigure];
136 | self.label.attributedText = attStr;
137 | //支持选择复制
138 | self.label.enableCopy = YES;
139 | ```
140 |
141 | * 自定义截断文本,并设置为可点击
142 |
143 |
144 |
145 |
146 | ```objective-c
147 | //配置链点属性
148 | configure.isLink = YES;
149 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) {
150 | //点击 `……全文`
151 | [self clickTruncationToken:linkModel];
152 | };
153 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor blueColor],NSFontAttributeName:[UIFont systemFontOfSize:13]};
154 | //自定义截断字符为:"……全文"
155 | NSAttributedString *truncationToken = [CJLabel initWithAttributedString:[[NSAttributedString alloc]initWithString:@"……全文"] strIdentifier:@"TruncationToken" configure:configure];
156 | //设置行尾截断
157 | self.label.lineBreakMode = NSLineBreakByTruncatingTail;
158 | self.label.attributedTruncationToken = truncationToken;
159 | //设置点击链点
160 | attStr = [CJLabel configureAttrString:attStr withAttributedString:truncationToken strIdentifier:@"TruncationToken" sameStringEnable:NO configure:configure];
161 | self.label.attributedText = attStr;
162 | //支持选择复制
163 | self.label.enableCopy = YES;
164 | ```
165 |
166 | ## 版本说明
167 | * ***V4.7.0***
168 | 新增不可换行标签功能,优化图文混排展示
169 |
170 | * ***V4.6.0***
171 | 支持显示任意view
172 |
173 | * ***V4.5.0 V4.5.1 V4.5.3***
174 | 增加`attributedTruncationToken`属性,支持自定义截断字符;增加`kCJStrikethroughStyleAttributeName、kCJStrikethroughColorAttributeName`属性,可对指定文本添加删除线
175 |
176 | * ***V4.4.0***
177 | 优化NSAttributedString链点属性设置
178 |
179 | * ***V4.0.0***
180 | 新增`enableCopy`属性,支持选择、全选、复制功能,类似`UITextView`的选择复制效果。
181 |
182 | * ***V3.0.0***
183 | 优化富文本配置方法,新增CJLabelConfigure类,简化方法调用,增加对NSAttributedString点击链点的判断(比如对于两个重名用户:@lele 和 @lele,可以分别设置不同的点击响应事件)
184 | ***注意***
185 | ***`V3.0.0`*** 版本引入`CJLabelConfigure`类,优化了NSAttributedString的设置,旧的配置API不再支持。相关调用请参照以下相关方法
186 | `+ initWithImage:imageSize:imagelineAlignment:configure:`
187 | `+ initWithString:configure:`
188 | `+ initWithAttributedString:strIdentifier:configure:`
189 |
190 | * ***V2.1.2***
191 | 可修改图片所在行在垂直方向的对齐方式(只针对当前行),有居上、居中、居下选项,默认居下
192 |
193 | * ***V2.1.1***
194 | 修复单行文字时候点击链点的判断,增加delegate
195 |
196 | * ***V2.0.0***
197 | 优化点击链点响应判断,增加插入图片、插入图片链点、点击链点背景色填充、点击链点边框线描边等功能
198 | v2.0.0之后版本与v1.x.x版本差别较大,基本上重写了增加以及移除点击链点的API
199 |
200 | * ***V1.0.2***
201 | 点击链点增加扩展属性parameter
202 |
203 | * ***V1.0.1***
204 | 增加文本中内容相同的链点能够响应点击属性sameLinkEnable,必须在设置self.attributedText前赋值,默认值为NO,只取文本中首次出现的链点。
205 |
206 | * ***V1.0.0***
207 | 支持链点点击响应
208 |
209 |
210 | ## 许可证
211 | CJLabel 使用 MIT 许可证,详情见 LICENSE 文件。
212 |
213 | ## 更多
214 | [深入理解 iOS 图文混排原理并自定义图文控件](https://www.infoq.cn/article/cy916KUJYK7GA3p2VjZH)
215 | [CJLabel富文本三 —— UILabel支持选择复制以及实现原理](https://www.jianshu.com/p/7de3e6d19e31)
--------------------------------------------------------------------------------
/Example/CJLabelTest/FirstDetailViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SecondDetailViewController.m
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 2017/11/9.
6 | // Copyright © 2017年 C.K.Lian. All rights reserved.
7 | //
8 |
9 | #import "FirstDetailViewController.h"
10 | #import "Common.h"
11 | #import "CJLabel.h"
12 | #import "CJLabel+UIMenu.h"
13 |
14 | @interface FirstDetailViewController ()
15 | @property (nonatomic, strong) CJLabel *label;
16 | @property (nonatomic, strong) NSAttributedString *attStr;
17 | @end
18 |
19 | @implementation FirstDetailViewController
20 |
21 | - (void)dealloc {
22 | NSLog(@"%@ dealloc",NSStringFromClass([self class]));
23 | }
24 |
25 | - (void)viewDidLoad {
26 | [super viewDidLoad];
27 | self.edgesForExtendedLayout = UIRectEdgeNone;
28 | self.automaticallyAdjustsScrollViewInsets = NO;
29 |
30 | self.label = [[CJLabel alloc]initWithFrame:CGRectMake(10, 10, ScreenWidth - 20, ScreenHeight - 64 - 150)];
31 | self.label.backgroundColor = UIColorFromRGB(0XE3F6FF);
32 | self.label.numberOfLines = 0;
33 | [self.view addSubview:self.label];
34 |
35 | [self handleContent:self.content];
36 | }
37 |
38 | - (void)didReceiveMemoryWarning {
39 | [super didReceiveMemoryWarning];
40 | // Dispose of any resources that can be recreated.
41 | }
42 |
43 | - (void)handleContent:(NSMutableAttributedString *)content {
44 |
45 | NSMutableAttributedString *attStr = content;
46 |
47 | CJLabelConfigure *configure = [CJLabel configureAttributes:nil isLink:NO activeLinkAttributes:nil parameter:nil clickLinkBlock:nil longPressBlock:nil];
48 | configure.isLink = NO;
49 |
50 | attStr = [CJLabel configureAttrString:attStr withString:@"CJLabel" sameStringEnable:YES configure:configure];
51 | self.attStr = attStr;
52 |
53 | __weak typeof(self)wSelf = self;
54 | switch (self.index) {
55 | case 1:
56 | self.navigationItem.title = @"垂直对齐";
57 | //设置垂直对齐方式
58 | self.label.verticalAlignment = CJVerticalAlignmentCenter;
59 | self.label.text = self.attStr;
60 | //支持选择复制
61 | self.label.enableCopy = YES;
62 | [self rightBarButtonItems];
63 | break;
64 | case 3:
65 | {
66 | self.navigationItem.title = @"添加不同链点";
67 | self.label.verticalAlignment = CJVerticalAlignmentBottom;
68 |
69 | NSArray *linkRangeArray = [CJLabel sameLinkStringRangeArray:@"CJLabel" inAttString:attStr];
70 | //第一个链点
71 | NSRange linkRange = [linkRangeArray[0] rangeValue];
72 | configure.attributes = @{
73 | NSForegroundColorAttributeName:[UIColor blueColor],
74 | NSFontAttributeName:[UIFont boldSystemFontOfSize:15],
75 | kCJBackgroundStrokeColorAttributeName:[UIColor orangeColor],
76 | kCJBackgroundFillColorAttributeName:[UIColor lightGrayColor]
77 | };
78 | configure.activeLinkAttributes = @{
79 | NSForegroundColorAttributeName:[UIColor redColor],
80 | kCJActiveBackgroundStrokeColorAttributeName:[UIColor redColor],
81 | kCJActiveBackgroundFillColorAttributeName:UIRGBColor(247,231,121,1)
82 | };
83 | configure.isLink = YES;
84 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) {
85 | [wSelf clickLink:linkModel];
86 | };
87 | configure.longPressBlock = ^(CJLabelLinkModel *linkModel) {
88 | [wSelf clicklongPressLink:linkModel];
89 | };
90 | configure.parameter = @"第1个点击链点";
91 | attStr = [CJLabel configureAttrString:attStr atRange:linkRange configure:configure];
92 |
93 | //第二个点击链点
94 | linkRange = [linkRangeArray[1] rangeValue];
95 | configure.longPressBlock = nil;
96 | configure.parameter = @"第2个点击链点";
97 | attStr = [CJLabel configureAttrString:attStr atRange:linkRange configure:configure];
98 |
99 | //第三个点击链点
100 | linkRange = [linkRangeArray[2] rangeValue];
101 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor redColor],
102 | NSFontAttributeName:[UIFont systemFontOfSize:15],
103 | kCJBackgroundStrokeColorAttributeName:[UIColor redColor],
104 | kCJBackgroundFillColorAttributeName:[UIColor colorWithWhite:0.7 alpha:0.7]
105 | };
106 | configure.activeLinkAttributes = @{NSForegroundColorAttributeName:[UIColor darkTextColor],
107 | kCJActiveBackgroundStrokeColorAttributeName:[UIColor darkTextColor],
108 | kCJActiveBackgroundFillColorAttributeName:[UIColor lightGrayColor]
109 | };
110 | configure.clickLinkBlock = nil;
111 | configure.parameter = @"第3个点击链点";
112 | configure.longPressBlock = ^(CJLabelLinkModel *linkModel) {
113 | [wSelf clicklongPressLink:linkModel];
114 | };
115 | attStr = [CJLabel configureAttrString:attStr atRange:linkRange configure:configure];
116 |
117 | self.label.attributedText = attStr;
118 | [self.label showSelectAllTextWithMenus:@[@"拷贝",@"翻译",@"搜一搜"] selectTextBackColor:[UIColor lightGrayColor] colorAlpha:0.35 clickMenuCompletion:^(NSString *menuTitle, CJLabel *label) {
119 | NSLog(@"点击 = %@",menuTitle);
120 | }];
121 | [CJLabel respondsToSelector:@selector(initWithView:viewSize:lineAlignment:configure:)];
122 | [CJLabel respondsToSelector:@selector(insertViewAtAttrString:view:viewSize:atIndex:lineAlignment:configure:)];
123 | }
124 | break;
125 | case 5:
126 | {
127 | self.navigationItem.title = @"自定义截断字符";
128 | self.label.numberOfLines = 3;
129 |
130 | //配置链点属性
131 | configure.isLink = YES;
132 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) {
133 | //点击 `……全文`
134 | [wSelf clickTruncationToken:linkModel];
135 | };
136 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor blueColor],NSFontAttributeName:[UIFont systemFontOfSize:13]};
137 |
138 | //自定义截断字符为:"……全文"
139 | NSAttributedString *truncationToken = [CJLabel initWithAttributedString:[[NSAttributedString alloc]initWithString:@"……全文"] strIdentifier:@"TruncationToken" configure:configure];
140 | //设置行尾截断
141 | self.label.lineBreakMode = NSLineBreakByTruncatingTail;
142 | self.label.attributedTruncationToken = truncationToken;
143 | //设置点击链点
144 | attStr = [CJLabel configureAttrString:attStr withAttributedString:truncationToken strIdentifier:@"TruncationToken" sameStringEnable:NO configure:configure];
145 |
146 | UIImageView *imgView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 100, 80)];
147 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1576752448623&di=42bd4f5d4a9f8f35eb8eade57484fde0&imgtype=0&src=http%3A%2F%2Faliyunzixunbucket.oss-cn-beijing.aliyuncs.com%2Fjpg%2Fb651212dd7b9019275ccc0266f7919ad.jpg%3Fx-oss-process%3Dimage%2Fresize%2Cp_100%2Fauto-orient%2C1%2Fquality%2Cq_90%2Fformat%2Cjpg%2Fwatermark%2Cimage_eXVuY2VzaGk%3D%2Ct_100"]];
148 | imgView.image = [UIImage imageWithData:data];
149 | attStr = [CJLabel insertViewAtAttrString:attStr view:imgView viewSize:CGSizeMake(100, 80) atIndex:attStr.length-12 lineAlignment:CJVerticalAlignmentBottom configure:nil];
150 |
151 | UISwitch *mySwitch = [[UISwitch alloc]initWithFrame:CGRectMake(40, 10, 50, 31)];
152 | mySwitch.on = YES;
153 | attStr = [CJLabel insertViewAtAttrString:attStr view:mySwitch viewSize:CGSizeMake(50, 31) atIndex:attStr.length-6 lineAlignment:CJVerticalAlignmentBottom configure:nil];
154 |
155 | self.label.attributedText = attStr;
156 | //支持选择复制
157 | // self.label.enableCopy = YES;
158 | CGFloat height = [CJLabel sizeWithAttributedString:attStr withConstraints:CGSizeMake(ScreenWidth-20, CGFLOAT_MAX) limitedToNumberOfLines:3].height;
159 | self.label.frame = CGRectMake(10, 10, ScreenWidth - 20, height);
160 | }
161 | default:
162 | break;
163 | }
164 | }
165 |
166 | - (void)clickTruncationToken:(CJLabelLinkModel *)linkModel {
167 | NSLog(@"点击了 `……全文`");
168 | [self truncationTokenRightBarButtonItem:YES];
169 | NSAttributedString *text = linkModel.label.attributedText;
170 | linkModel.label.numberOfLines = 0;
171 | CGFloat height = [CJLabel sizeWithAttributedString:text withConstraints:CGSizeMake(ScreenWidth-20, CGFLOAT_MAX) limitedToNumberOfLines:0].height;
172 | linkModel.label.frame = CGRectMake(10, 10, ScreenWidth - 20, height);
173 | [linkModel.label flushText];
174 | }
175 |
176 | - (void)truncationTokenRightBarButtonItem:(BOOL)show {
177 | if (show) {
178 | UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithTitle:@"收起" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)];
179 | item.tag = 400;
180 | self.navigationItem.rightBarButtonItem = item;
181 | }else{
182 | self.navigationItem.rightBarButtonItem = nil;
183 | }
184 | }
185 |
186 | - (void)rightBarButtonItems {
187 | UIBarButtonItem *item1 = [[UIBarButtonItem alloc]initWithTitle:@"居上" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)];
188 | item1.tag = 100;
189 | UIBarButtonItem *item2 = [[UIBarButtonItem alloc]initWithTitle:@"居中" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)];
190 | item2.tag = 200;
191 | UIBarButtonItem *item3 = [[UIBarButtonItem alloc]initWithTitle:@"居下" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)];
192 | item3.tag = 300;
193 | self.navigationItem.rightBarButtonItems = @[item3,item2,item1];
194 | }
195 |
196 | - (void)itemClick:(UIBarButtonItem *)item {
197 |
198 | if (item.tag == 100) {
199 | self.label.verticalAlignment = CJVerticalAlignmentTop;
200 | }else if (item.tag == 200) {
201 | self.label.verticalAlignment = CJVerticalAlignmentCenter;
202 | }else if (item.tag == 300) {
203 | self.label.verticalAlignment = CJVerticalAlignmentBottom;
204 | }else if (item.tag == 400) {
205 | self.label.numberOfLines = 3;
206 | CGFloat height = [CJLabel sizeWithAttributedString:self.label.attributedText withConstraints:CGSizeMake(ScreenWidth-20, CGFLOAT_MAX) limitedToNumberOfLines:3].height;
207 | self.label.frame = CGRectMake(10, 10, ScreenWidth - 20, height);
208 | [self.label flushText];
209 | [self truncationTokenRightBarButtonItem:NO];
210 | }
211 |
212 | }
213 |
214 | - (void)clickLink:(CJLabelLinkModel *)linkModel {
215 | NSString *title = [NSString stringWithFormat:@"点击链点 %@",linkModel.attributedString.string];
216 | NSString *parameter = [NSString stringWithFormat:@"自定义参数:%@",linkModel.parameter];
217 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:parameter preferredStyle:UIAlertControllerStyleAlert];
218 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
219 | [alert addAction:cancel];
220 | [self presentViewController:alert animated:YES completion:nil];
221 | }
222 |
223 | - (void)clicklongPressLink:(CJLabelLinkModel *)linkModel {
224 | NSString *title = [NSString stringWithFormat:@"长按点击: %@",linkModel.parameter];
225 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
226 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
227 | [alert addAction:cancel];
228 | [self presentViewController:alert animated:YES completion:nil];
229 |
230 | }
231 |
232 | @end
233 |
--------------------------------------------------------------------------------
/Example/CJLabelTest/SecondDetailViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // DetailViewController.m
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 2017/4/28.
6 | // Copyright © 2017年 C.K.Lian. All rights reserved.
7 | //
8 |
9 | #import "SecondDetailViewController.h"
10 | #import "CJLabel.h"
11 | #import "Common.h"
12 |
13 |
14 | @interface SecondDetailViewController ()
15 | @property (nonatomic, weak) IBOutlet CJLabel *label;
16 | @property (nonatomic, strong) CJLabel *secondLabel;
17 | @property (nonatomic, strong) NSMutableAttributedString *attStr;
18 | @property (nonatomic, strong) CJLabelConfigure *configure;
19 |
20 | @property (nonatomic, weak) IBOutlet UILabel *label2;
21 | @end
22 |
23 | @implementation SecondDetailViewController
24 |
25 | - (void)dealloc {
26 | NSLog(@"%@ dealloc",NSStringFromClass([self class]));
27 | }
28 |
29 | - (void)viewDidLoad {
30 | [super viewDidLoad];
31 | self.edgesForExtendedLayout = UIRectEdgeNone;
32 | self.automaticallyAdjustsScrollViewInsets = NO;
33 |
34 | self.label.numberOfLines = 0;
35 |
36 | [self handleContent:self.content];
37 |
38 | }
39 |
40 | - (void)didReceiveMemoryWarning {
41 | [super didReceiveMemoryWarning];
42 | }
43 |
44 | - (void)handleContent:(NSMutableAttributedString *)content {
45 | NSMutableAttributedString *attStr = content;
46 |
47 | CJLabelConfigure *configure = [CJLabel configureAttributes:nil isLink:NO activeLinkAttributes:nil parameter:nil clickLinkBlock:nil longPressBlock:nil];
48 |
49 | switch (self.index) {
50 | case 0:
51 | {
52 | self.navigationItem.title = @"富文本展示";
53 | self.label.enableCopy = YES;
54 | //初始化配置
55 | CJLabelConfigure *configure = [CJLabel configureAttributes:nil isLink:NO activeLinkAttributes:nil parameter:nil clickLinkBlock:nil longPressBlock:nil];
56 | //设置 'CJLabel' 字符不可点击
57 | configure.isLink = NO;
58 | attStr = [CJLabel configureAttrString:attStr withString:@"CJLabel" sameStringEnable:YES configure:configure];
59 | //设置 `不同字体` 显示为粗体17的字号
60 | configure.attributes = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:18]};
61 | attStr = [CJLabel configureAttrString:attStr withString:@"不同字体" sameStringEnable:NO configure:configure];
62 | //设置 `字体背景色` 填充背景色,以及填充区域圆角
63 | configure.attributes = @{kCJBackgroundFillColorAttributeName:[UIColor colorWithWhite:0.5 alpha:1],kCJBackgroundLineCornerRadiusAttributeName:@(0)};
64 | attStr = [CJLabel configureAttrString:attStr withString:@"字体背景色" sameStringEnable:NO configure:configure];
65 | //设置 `字体边框线` 边框线
66 | configure.attributes = @{kCJBackgroundStrokeColorAttributeName:[UIColor orangeColor]};
67 | attStr = [CJLabel configureAttrString:attStr withString:@"字体边框线" sameStringEnable:NO configure:configure];
68 | //指定文本添加删除线
69 | configure.attributes = @{kCJStrikethroughStyleAttributeName:@(1),
70 | kCJStrikethroughColorAttributeName:[UIColor redColor]};
71 | attStr = [CJLabel configureAttrString:attStr withString:@"对指定文本添加删除线" sameStringEnable:NO configure:configure];
72 | //指定位置插入图片
73 | NSRange imgRange = [attStr.string rangeOfString:@"插入图片"];
74 | [configure removeAttributesForKey:kCJBackgroundStrokeColorAttributeName];
75 | [configure removeAttributesForKey:kCJStrikethroughStyleAttributeName];
76 | [configure removeAttributesForKey:kCJStrikethroughColorAttributeName];
77 |
78 | attStr = [CJLabel insertViewAtAttrString:attStr view:@"CJLabel.png" viewSize:CGSizeMake(55, 45) atIndex:(imgRange.location+imgRange.length) lineAlignment:CJVerticalAlignmentBottom configure:configure];
79 | //设置内边距
80 | self.label.textInsets = UIEdgeInsetsMake(10, 10, 10, 0);
81 | self.label.attributedText = attStr;
82 | self.attStr = attStr;
83 | }
84 | break;
85 |
86 | case 2:
87 | {
88 | self.navigationItem.title = @"点击链点";
89 | UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithTitle:@"删除链点" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)];
90 | item.tag = 100;
91 | self.navigationItem.rightBarButtonItem = item;
92 |
93 | attStr = [self configureLabelContent:attStr configure:configure];
94 | self.label.attributedText = attStr;
95 | self.label.enableCopy = YES;
96 | self.attStr = attStr;
97 | }
98 | break;
99 |
100 | case 4:
101 | {
102 | self.navigationItem.title = @"图文混排";
103 |
104 | UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithTitle:@"···" style:UIBarButtonItemStylePlain target:self action:@selector(itemClick:)];
105 | item.tag = 200;
106 | self.navigationItem.rightBarButtonItem = item;
107 |
108 | self.attStr = attStr;
109 | self.configure = configure;
110 |
111 | [self configureLabelContent:attStr verticalAlignment:CJVerticalAlignmentBottom configure:configure];
112 | self.label.enableCopy = YES;
113 | break;
114 | }
115 |
116 | case 6:
117 | {
118 | self.navigationItem.title = @"指定文字不换行";
119 |
120 | self.attStr = attStr;
121 | self.configure = configure;
122 |
123 | NSMutableAttributedString *resultStr = [[NSMutableAttributedString alloc]init];
124 |
125 | NSRange lastRange = NSMakeRange(0, 0);
126 | NSArray *ary = [CJLabel sameLinkStringRangeArray:@"#CJLabel#" inAttString:attStr];
127 | for (NSValue *value in ary) {
128 | NSRange range = [value rangeValue];
129 |
130 | if (resultStr.length != range.location) {
131 | NSInteger length = range.location - (lastRange.location+lastRange.length);
132 | NSAttributedString *str = [attStr attributedSubstringFromRange:NSMakeRange(lastRange.location+lastRange.length, length)];
133 | [resultStr appendAttributedString:str];
134 |
135 | configure.attributes = @{NSForegroundColorAttributeName:[UIColor blueColor],
136 | NSFontAttributeName:[UIFont boldSystemFontOfSize:13]};
137 | configure.activeLinkAttributes = @{NSForegroundColorAttributeName:[UIColor redColor],
138 | NSFontAttributeName:[UIFont boldSystemFontOfSize:13]};
139 | configure.isLink = YES;
140 | NSAttributedString *label = [[NSAttributedString alloc]initWithString:@"#CJLabel#"];
141 | NSMutableAttributedString *labelStr = [CJLabel initWithNonLineWrapAttributedString:label textInsets:UIEdgeInsetsZero configure:configure];
142 | [resultStr appendAttributedString:labelStr];
143 | lastRange = range;
144 |
145 | }
146 | }
147 | self.label.textInsets = UIEdgeInsetsMake(10, 10, 10, 10);
148 | self.label.enableCopy = YES;
149 | self.label.attributedText = resultStr;
150 | break;
151 | }
152 |
153 | default:
154 | break;
155 | }
156 | }
157 |
158 | - (NSMutableAttributedString *)configureLabelContent:(NSMutableAttributedString *)attStr configure:(CJLabelConfigure *)configure {
159 |
160 | __weak typeof(self)wSelf = self;
161 | //设置点击链点属性
162 | configure.attributes = @{
163 | NSForegroundColorAttributeName:[UIColor blueColor],
164 | NSFontAttributeName:[UIFont boldSystemFontOfSize:15],
165 | kCJBackgroundStrokeColorAttributeName:[UIColor orangeColor],
166 | kCJBackgroundLineWidthAttributeName:@(1),
167 | kCJBackgroundFillColorAttributeName:[UIColor lightGrayColor]
168 | };
169 | //设置点击高亮属性
170 | configure.activeLinkAttributes = @{
171 | NSForegroundColorAttributeName:[UIColor redColor],
172 | kCJActiveBackgroundStrokeColorAttributeName:[UIColor redColor],
173 | kCJActiveBackgroundFillColorAttributeName:UIRGBColor(247,231,121,1)
174 | };
175 | //链点自定义参数
176 | configure.parameter = @"参数为字符串";
177 | //点击回调
178 | configure.clickLinkBlock = ^(CJLabelLinkModel *linkModel) {
179 | [wSelf clickLink:linkModel isImage:NO];
180 | };
181 | //长按回调
182 | configure.longPressBlock = ^(CJLabelLinkModel *linkModel) {
183 | [wSelf clicklongPressLink:linkModel isImage:NO];
184 | };
185 | //设置为可点击链点
186 | configure.isLink = YES;
187 |
188 | NSAttributedString *link = [CJLabel initWithNSString:@"CJLabel" strIdentifier:@"aa" configure:configure];
189 | attStr = [CJLabel configureAttrString:attStr withAttributedString:link strIdentifier:@"aa" sameStringEnable:YES configure:configure];
190 |
191 | return attStr;
192 | }
193 |
194 | - (void)configureLabelContent:(NSMutableAttributedString *)attStr verticalAlignment:(CJLabelVerticalAlignment)verticalAlignment configure:(CJLabelConfigure *)configure {
195 |
196 | __weak typeof(self)wSelf = self;
197 | attStr = [self configureLabelContent:attStr configure:configure];
198 |
199 | //设置图片点击链点属性
200 | NSRange imageRange = [attStr.string rangeOfString:@"图片"];
201 | CJLabelConfigure *imgConfigure =
202 | [CJLabel configureAttributes:@{kCJBackgroundStrokeColorAttributeName:[UIColor redColor],
203 | kCJBackgroundLineWidthAttributeName:@(1),
204 | kCJBackgroundLineCornerRadiusAttributeName:@(2)}
205 | isLink:YES
206 | activeLinkAttributes:@{kCJActiveBackgroundStrokeColorAttributeName:[UIColor lightGrayColor]}
207 | parameter:@"图片参数"
208 | clickLinkBlock:^(CJLabelLinkModel *linkModel){
209 | [wSelf clickLink:linkModel isImage:YES];
210 | }
211 | longPressBlock:^(CJLabelLinkModel *linkModel){
212 | [wSelf clicklongPressLink:linkModel isImage:YES];
213 | }];
214 |
215 | UIImageView *imgView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"CJLabel.png"]];
216 | imgView.contentMode = UIViewContentModeScaleToFill;
217 | imgView.clipsToBounds = YES;
218 | attStr = [CJLabel insertViewAtAttrString:attStr view:imgView viewSize:CGSizeMake(100, 40) atIndex:(imageRange.location+imageRange.length) lineAlignment:verticalAlignment configure:imgConfigure];
219 |
220 | // attStr = [CJLabel insertImageAtAttrString:attStr image:@"CJLabel.png" imageSize:CGSizeMake(45, 35) atIndex:(imageRange.location+imageRange.length) imagelineAlignment:verticalAlignment configure:imgConfigure];
221 |
222 | self.label.attributedText = attStr;
223 | }
224 |
225 | - (void)itemClick:(UIBarButtonItem *)item {
226 | if (item.tag == 100) {
227 | //移除指定链点
228 | NSArray *linkRangeArray = [CJLabel sameLinkStringRangeArray:@"CJLabel" inAttString:self.attStr];
229 | [self.label removeLinkAtRange:[linkRangeArray[0] rangeValue]];
230 | item.enabled = NO;
231 | }
232 | else if (item.tag == 200) {
233 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"图片所在行垂直对齐方式" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
234 | [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
235 | }]];
236 | [alert addAction:[UIAlertAction actionWithTitle:@"顶部对齐" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
237 | [self configureLabelContent:self.attStr verticalAlignment:CJVerticalAlignmentTop configure:self.configure];
238 | }]];
239 | [alert addAction:[UIAlertAction actionWithTitle:@"居中对齐" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
240 | [self configureLabelContent:self.attStr verticalAlignment:CJVerticalAlignmentCenter configure:self.configure];
241 | }]];
242 | [alert addAction:[UIAlertAction actionWithTitle:@"底部对齐" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
243 | [self configureLabelContent:self.attStr verticalAlignment:CJVerticalAlignmentBottom configure:self.configure];
244 | }]];
245 | [self presentViewController:alert animated:YES completion:nil];
246 | }
247 | }
248 |
249 | - (void)clickLink:(CJLabelLinkModel *)linkModel isImage:(BOOL)isImage {
250 | NSString *title = [NSString stringWithFormat:@"点击链点 %@",linkModel.attributedString.string];
251 | if (isImage) {
252 | title = [NSString stringWithFormat:@"点击链点图片:%@",linkModel.insertView];
253 | }
254 | NSString *parameter = [NSString stringWithFormat:@"自定义参数:%@",linkModel.parameter];
255 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:parameter preferredStyle:UIAlertControllerStyleAlert];
256 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
257 | [alert addAction:cancel];
258 | [self presentViewController:alert animated:YES completion:nil];
259 | }
260 |
261 | - (void)clicklongPressLink:(CJLabelLinkModel *)linkModel isImage:(BOOL)isImage {
262 | NSString *title = [NSString stringWithFormat:@"长按点击: %@",linkModel.attributedString.string];
263 | if (isImage) {
264 | title = [NSString stringWithFormat:@"长按点击图片:%@",linkModel.insertView];
265 | }
266 |
267 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet];
268 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
269 | [alert addAction:cancel];
270 | [self presentViewController:alert animated:YES completion:nil];
271 | }
272 |
273 | @end
274 |
--------------------------------------------------------------------------------
/CJLabel/CJLabel.h:
--------------------------------------------------------------------------------
1 | //
2 | // CJLabel.h
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 17/3/31.
6 | // Copyright © 2017年 ChiJinLian. All rights reserved.
7 | //
8 | /*
9 | ____ _ _ _ _
10 | / ___| | | | | __ _ | |__ ___ | |
11 | | | _ | | | | / _` | | '_ \ / _ \ | |
12 | | |___ | |_| | | |___ | (_| | | |_) | | __/ | |
13 | \____| \___/ |_____| \__,_| |_.__/ \___| |_|
14 | */
15 |
16 | #import
17 | #import
18 | #import "CJLabelConfigure.h"
19 |
20 | @class CJLabel;
21 | @class CJLabelLinkModel;
22 |
23 | @protocol CJLabelLinkDelegate
24 | @optional
25 | /**
26 | 点击链点回调
27 |
28 | @param label CJLabel
29 | @param linkModel 链点model
30 | */
31 | - (void)CJLable:(CJLabel *)label didClickLink:(CJLabelLinkModel *)linkModel;
32 |
33 | /**
34 | 长按点击链点回调
35 |
36 | @param label CJLabel
37 | @param linkModel 链点model
38 | */
39 | - (void)CJLable:(CJLabel *)label didLongPressLink:(CJLabelLinkModel *)linkModel;
40 | @end
41 |
42 |
43 | /**
44 | * CJLabel 继承自 UILabel,支持富文本展示、图文混排、添加自定义点击链点以及选择复制等功能。
45 | *
46 | *
47 | * CJLabel 与 UILabel 不同点:
48 | *
49 | 1. 禁止使用`-init`初始化!!
50 | 2. `enableCopy` 长按或双击可唤起`UIMenuController`进行选择、全选、复制文本操作
51 | 3. `attributedText` 与 `text` 均可设置富文本
52 | 4. 不支持`NSAttachmentAttributeName`,`NSTextAttachment`!!显示自定义view请调用:
53 | `+ initWithView:viewSize:lineAlignment:configure:`或者
54 | `+ insertViewAtAttrString:view:viewSize:atIndex:lineAlignment:configure:`方法初始化`NSAttributedString`后显示
55 | 5. `extendsLinkTouchArea`设置是否扩大链点点击识别范围
56 | 6. `shadowRadius`设置文本阴影模糊半径
57 | 7. `textInsets` 设置文本内边距
58 | 8. `verticalAlignment` 设置垂直方向的文本对齐方式。
59 | 注意与显示图片时候的`imagelineAlignment`作区分,`self.verticalAlignment`对应的是整体文本在垂直方向的对齐方式,而`imagelineAlignment`只对图片所在行的垂直对齐方式有效
60 | 9. `delegate` 点击链点代理
61 | 10. `kCJBackgroundFillColorAttributeName` 背景填充颜色,属性优先级低于`NSBackgroundColorAttributeName`,如果设置`NSBackgroundColorAttributeName`会忽略`kCJBackgroundFillColorAttributeName`的设置
62 | 11. `kCJBackgroundStrokeColorAttributeName ` 背景边框线颜色
63 | 12. `kCJBackgroundLineWidthAttributeName ` 背景边框线宽度
64 | 13. `kCJBackgroundLineCornerRadiusAttributeName ` 背景边框线圆角弧度
65 | 14. `kCJActiveBackgroundFillColorAttributeName ` 点击时候的背景填充颜色属性优先级同
66 | `kCJBackgroundFillColorAttributeName`
67 | 15. `kCJActiveBackgroundStrokeColorAttributeName ` 点击时候的背景边框线颜色
68 | 16. 支持添加自定义样式、可点击(长按)的文本点击链点
69 | *
70 | *
71 | * CJLabel 已知bug:
72 | *
73 | `numberOfLines`大于0且小于实际`label.numberOfLines`,同时`verticalAlignment`不等于`CJContentVerticalAlignmentTop`时:
74 | 文本显示位置有偏差
75 | *
76 | */
77 | @interface CJLabel : UILabel
78 |
79 | /**
80 | * 指定初始化函数为 -initWithFrame: 或 -initWithCoder:
81 | * 直接调用 init 会忽略相关属性的设置,所以不能直接调用 init 初始化.
82 | */
83 | - (instancetype)init NS_UNAVAILABLE;
84 |
85 | /**
86 | * 对应UILabel的attributedText属性
87 | */
88 | @property (readwrite, nonatomic, copy) NSAttributedString *attributedText;
89 | /**
90 | * 对应UILabel的text属性
91 | */
92 | @property (readwrite, nonatomic, copy) id text;
93 | /**
94 | * 是否加大点击响应范围,类似于UIWebView的链点点击效果,默认NO
95 | */
96 | @property (readwrite, nonatomic, assign) IBInspectable BOOL extendsLinkTouchArea;
97 | /**
98 | * 阴影模糊半径,值为0表示没有模糊,值越大越模糊,该值不能为负, 默认值为0。
99 | * 可与 `shadowColor`、`shadowOffset` 配合设置
100 | */
101 | @property (readwrite, nonatomic, assign) CGFloat shadowRadius;
102 | /**
103 | * 绘制文本的内边距,默认UIEdgeInsetsZero
104 | */
105 | @property (readwrite, nonatomic, assign) UIEdgeInsets textInsets;
106 | /**
107 | * 当text rect 小于 label frame 时文本在垂直方向的对齐方式,默认 CJVerticalAlignmentCenter
108 | */
109 | @property (readwrite, nonatomic, assign) CJLabelVerticalAlignment verticalAlignment;
110 | /**
111 | 点击链点代理对象
112 | */
113 | @property (readwrite, nonatomic, weak) id delegate;
114 | /**
115 | 是否支持选择复制,默认NO
116 | */
117 | @property (readwrite, nonatomic, assign) IBInspectable BOOL enableCopy;
118 |
119 | /**
120 | 设置`self.lineBreakMode`时候的自定义字符,默认值为"…"
121 | 只针对`self.lineBreakMode`的以下三种值有效
122 | NSLineBreakByTruncatingHead, // Truncate at head of line: "…wxyz"
123 | NSLineBreakByTruncatingTail, // Truncate at tail of line: "abcd…"
124 | NSLineBreakByTruncatingMiddle // Truncate middle of line: "ab…yz"
125 | */
126 | @property (readwrite, nonatomic, strong) NSAttributedString *attributedTruncationToken;
127 |
128 | /**
129 | 根据NSAttributedString计算CJLabel的size大小
130 |
131 | @param attributedString NSAttributedString字符串
132 | @param size 预计大小(比如:CGSizeMake(320, CGFLOAT_MAX))
133 | @param numberOfLines 指定行数(0表示不限制)
134 | @return 结果size
135 | */
136 | + (CGSize)sizeWithAttributedString:(NSAttributedString *)attributedString
137 | withConstraints:(CGSize)size
138 | limitedToNumberOfLines:(NSUInteger)numberOfLines;
139 |
140 |
141 | /**
142 | 根据NSAttributedString计算CJLabel的size大小
143 |
144 | @param attributedString NSAttributedString字符串
145 | @param size 预计大小(比如:CGSizeMake(320, CGFLOAT_MAX))
146 | @param numberOfLines 指定行数(0表示不限制)
147 | @param textInsets CJLabel的内边距
148 | @return 结果size
149 | */
150 | + (CGSize)sizeWithAttributedString:(NSAttributedString *)attributedString
151 | withConstraints:(CGSize)size
152 | limitedToNumberOfLines:(NSUInteger)numberOfLines
153 | textInsets:(UIEdgeInsets)textInsets;
154 |
155 | /**
156 | 初始化配置实例
157 |
158 | @param attributes 普通属性
159 | @param isLink 是否是点击链点
160 | @param activeLinkAttributes 点击高亮属性
161 | @param parameter 链点参数
162 | @param clickLinkBlock 链点点击block
163 | @param longPressBlock 链点长按block
164 | @return CJLabelConfigure实例
165 | */
166 | + (CJLabelConfigure *)configureAttributes:(NSDictionary *)attributes
167 | isLink:(BOOL)isLink
168 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes
169 | parameter:(id)parameter
170 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
171 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock;
172 |
173 | /**
174 | 根据图片名或UIImage初始化NSAttributedString
175 | (废弃原因:该方法显示图片只能是UIViewContentModeScaleToFill模式,而且不能更改)
176 | */
177 | + (NSMutableAttributedString *)initWithImage:(id)image
178 | imageSize:(CGSize)size
179 | imagelineAlignment:(CJLabelVerticalAlignment)lineAlignment
180 | configure:(CJLabelConfigure *)configure __deprecated_msg("Use + initWithView:viewSize:lineAlignment:configure: instead");
181 |
182 | /**
183 | 根据自定义View初始化NSAttributedString
184 |
185 | @param view 需要插入的view(包括UIImage,NSString图片名称,UIView)
186 | @param size view的显示区域大小
187 | @param lineAlignment 插入view所在行,与文字在垂直方向的对齐方式(只针对当前行)
188 | @param configure 链点配置
189 | @return NSAttributedString
190 | */
191 | + (NSMutableAttributedString *)initWithView:(id)view
192 | viewSize:(CGSize)size
193 | lineAlignment:(CJLabelVerticalAlignment)lineAlignment
194 | configure:(CJLabelConfigure *)configure;
195 |
196 | /**
197 | 在指定位置插入图片,并设置图片链点属性(已废弃,废弃原因:该方法显示图片只能是UIViewContentModeScaleToFill模式,而且不能更改)
198 | 注意!!!插入图片, 如果设置 NSParagraphStyleAttributeName 属性,
199 | 请保证 paragraph.lineBreakMode = NSLineBreakByCharWrapping,不然当Label的宽度不够显示内容或图片时,不会自动换行, 部分图片将会看不见
200 | 默认 paragraph.lineBreakMode = NSLineBreakByCharWrapping
201 | */
202 | + (NSMutableAttributedString *)insertImageAtAttrString:(NSAttributedString *)attrStr
203 | image:(id)image
204 | imageSize:(CGSize)size
205 | atIndex:(NSUInteger)loc
206 | imagelineAlignment:(CJLabelVerticalAlignment)lineAlignment
207 | configure:(CJLabelConfigure *)configure __deprecated_msg("Use + insertViewAtAttrString:view:viewSize:atIndex:imagelineAlignment:configure: instead");
208 |
209 | /**
210 | 在指定位置插入任意UIView
211 |
212 | 注意!!!
213 | 1、插入任意View, 如果设置 NSParagraphStyleAttributeName 属性,
214 | 请保证 paragraph.lineBreakMode = NSLineBreakByCharWrapping,不然当Label的宽度不够显示内容或view时,不会自动换行, 部分view将会看不见
215 | 默认 paragraph.lineBreakMode = NSLineBreakByCharWrapping
216 | 2、插入任意View,如果 configure.isLink = YES,那么将优先响应CJLabel的点击响应
217 |
218 | @param attrStr 源字符串
219 | @param view 需要插入的view(包括UIImage,NSString图片名称,UIView)
220 | @param size view的显示区域大小
221 | @param loc 插入位置
222 | @param lineAlignment 插入view所在行,与文字在垂直方向的对齐方式(只针对当前行)
223 | @param configure 链点配置
224 | @return NSAttributedString
225 | */
226 | + (NSMutableAttributedString *)insertViewAtAttrString:(NSAttributedString *)attrStr
227 | view:(id)view
228 | viewSize:(CGSize)size
229 | atIndex:(NSUInteger)loc
230 | lineAlignment:(CJLabelVerticalAlignment)lineAlignment
231 | configure:(CJLabelConfigure *)configure;
232 |
233 | /**
234 | 设置指定NSRange属性
235 |
236 | @param attrStr 需要设置的源NSAttributedString
237 | @param range 指定NSRange
238 | @param configure 链点配置
239 | @return NSAttributedString
240 | */
241 | + (NSMutableAttributedString *)configureAttrString:(NSAttributedString *)attrStr
242 | atRange:(NSRange)range
243 | configure:(CJLabelConfigure *)configure;
244 |
245 | /**
246 | 根据NSString初始化NSAttributedString
247 | */
248 | + (NSMutableAttributedString *)initWithString:(NSString *)string configure:(CJLabelConfigure *)configure;
249 |
250 |
251 | /**
252 | 根据NSString初始化NSAttributedString
253 |
254 | @param string 指定的NSString
255 | @param strIdentifier 设置链点的唯一标识(用来区分不同的NSString,比如重名的 "@王小明" ,此时代表了不同的用户,不应该设置相同属性)
256 | @param configure 链点配置
257 | @return NSAttributedString
258 | */
259 | + (NSMutableAttributedString *)initWithNSString:(NSString *)string
260 | strIdentifier:(NSString *)strIdentifier
261 | configure:(CJLabelConfigure *)configure;
262 |
263 | /**
264 | 对跟string相同的文本设置链点属性
265 |
266 | @param attrStr 需要设置的源NSAttributedString
267 | @param string 指定字符串
268 | @param configure 链点配置
269 | @param sameStringEnable 文本中所有与string相同的文本是否同步设置属性,sameStringEnable=NO 时取文本中首次匹配的NSAttributedString
270 | @return NSAttributedString
271 | */
272 | + (NSMutableAttributedString *)configureAttrString:(NSAttributedString *)attrStr
273 | withString:(NSString *)string
274 | sameStringEnable:(BOOL)sameStringEnable
275 | configure:(CJLabelConfigure *)configure;
276 |
277 | /**
278 | 根据NSAttributedString初始化NSAttributedString
279 |
280 | @param attributedString 指定的NSAttributedString
281 | @param configure 链点配置
282 | @param strIdentifier 设置链点的唯一标识(用来区分不同的NSAttributedString,比如重名的 "@王小明" ,此时代表了不同的用户,不应该设置相同属性)
283 | @return NSAttributedString
284 | */
285 | + (NSMutableAttributedString *)initWithAttributedString:(NSAttributedString *)attributedString
286 | strIdentifier:(NSString *)strIdentifier
287 | configure:(CJLabelConfigure *)configure;
288 |
289 | /**
290 | 对指定strIdentifier标识的attributedString设置链点属性,如果存在多个相同的文本,可以同时设置
291 |
292 | @param attrString 需要设置的源NSAttributedString
293 | @param attributedString 指定的NSAttributedString
294 | @param strIdentifier 设置链点的唯一标识(用来区分不同的NSAttributedString,比如重名的 "@王小明" ,此时代表了不同的用户,不应该设置相同属性)
295 | @param sameStringEnable 文本中相同的NSAttributedString是否同步设置属性,sameStringEnable=NO 时取文本中首次匹配的NSAttributedString
296 | @return NSAttributedString
297 | */
298 | + (NSMutableAttributedString *)configureAttrString:(NSAttributedString *)attrString
299 | withAttributedString:(NSAttributedString *)attributedString
300 | strIdentifier:(NSString *)strIdentifier
301 | sameStringEnable:(BOOL)sameStringEnable
302 | configure:(CJLabelConfigure *)configure;
303 |
304 |
305 | /// 根据NSAttributedString初始化不可换行的NSAttributedString
306 | /// @param attString 指定的NSAttributedString
307 | /// @param textInsets 不可换行字符的内边距
308 | /// @param configure 链点配置
309 | + (NSMutableAttributedString *)initWithNonLineWrapAttributedString:(NSAttributedString *)attString
310 | textInsets:(UIEdgeInsets)textInsets
311 | configure:(CJLabelConfigure *)configure;
312 |
313 | /**
314 | 获取指定NSAttributedString中跟linkString相同的NSValue数组,NSValue值为对应的NSRange
315 |
316 | @param linkString 需要寻找的string
317 | @param attString 指定NSAttributedString
318 | @return NSRange的数组
319 | */
320 | + (NSArray *)sameLinkStringRangeArray:(NSString *)linkString inAttString:(NSAttributedString *)attString;
321 |
322 | /**
323 | 获取指定NSAttributedString中跟linkAttString相同的NSRange数组,NSValue值为对应的NSRange
324 |
325 | @param linkAttString 需要寻找的NSAttributedString
326 | @param strIdentifier 链点标识
327 | @param attString 指定NSAttributedString
328 | @return NSRange的数组
329 | */
330 | + (NSArray *)samelinkAttStringRangeArray:(NSAttributedString *)linkAttString strIdentifier:(NSString *)strIdentifier inAttString:(NSAttributedString *)attString;
331 |
332 |
333 | /**
334 | * 移除指定range的点击链点
335 | *
336 | * @param range 移除链点位置
337 | *
338 | * @return 返回新的NSAttributedString
339 | */
340 | - (NSAttributedString *)removeLinkAtRange:(NSRange)range;
341 |
342 | /**
343 | * 移除所有点击链点
344 | *
345 | * @return 返回新的NSAttributedString
346 | */
347 | - (NSAttributedString *)removeAllLink;
348 |
349 | /**
350 | 刷新文本
351 | */
352 | - (void)flushText;
353 |
354 | @end
355 |
356 |
357 | /**
358 | 背景填充颜色。值为UIColor。默认 `nil`。
359 | 该属性优先级低于NSBackgroundColorAttributeName,如果设置NSBackgroundColorAttributeName会覆盖kCJBackgroundFillColorAttributeName
360 | */
361 | extern NSString * const kCJBackgroundFillColorAttributeName;
362 |
363 | /**
364 | 背景边框线颜色。值为UIColor。默认 `nil`
365 | */
366 | extern NSString * const kCJBackgroundStrokeColorAttributeName;
367 |
368 | /**
369 | 背景边框线宽度。值为NSNumber。默认 `1.0f`
370 | */
371 | extern NSString * const kCJBackgroundLineWidthAttributeName;
372 |
373 | /**
374 | 背景边框线圆角角度。值为NSNumber。默认 `5.0f`
375 | */
376 | extern NSString * const kCJBackgroundLineCornerRadiusAttributeName;
377 |
378 | /**
379 | 点击时候的背景填充颜色。值为UIColor。默认 `nil`。
380 | 该属性优先级低于NSBackgroundColorAttributeName,如果设置NSBackgroundColorAttributeName会覆盖kCJActiveBackgroundFillColorAttributeName
381 | */
382 | extern NSString * const kCJActiveBackgroundFillColorAttributeName;
383 |
384 | /**
385 | 点击时候的背景边框线颜色。值为UIColor。默认 `nil`
386 | */
387 | extern NSString * const kCJActiveBackgroundStrokeColorAttributeName;
388 |
389 | /**
390 | 删除线宽度。值为NSNumber。默认 `0.0f`,表示无删除线
391 | */
392 | extern NSString * const kCJStrikethroughStyleAttributeName;
393 |
394 | /**
395 | 删除线颜色。值为UIColor。默认 `[UIColor blackColor]`。
396 | */
397 | extern NSString * const kCJStrikethroughColorAttributeName;
398 |
399 | /**
400 | 对NSAttributedString文本设置链点属性时候的唯一标识
401 | */
402 | extern NSString * const kCJLinkStringIdentifierAttributesName;
403 |
--------------------------------------------------------------------------------
/CJLabel/CJLabelConfigure.h:
--------------------------------------------------------------------------------
1 | //
2 | // CJLabelConfigure.h
3 | // CJLabelTest
4 | //
5 | // Created by ChiJinLian on 2017/4/13.
6 | // Copyright © 2017年 ChiJinLian. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 | @class CJLabelLinkModel;
13 | @class CJLabel;
14 |
15 | static char kAssociatedUITouchKey;
16 |
17 | extern NSString * const kCJInsertViewTag;
18 |
19 | #define CJLabelIsNull(a) ((a)==nil || (a)==NULL || (NSNull *)(a)==[NSNull null])
20 | #define CJUIRGBColor(r,g,b,a) ([UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a])
21 |
22 | typedef void (^CJLabelLinkModelBlock)(CJLabelLinkModel *linkModel);
23 |
24 | /**
25 | 当text bounds小于label bounds时,文本的垂直对齐方式
26 | */
27 | typedef NS_ENUM(NSInteger, CJLabelVerticalAlignment) {
28 | CJVerticalAlignmentCenter = 0,//垂直居中
29 | CJVerticalAlignmentTop = 1,//居上
30 | CJVerticalAlignmentBottom = 2,//靠下
31 | };
32 |
33 | /**
34 | 当CTLine包含插入图片时,描述当前行文字在垂直方向的对齐方式
35 | */
36 | struct CJCTLineVerticalLayout {
37 | CFIndex line;//第几行
38 | CGFloat lineAscentAndDescent;//上行高和下行高之和
39 | CGRect lineRect;//当前行对应的CGRect
40 |
41 | CGFloat maxRunHeight;//当前行run的最大高度(不包括图片)
42 | CGFloat maxRunAscent;//CTRun的最大上行高
43 |
44 | CGFloat maxImageHeight;//图片的最大高度
45 | CGFloat maxImageAscent;//图片的最大上行高
46 |
47 | CJLabelVerticalAlignment verticalAlignment;//对齐方式(默认底部对齐)
48 | };
49 | typedef struct CJCTLineVerticalLayout CJCTLineVerticalLayout;
50 |
51 | /**
52 | 设置链点属性辅助类,可设置链点正常属性、点击高亮属性、链点自定义参数、点击回调以及长按回调
53 | */
54 | @interface CJLabelConfigure : NSObject
55 | /**
56 | 设置链点的自定义属性
57 | */
58 | @property (nonatomic, strong) NSDictionary *attributes;
59 | /**
60 | 是否为可点击链点,设置 isLink=YES 时,activeLinkAttributes、parameter、clickLinkBlock、longPressBlock才有效
61 | */
62 | @property (nonatomic, assign) BOOL isLink;
63 | /**
64 | 设置链点点击高亮时候的自定义属性
65 | */
66 | @property (nonatomic, strong) NSDictionary *activeLinkAttributes;
67 | /**
68 | 点击链点的自定义参数
69 | */
70 | @property (nonatomic, strong) id parameter;
71 | /**
72 | 点击链点回调block
73 | */
74 | @property (nonatomic, copy) CJLabelLinkModelBlock clickLinkBlock;
75 | /**
76 | 长按链点的回调block
77 | */
78 | @property (nonatomic, copy) CJLabelLinkModelBlock longPressBlock;
79 |
80 | /**
81 | 添加 attributes 属性
82 |
83 | @param attributes 属性值
84 | @param key 属性
85 | */
86 | - (void)addAttributes:(id)attributes key:(NSString *)key;
87 | /**
88 | 移除 attributes 指定属性
89 |
90 | @param key 属性
91 | */
92 | - (void)removeAttributesForKey:(NSString *)key;
93 | /**
94 | 添加 activeAttributes 属性
95 |
96 | @param activeAttributes 属性值
97 | @param key 属性
98 | */
99 | - (void)addActiveAttributes:(id)activeAttributes key:(NSString *)key;
100 | /**
101 | 移除 activeAttributes 指定属性
102 |
103 | @param key 属性
104 | */
105 | - (void)removeActiveLinkAttributesForKey:(NSString *)key;
106 |
107 | /**
108 | 在指定位置插入图片链点!!!
109 | */
110 | + (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr
111 | addImage:(id)image
112 | imageSize:(CGSize)size
113 | atIndex:(NSUInteger)loc
114 | verticalAlignment:(CJLabelVerticalAlignment)verticalAlignment
115 | linkAttributes:(NSDictionary *)linkAttributes
116 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes
117 | parameter:(id)parameter
118 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
119 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock
120 | islink:(BOOL)isLink;
121 | /**
122 | 根据指定NSRange配置富文本链点!!!
123 | */
124 | + (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr
125 | atRange:(NSRange)range
126 | linkAttributes:(NSDictionary *)linkAttributes
127 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes
128 | parameter:(id)parameter
129 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
130 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock
131 | islink:(BOOL)isLink;
132 |
133 |
134 | /**
135 | 对文本中跟withString相同的文字配置富文本链点!!!
136 | */
137 | + (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr
138 | withString:(NSString *)withString
139 | sameStringEnable:(BOOL)sameStringEnable
140 | linkAttributes:(NSDictionary *)linkAttributes
141 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes
142 | parameter:(id)parameter
143 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
144 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock
145 | islink:(BOOL)isLink;
146 |
147 | /**
148 | 对文本中跟withAttString相同的NSAttributedString配置富文本链点!!!
149 | */
150 | + (NSMutableAttributedString *)configureLinkAttributedString:(NSAttributedString *)attrStr
151 | withAttString:(NSAttributedString *)withAttString
152 | sameStringEnable:(BOOL)sameStringEnable
153 | linkAttributes:(NSDictionary *)linkAttributes
154 | activeLinkAttributes:(NSDictionary *)activeLinkAttributes
155 | parameter:(id)parameter
156 | clickLinkBlock:(CJLabelLinkModelBlock)clickLinkBlock
157 | longPressBlock:(CJLabelLinkModelBlock)longPressBlock
158 | islink:(BOOL)isLink;
159 | /**
160 | 对文本中指定strIdentifier标识的NSAttributedString配置富文本链点!!!
161 | */
162 | + (NSMutableAttributedString *)configureAttrString:(NSAttributedString *)attrString
163 | strIdentifier:(NSString *)strIdentifier
164 | configure:(CJLabelConfigure *)configure
165 | linkRangeAry:(NSArray *)linkRangeAry;
166 |
167 | /**
168 | 生成string链点的NSAttributedString(请保证identifier的唯一性!!!)
169 |
170 | @param string 点击链点的string
171 | @param attrs 链点属性
172 | @param identifier 点击链点的唯一标识
173 | @return 返回点击链点的NSMutableAttributedString
174 | */
175 | + (NSMutableAttributedString *)linkAttStr:(NSString *)string
176 | attributes:(NSDictionary *)attrs
177 | identifier:(NSString *)identifier;
178 |
179 | + (NSArray *)getLinkStringRangeArray:(NSString *)linkString inAttString:(NSAttributedString *)attString;
180 | + (NSArray *)getLinkAttStringRangeArray:(NSAttributedString *)linkAttString inAttString:(NSAttributedString *)attString;
181 |
182 | @end
183 |
184 | /**
185 | 点击链点model
186 | */
187 | @interface CJLabelLinkModel : NSObject
188 | /**
189 | 当前CJLabel实例
190 | */
191 | @property (readonly, nonatomic, strong) CJLabel *label;
192 | /**
193 | 链点文本
194 | */
195 | @property (readonly, nonatomic, strong) NSAttributedString *attributedString;
196 | /**
197 | 链点自定义参数
198 | */
199 | @property (readonly, nonatomic, strong) id parameter;
200 | /**
201 | 链点在整体文本中的range值
202 | */
203 | @property (readonly, nonatomic, assign) NSRange linkRange;
204 | /**
205 | 链点view的Rect(相对于CJLabel坐标的rect)
206 | */
207 | @property (readonly, nonatomic, assign) CGRect insertViewRect;
208 | /**
209 | 插入链点view
210 | */
211 | @property (readonly, nonatomic, strong) id insertView;
212 |
213 | - (instancetype)initWithAttributedString:(NSAttributedString *)attributedString
214 | insertView:(id)insertView
215 | insertViewRect:(CGRect)insertViewRect
216 | parameter:(id)parameter
217 | linkRange:(NSRange)linkRange
218 | label:(CJLabel *)label;
219 | @end
220 |
221 | /**
222 | 响应点击以及指定区域绘制边框线辅助类
223 | */
224 | @interface CJGlyphRunStrokeItem : NSObject
225 |
226 | @property (nonatomic, strong) UIColor *fillColor;//填充背景色
227 | @property (nonatomic, strong) UIColor *strokeColor;//描边边框色
228 | @property (nonatomic, strong) UIColor *activeFillColor;//点击选中时候的填充背景色
229 | @property (nonatomic, strong) UIColor *activeStrokeColor;//点击选中时候的描边边框色
230 | @property (nonatomic, assign) CGFloat strokeLineWidth;//描边边框粗细
231 | @property (nonatomic, assign) CGFloat cornerRadius;//描边圆角
232 | @property (nonatomic, assign) CGFloat strikethroughStyle;//删除线
233 | @property (nonatomic, strong) UIColor *strikethroughColor;//删除线颜色
234 | @property (nonatomic, assign) CGRect runBounds;//描边区域在系统坐标下的rect(原点在左下角)
235 | @property (nonatomic, assign) CGRect locBounds;//描边区域在屏幕坐标下的rect(原点在左上角),相同的一组CTRun,发生了合并
236 | @property (nonatomic, assign) CGRect withOutMergeBounds;//每个字符对应的CTRun 在屏幕坐标下的rect(原点在左上角),没有发生合并
237 |
238 | @property (nonatomic, assign) CGFloat runDescent;//对应的CTRun的下行高
239 | @property (nonatomic, assign) CTRunRef runRef;//对应的CTRun
240 |
241 | @property (nonatomic, strong) id insertView;//插入view
242 | @property (nonatomic, assign) BOOL isInsertView;//是否是插入view
243 | @property (nonatomic, assign) NSRange range;//链点在文本中的range
244 | @property (nonatomic, strong) id parameter;//链点自定义参数
245 | @property (nonatomic, assign) CJCTLineVerticalLayout lineVerticalLayout;//所在CTLine的行高信息结构体
246 | @property (nonatomic, assign) BOOL isLink;//判断是否为点击链点
247 | @property (nonatomic, assign) BOOL needRedrawn;//标记点击该链点是否需要重绘文本
248 | @property (nonatomic, copy) CJLabelLinkModelBlock linkBlock;//点击链点回调
249 | @property (nonatomic, copy) CJLabelLinkModelBlock longPressBlock;//长按点击链点回调
250 | /** 是否不允许换行的字符*/
251 | @property (nonatomic, assign) BOOL isNonLineWrap;
252 |
253 | //与选择复制相关的属性
254 | @property (nonatomic, assign) NSInteger characterIndex;//字符在整段文本中的index值
255 | @property (nonatomic, assign) NSRange characterRange;//字符在整段文本中的range值
256 |
257 | @end
258 |
259 | @interface CJCTLineLayoutModel : NSObject
260 | @property (nonatomic, assign) CFIndex lineIndex;//第几行
261 | @property (nonatomic, assign) CJCTLineVerticalLayout lineVerticalLayout;//所在CTLine的行高信息结构体
262 |
263 | @property (nonatomic, assign) CGFloat selectCopyBackY;//选中后被填充背景色的复制视图的Y坐标
264 | @property (nonatomic, assign) CGFloat selectCopyBackHeight;//选中后被填充背景色的复制视图的高度
265 | @end
266 |
267 | /**
268 | 长按时候显示的放大镜视图
269 | */
270 | @interface CJMagnifierView : UIView
271 | @end
272 | /**
273 | 选择复制时候的操作视图
274 | CJSelectBackView 在 window 层,全局只有一个
275 | CJSelectTextRangeView(选中填充背景色的view)在 CJSelectBackView上,CJMagnifierView(放大镜)则在window上
276 | */
277 | @interface CJSelectCopyManagerView : UIView
278 | @property (nonatomic, strong) CJMagnifierView *magnifierView;//放大镜
279 | + (instancetype)instance;
280 |
281 | /**
282 | CJLabel选中point点对应的文本内容
283 |
284 | @param label CJLabel
285 | @param point 放大点
286 | @param item 放大点对应的CJGlyphRunStrokeItem
287 | @param maxLineWidth CJLabel的最大行宽度
288 | @param allCTLineVerticalArray CJLabel的CTLine数组
289 | @param allRunItemArray CJLabel的CTRun数组
290 | @param hideViewBlock 复制选择view隐藏后的block
291 | */
292 | - (void)showSelectViewInCJLabel:(CJLabel *)label
293 | atPoint:(CGPoint)point
294 | runItem:(CJGlyphRunStrokeItem *)item
295 | maxLineWidth:(CGFloat)maxLineWidth
296 | allCTLineVerticalArray:(NSArray *)allCTLineVerticalArray
297 | allRunItemArray:(NSArray *)allRunItemArray
298 | hideViewBlock:(void(^)(void))hideViewBlock;
299 |
300 | /**
301 | 显示放大镜
302 |
303 | @param label CJLabel
304 | @param point 放大点
305 | @param runItem 放大点对应的CJGlyphRunStrokeItem
306 | */
307 | - (void)showMagnifyInCJLabel:(CJLabel *)label
308 | magnifyPoint:(CGPoint)point
309 | runItem:(CJGlyphRunStrokeItem *)runItem;
310 |
311 | /**
312 | 隐藏选择复制相关的view
313 | */
314 | - (void)hideView;
315 |
316 | + (CJGlyphRunStrokeItem *)currentItem:(CGPoint)point allRunItemArray:(NSArray *)allRunItemArray inset:(CGFloat)inset;
317 | @end
318 |
319 | /// 混排插入任意view时的背景view
320 | @interface CJInsertBackView : UIView
321 | @end
322 |
323 | extern NSString * const kCJImageAttributeName;
324 | extern NSString * const kCJImage;
325 | extern NSString * const kCJImageHeight;
326 | extern NSString * const kCJImageWidth;
327 | extern NSString * const kCJImageLineVerticalAlignment;
328 |
329 | extern NSString * const kCJLinkAttributesName;
330 | extern NSString * const kCJActiveLinkAttributesName;
331 | extern NSString * const kCJIsLinkAttributesName;
332 | //点击链点唯一标识
333 | extern NSString * const kCJLinkIdentifierAttributesName;
334 | extern NSString * const kCJLinkLengthAttributesName;
335 | extern NSString * const kCJLinkRangeAttributesName;
336 | extern NSString * const kCJLinkParameterAttributesName;
337 | extern NSString * const kCJClickLinkBlockAttributesName;
338 | extern NSString * const kCJLongPressBlockAttributesName;
339 | extern NSString * const kCJLinkNeedRedrawnAttributesName;
340 | /**
341 | 不允许换行字符
342 | */
343 | extern NSString * const kCJNonLineWrapAttributesName;
344 |
345 | static CGFloat const CJFLOAT_MAX = 100000;
346 |
347 | static inline CGFLOAT_TYPE CGFloat_ceil(CGFLOAT_TYPE cgfloat) {
348 | #if CGFLOAT_IS_DOUBLE
349 | return ceil(cgfloat);
350 | #else
351 | return ceilf(cgfloat);
352 | #endif
353 | }
354 |
355 | static inline CGFLOAT_TYPE CGFloat_floor(CGFLOAT_TYPE cgfloat) {
356 | #if CGFLOAT_IS_DOUBLE
357 | return floor(cgfloat);
358 | #else
359 | return floorf(cgfloat);
360 | #endif
361 | }
362 |
363 | static inline CGFloat CJFlushFactorForTextAlignment(NSTextAlignment textAlignment) {
364 | switch (textAlignment) {
365 | case NSTextAlignmentCenter:
366 | return 0.5f;
367 | case NSTextAlignmentRight:
368 | return 1.0f;
369 | case NSTextAlignmentLeft:
370 | default:
371 | return 0.0f;
372 | }
373 | }
374 |
375 | static inline CGColorRef CGColorRefFromColor(id color) {
376 | return [color isKindOfClass:[UIColor class]] ? [color CGColor] : (__bridge CGColorRef)color;
377 | }
378 |
379 | static inline NSAttributedString * NSAttributedStringByScalingFontSize(NSAttributedString *attributedString, CGFloat scale) {
380 | NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy];
381 | [mutableAttributedString enumerateAttribute:(NSString *)kCTFontAttributeName inRange:NSMakeRange(0, [mutableAttributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL * __unused stop) {
382 | UIFont *font = (UIFont *)value;
383 | if (font) {
384 | NSString *fontName;
385 | CGFloat pointSize;
386 |
387 | if ([font isKindOfClass:[UIFont class]]) {
388 | fontName = font.fontName;
389 | pointSize = font.pointSize;
390 | } else {
391 | fontName = (NSString *)CFBridgingRelease(CTFontCopyName((__bridge CTFontRef)font, kCTFontPostScriptNameKey));
392 | pointSize = CTFontGetSize((__bridge CTFontRef)font);
393 | }
394 |
395 | [mutableAttributedString removeAttribute:(NSString *)kCTFontAttributeName range:range];
396 | CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)fontName, CGFloat_floor(pointSize * scale), NULL);
397 | [mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)fontRef range:range];
398 | CFRelease(fontRef);
399 | }
400 | }];
401 |
402 | return mutableAttributedString;
403 | }
404 |
405 | static inline CGSize CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(CTFramesetterRef framesetter, NSAttributedString *attributedString, CGSize size, NSUInteger numberOfLines) {
406 | CFRange rangeToSize = CFRangeMake(0, (CFIndex)[attributedString length]);
407 | CGSize constraints = CGSizeMake(size.width, CJFLOAT_MAX);
408 |
409 | if (numberOfLines == 1) {
410 | constraints = CGSizeMake(CJFLOAT_MAX, CJFLOAT_MAX);
411 | } else if (numberOfLines > 0) {
412 | CGMutablePathRef path = CGPathCreateMutable();
413 | CGPathAddRect(path, NULL, CGRectMake(0.0f, 0.0f, constraints.width, CJFLOAT_MAX));
414 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
415 | CFArrayRef lines = CTFrameGetLines(frame);
416 |
417 | if (CFArrayGetCount(lines) > 0) {
418 | NSInteger lastVisibleLineIndex = MIN((CFIndex)numberOfLines, CFArrayGetCount(lines)) - 1;
419 | CTLineRef lastVisibleLine = CFArrayGetValueAtIndex(lines, lastVisibleLineIndex);
420 |
421 | CFRange rangeToLayout = CTLineGetStringRange(lastVisibleLine);
422 | rangeToSize = CFRangeMake(0, rangeToLayout.location + rangeToLayout.length);
423 | }
424 |
425 | CFRelease(frame);
426 | CGPathRelease(path);
427 | }
428 |
429 | CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, rangeToSize, NULL, constraints, NULL);
430 |
431 | return CGSizeMake(CGFloat_ceil(suggestedSize.width), CGFloat_ceil(suggestedSize.height));
432 | };
433 |
434 | static inline CGFloat compareMaxNum(CGFloat firstNum, CGFloat secondNum, BOOL max){
435 | CGFloat result = firstNum;
436 | if (max) {
437 | result = (firstNum >= secondNum)?firstNum:secondNum;
438 | }else{
439 | result = (firstNum <= secondNum)?firstNum:secondNum;
440 | }
441 | return result;
442 | }
443 |
444 | static inline UIColor * colorWithAttributeName(NSDictionary *dic, NSString *key){
445 | UIColor *color = nil;
446 | if (dic[key] && nil != dic[key]) {
447 | color = dic[key];
448 | }
449 | return color;
450 | }
451 |
452 | static inline BOOL isNotClearColor(UIColor *color){
453 | if (CJLabelIsNull(color)) {
454 | return NO;
455 | }
456 | BOOL notClearColor = YES;
457 | if (CGColorEqualToColor(color.CGColor, [UIColor clearColor].CGColor)) {
458 | notClearColor = NO;
459 | }
460 | return notClearColor;
461 | }
462 |
463 | static inline BOOL isSameColor(UIColor *color1, UIColor *color2){
464 | BOOL same = YES;
465 | if (!CGColorEqualToColor(color1.CGColor, color2.CGColor)) {
466 | same = NO;
467 | }
468 | return same;
469 | }
470 |
471 | static inline UIWindow * CJkeyWindow() {
472 | UIApplication *app = [UIApplication sharedApplication];
473 | if ([app.delegate respondsToSelector:@selector(window)]) {
474 | return [app.delegate window];
475 | } else {
476 | return [app keyWindow];
477 | }
478 | }
479 |
480 |
--------------------------------------------------------------------------------
/Example/CJLabelTest.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4360A64323E6C30D003920A1 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4360A64223E6C30D003920A1 /* HealthKit.framework */; };
11 | 438257861C1A6F5F00A3DA24 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 438257851C1A6F5F00A3DA24 /* main.m */; };
12 | 438257891C1A6F5F00A3DA24 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 438257881C1A6F5F00A3DA24 /* AppDelegate.m */; };
13 | 4382578C1C1A6F5F00A3DA24 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4382578B1C1A6F5F00A3DA24 /* ViewController.m */; };
14 | 4382578F1C1A6F5F00A3DA24 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4382578D1C1A6F5F00A3DA24 /* Main.storyboard */; };
15 | 438257911C1A6F5F00A3DA24 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 438257901C1A6F5F00A3DA24 /* Assets.xcassets */; };
16 | 438257941C1A6F5F00A3DA24 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 438257921C1A6F5F00A3DA24 /* LaunchScreen.storyboard */; };
17 | 4382579F1C1A6F5F00A3DA24 /* CJLabelTestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4382579E1C1A6F5F00A3DA24 /* CJLabelTestTests.m */; };
18 | 438257AA1C1A6F5F00A3DA24 /* CJLabelTestUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 438257A91C1A6F5F00A3DA24 /* CJLabelTestUITests.m */; };
19 | 43C1652E25C92B1B00EAA38B /* CJLabel+UIMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 43C1652D25C92B1B00EAA38B /* CJLabel+UIMenu.m */; };
20 | B40ACCA71EB30CF3006DB787 /* AttributedTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B40ACCA41EB30CF3006DB787 /* AttributedTableViewCell.m */; };
21 | B40ACCA81EB30CF3006DB787 /* AttributedTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B40ACCA51EB30CF3006DB787 /* AttributedTableViewCell.xib */; };
22 | B431D5321D0545E700228988 /* CJLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = B431D52E1D0545E700228988 /* CJLabel.m */; };
23 | B45AC7ED1F8A7181006B5336 /* CJLabelConfigure.m in Sources */ = {isa = PBXBuildFile; fileRef = B45AC7EC1F8A7181006B5336 /* CJLabelConfigure.m */; };
24 | B483EC891FBAC7AD000FCE06 /* FirstDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B483EC861FBAC7AD000FCE06 /* FirstDetailViewController.m */; };
25 | B483EC8A1FBAC7AD000FCE06 /* FirstDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B483EC881FBAC7AD000FCE06 /* FirstDetailViewController.xib */; };
26 | B4928B9F1FB4003D0017A25C /* Example.json in Resources */ = {isa = PBXBuildFile; fileRef = B4928B9E1FB4003D0017A25C /* Example.json */; };
27 | B4C8931B1EB881EA00AC9E42 /* SecondDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B4C893191EB881EA00AC9E42 /* SecondDetailViewController.m */; };
28 | B4C8931C1EB881EA00AC9E42 /* SecondDetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B4C8931A1EB881EA00AC9E42 /* SecondDetailViewController.xib */; };
29 | /* End PBXBuildFile section */
30 |
31 | /* Begin PBXContainerItemProxy section */
32 | 4382579B1C1A6F5F00A3DA24 /* PBXContainerItemProxy */ = {
33 | isa = PBXContainerItemProxy;
34 | containerPortal = 438257791C1A6F5F00A3DA24 /* Project object */;
35 | proxyType = 1;
36 | remoteGlobalIDString = 438257801C1A6F5F00A3DA24;
37 | remoteInfo = CJLabelTest;
38 | };
39 | 438257A61C1A6F5F00A3DA24 /* PBXContainerItemProxy */ = {
40 | isa = PBXContainerItemProxy;
41 | containerPortal = 438257791C1A6F5F00A3DA24 /* Project object */;
42 | proxyType = 1;
43 | remoteGlobalIDString = 438257801C1A6F5F00A3DA24;
44 | remoteInfo = CJLabelTest;
45 | };
46 | /* End PBXContainerItemProxy section */
47 |
48 | /* Begin PBXCopyFilesBuildPhase section */
49 | E7D4B2B723B2248E00F2E528 /* Embed Frameworks */ = {
50 | isa = PBXCopyFilesBuildPhase;
51 | buildActionMask = 2147483647;
52 | dstPath = "";
53 | dstSubfolderSpec = 10;
54 | files = (
55 | );
56 | name = "Embed Frameworks";
57 | runOnlyForDeploymentPostprocessing = 0;
58 | };
59 | /* End PBXCopyFilesBuildPhase section */
60 |
61 | /* Begin PBXFileReference section */
62 | 4360A64223E6C30D003920A1 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
63 | 438257811C1A6F5F00A3DA24 /* CJLabelTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CJLabelTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
64 | 438257851C1A6F5F00A3DA24 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
65 | 438257871C1A6F5F00A3DA24 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
66 | 438257881C1A6F5F00A3DA24 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
67 | 4382578A1C1A6F5F00A3DA24 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ViewController.h; path = ../Example/CJLabelTest/ViewController.h; sourceTree = ""; };
68 | 4382578B1C1A6F5F00A3DA24 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ViewController.m; path = ../Example/CJLabelTest/ViewController.m; sourceTree = ""; };
69 | 4382578E1C1A6F5F00A3DA24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
70 | 438257901C1A6F5F00A3DA24 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
71 | 438257931C1A6F5F00A3DA24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
72 | 438257951C1A6F5F00A3DA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
73 | 4382579A1C1A6F5F00A3DA24 /* CJLabelTestTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CJLabelTestTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
74 | 4382579E1C1A6F5F00A3DA24 /* CJLabelTestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CJLabelTestTests.m; sourceTree = ""; };
75 | 438257A01C1A6F5F00A3DA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
76 | 438257A51C1A6F5F00A3DA24 /* CJLabelTestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CJLabelTestUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
77 | 438257A91C1A6F5F00A3DA24 /* CJLabelTestUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CJLabelTestUITests.m; sourceTree = ""; };
78 | 438257AB1C1A6F5F00A3DA24 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
79 | 4396AC9E23E6C3B800836CE2 /* CJLabelTest.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; name = CJLabelTest.entitlements; path = ../../../../../../.Trash/CJLabelTest.entitlements; sourceTree = ""; };
80 | 43C1652C25C92B1B00EAA38B /* CJLabel+UIMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CJLabel+UIMenu.h"; sourceTree = ""; };
81 | 43C1652D25C92B1B00EAA38B /* CJLabel+UIMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CJLabel+UIMenu.m"; sourceTree = ""; };
82 | B40ACCA31EB30CF3006DB787 /* AttributedTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AttributedTableViewCell.h; sourceTree = ""; };
83 | B40ACCA41EB30CF3006DB787 /* AttributedTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AttributedTableViewCell.m; sourceTree = ""; };
84 | B40ACCA51EB30CF3006DB787 /* AttributedTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AttributedTableViewCell.xib; sourceTree = ""; };
85 | B431D52D1D0545E700228988 /* CJLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CJLabel.h; sourceTree = ""; };
86 | B431D52E1D0545E700228988 /* CJLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CJLabel.m; sourceTree = ""; };
87 | B45AC7EB1F8A7181006B5336 /* CJLabelConfigure.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CJLabelConfigure.h; sourceTree = ""; };
88 | B45AC7EC1F8A7181006B5336 /* CJLabelConfigure.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CJLabelConfigure.m; sourceTree = ""; };
89 | B483EC861FBAC7AD000FCE06 /* FirstDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FirstDetailViewController.m; path = CJLabelTest/FirstDetailViewController.m; sourceTree = SOURCE_ROOT; };
90 | B483EC871FBAC7AD000FCE06 /* FirstDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FirstDetailViewController.h; path = CJLabelTest/FirstDetailViewController.h; sourceTree = SOURCE_ROOT; };
91 | B483EC881FBAC7AD000FCE06 /* FirstDetailViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FirstDetailViewController.xib; path = CJLabelTest/FirstDetailViewController.xib; sourceTree = SOURCE_ROOT; };
92 | B4928B9E1FB4003D0017A25C /* Example.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Example.json; sourceTree = ""; };
93 | B4928BA51FB447920017A25C /* Common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = ""; };
94 | B4C893181EB881EA00AC9E42 /* SecondDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecondDetailViewController.h; path = CJLabelTest/SecondDetailViewController.h; sourceTree = SOURCE_ROOT; };
95 | B4C893191EB881EA00AC9E42 /* SecondDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SecondDetailViewController.m; path = CJLabelTest/SecondDetailViewController.m; sourceTree = SOURCE_ROOT; };
96 | B4C8931A1EB881EA00AC9E42 /* SecondDetailViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SecondDetailViewController.xib; path = CJLabelTest/SecondDetailViewController.xib; sourceTree = SOURCE_ROOT; };
97 | /* End PBXFileReference section */
98 |
99 | /* Begin PBXFrameworksBuildPhase section */
100 | 4382577E1C1A6F5F00A3DA24 /* Frameworks */ = {
101 | isa = PBXFrameworksBuildPhase;
102 | buildActionMask = 2147483647;
103 | files = (
104 | 4360A64323E6C30D003920A1 /* HealthKit.framework in Frameworks */,
105 | );
106 | runOnlyForDeploymentPostprocessing = 0;
107 | };
108 | 438257971C1A6F5F00A3DA24 /* Frameworks */ = {
109 | isa = PBXFrameworksBuildPhase;
110 | buildActionMask = 2147483647;
111 | files = (
112 | );
113 | runOnlyForDeploymentPostprocessing = 0;
114 | };
115 | 438257A21C1A6F5F00A3DA24 /* Frameworks */ = {
116 | isa = PBXFrameworksBuildPhase;
117 | buildActionMask = 2147483647;
118 | files = (
119 | );
120 | runOnlyForDeploymentPostprocessing = 0;
121 | };
122 | /* End PBXFrameworksBuildPhase section */
123 |
124 | /* Begin PBXGroup section */
125 | 4360A64123E6C30D003920A1 /* Frameworks */ = {
126 | isa = PBXGroup;
127 | children = (
128 | 4360A64223E6C30D003920A1 /* HealthKit.framework */,
129 | );
130 | name = Frameworks;
131 | sourceTree = "";
132 | };
133 | 438257781C1A6F5F00A3DA24 = {
134 | isa = PBXGroup;
135 | children = (
136 | 438257831C1A6F5F00A3DA24 /* CJLabelTest */,
137 | 4382579D1C1A6F5F00A3DA24 /* CJLabelTestTests */,
138 | 438257A81C1A6F5F00A3DA24 /* CJLabelTestUITests */,
139 | 438257821C1A6F5F00A3DA24 /* Products */,
140 | 4360A64123E6C30D003920A1 /* Frameworks */,
141 | );
142 | sourceTree = "";
143 | };
144 | 438257821C1A6F5F00A3DA24 /* Products */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 438257811C1A6F5F00A3DA24 /* CJLabelTest.app */,
148 | 4382579A1C1A6F5F00A3DA24 /* CJLabelTestTests.xctest */,
149 | 438257A51C1A6F5F00A3DA24 /* CJLabelTestUITests.xctest */,
150 | );
151 | name = Products;
152 | sourceTree = "";
153 | };
154 | 438257831C1A6F5F00A3DA24 /* CJLabelTest */ = {
155 | isa = PBXGroup;
156 | children = (
157 | 4396AC9E23E6C3B800836CE2 /* CJLabelTest.entitlements */,
158 | B431D52C1D0545E700228988 /* CJLabel */,
159 | 438257871C1A6F5F00A3DA24 /* AppDelegate.h */,
160 | 438257881C1A6F5F00A3DA24 /* AppDelegate.m */,
161 | B40ACCAB1EB30D8F006DB787 /* Controllers */,
162 | B40ACCAA1EB30D6A006DB787 /* Views */,
163 | 438257901C1A6F5F00A3DA24 /* Assets.xcassets */,
164 | 438257841C1A6F5F00A3DA24 /* Supporting Files */,
165 | );
166 | path = CJLabelTest;
167 | sourceTree = "";
168 | };
169 | 438257841C1A6F5F00A3DA24 /* Supporting Files */ = {
170 | isa = PBXGroup;
171 | children = (
172 | B4928B9E1FB4003D0017A25C /* Example.json */,
173 | B4928BA51FB447920017A25C /* Common.h */,
174 | 4382578D1C1A6F5F00A3DA24 /* Main.storyboard */,
175 | 438257921C1A6F5F00A3DA24 /* LaunchScreen.storyboard */,
176 | 438257951C1A6F5F00A3DA24 /* Info.plist */,
177 | 438257851C1A6F5F00A3DA24 /* main.m */,
178 | );
179 | name = "Supporting Files";
180 | sourceTree = "";
181 | };
182 | 4382579D1C1A6F5F00A3DA24 /* CJLabelTestTests */ = {
183 | isa = PBXGroup;
184 | children = (
185 | 4382579E1C1A6F5F00A3DA24 /* CJLabelTestTests.m */,
186 | 438257A01C1A6F5F00A3DA24 /* Info.plist */,
187 | );
188 | path = CJLabelTestTests;
189 | sourceTree = "";
190 | };
191 | 438257A81C1A6F5F00A3DA24 /* CJLabelTestUITests */ = {
192 | isa = PBXGroup;
193 | children = (
194 | 438257A91C1A6F5F00A3DA24 /* CJLabelTestUITests.m */,
195 | 438257AB1C1A6F5F00A3DA24 /* Info.plist */,
196 | );
197 | path = CJLabelTestUITests;
198 | sourceTree = "";
199 | };
200 | B40ACCAA1EB30D6A006DB787 /* Views */ = {
201 | isa = PBXGroup;
202 | children = (
203 | B40ACCA31EB30CF3006DB787 /* AttributedTableViewCell.h */,
204 | B40ACCA41EB30CF3006DB787 /* AttributedTableViewCell.m */,
205 | B40ACCA51EB30CF3006DB787 /* AttributedTableViewCell.xib */,
206 | );
207 | name = Views;
208 | sourceTree = "";
209 | };
210 | B40ACCAB1EB30D8F006DB787 /* Controllers */ = {
211 | isa = PBXGroup;
212 | children = (
213 | B483EC871FBAC7AD000FCE06 /* FirstDetailViewController.h */,
214 | B483EC861FBAC7AD000FCE06 /* FirstDetailViewController.m */,
215 | B483EC881FBAC7AD000FCE06 /* FirstDetailViewController.xib */,
216 | 4382578A1C1A6F5F00A3DA24 /* ViewController.h */,
217 | 4382578B1C1A6F5F00A3DA24 /* ViewController.m */,
218 | B4C893181EB881EA00AC9E42 /* SecondDetailViewController.h */,
219 | B4C893191EB881EA00AC9E42 /* SecondDetailViewController.m */,
220 | B4C8931A1EB881EA00AC9E42 /* SecondDetailViewController.xib */,
221 | );
222 | name = Controllers;
223 | path = ../../CJLabel;
224 | sourceTree = "";
225 | };
226 | B431D52C1D0545E700228988 /* CJLabel */ = {
227 | isa = PBXGroup;
228 | children = (
229 | B431D52D1D0545E700228988 /* CJLabel.h */,
230 | B431D52E1D0545E700228988 /* CJLabel.m */,
231 | 43C1652C25C92B1B00EAA38B /* CJLabel+UIMenu.h */,
232 | 43C1652D25C92B1B00EAA38B /* CJLabel+UIMenu.m */,
233 | B45AC7EB1F8A7181006B5336 /* CJLabelConfigure.h */,
234 | B45AC7EC1F8A7181006B5336 /* CJLabelConfigure.m */,
235 | );
236 | name = CJLabel;
237 | path = ../../CJLabel;
238 | sourceTree = "";
239 | };
240 | /* End PBXGroup section */
241 |
242 | /* Begin PBXNativeTarget section */
243 | 438257801C1A6F5F00A3DA24 /* CJLabelTest */ = {
244 | isa = PBXNativeTarget;
245 | buildConfigurationList = 438257AE1C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTest" */;
246 | buildPhases = (
247 | 4382577D1C1A6F5F00A3DA24 /* Sources */,
248 | 4382577E1C1A6F5F00A3DA24 /* Frameworks */,
249 | 4382577F1C1A6F5F00A3DA24 /* Resources */,
250 | E7D4B2B723B2248E00F2E528 /* Embed Frameworks */,
251 | );
252 | buildRules = (
253 | );
254 | dependencies = (
255 | );
256 | name = CJLabelTest;
257 | productName = CJLabelTest;
258 | productReference = 438257811C1A6F5F00A3DA24 /* CJLabelTest.app */;
259 | productType = "com.apple.product-type.application";
260 | };
261 | 438257991C1A6F5F00A3DA24 /* CJLabelTestTests */ = {
262 | isa = PBXNativeTarget;
263 | buildConfigurationList = 438257B11C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTestTests" */;
264 | buildPhases = (
265 | 438257961C1A6F5F00A3DA24 /* Sources */,
266 | 438257971C1A6F5F00A3DA24 /* Frameworks */,
267 | 438257981C1A6F5F00A3DA24 /* Resources */,
268 | );
269 | buildRules = (
270 | );
271 | dependencies = (
272 | 4382579C1C1A6F5F00A3DA24 /* PBXTargetDependency */,
273 | );
274 | name = CJLabelTestTests;
275 | productName = CJLabelTestTests;
276 | productReference = 4382579A1C1A6F5F00A3DA24 /* CJLabelTestTests.xctest */;
277 | productType = "com.apple.product-type.bundle.unit-test";
278 | };
279 | 438257A41C1A6F5F00A3DA24 /* CJLabelTestUITests */ = {
280 | isa = PBXNativeTarget;
281 | buildConfigurationList = 438257B41C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTestUITests" */;
282 | buildPhases = (
283 | 438257A11C1A6F5F00A3DA24 /* Sources */,
284 | 438257A21C1A6F5F00A3DA24 /* Frameworks */,
285 | 438257A31C1A6F5F00A3DA24 /* Resources */,
286 | );
287 | buildRules = (
288 | );
289 | dependencies = (
290 | 438257A71C1A6F5F00A3DA24 /* PBXTargetDependency */,
291 | );
292 | name = CJLabelTestUITests;
293 | productName = CJLabelTestUITests;
294 | productReference = 438257A51C1A6F5F00A3DA24 /* CJLabelTestUITests.xctest */;
295 | productType = "com.apple.product-type.bundle.ui-testing";
296 | };
297 | /* End PBXNativeTarget section */
298 |
299 | /* Begin PBXProject section */
300 | 438257791C1A6F5F00A3DA24 /* Project object */ = {
301 | isa = PBXProject;
302 | attributes = {
303 | LastUpgradeCheck = 0900;
304 | ORGANIZATIONNAME = C.K.Lian;
305 | TargetAttributes = {
306 | 438257801C1A6F5F00A3DA24 = {
307 | CreatedOnToolsVersion = 7.0;
308 | DevelopmentTeam = 2G9KQAD4K5;
309 | ProvisioningStyle = Automatic;
310 | };
311 | 438257991C1A6F5F00A3DA24 = {
312 | CreatedOnToolsVersion = 7.0;
313 | TestTargetID = 438257801C1A6F5F00A3DA24;
314 | };
315 | 438257A41C1A6F5F00A3DA24 = {
316 | CreatedOnToolsVersion = 7.0;
317 | TestTargetID = 438257801C1A6F5F00A3DA24;
318 | };
319 | };
320 | };
321 | buildConfigurationList = 4382577C1C1A6F5F00A3DA24 /* Build configuration list for PBXProject "CJLabelTest" */;
322 | compatibilityVersion = "Xcode 3.2";
323 | developmentRegion = English;
324 | hasScannedForEncodings = 0;
325 | knownRegions = (
326 | English,
327 | en,
328 | Base,
329 | );
330 | mainGroup = 438257781C1A6F5F00A3DA24;
331 | productRefGroup = 438257821C1A6F5F00A3DA24 /* Products */;
332 | projectDirPath = "";
333 | projectRoot = "";
334 | targets = (
335 | 438257801C1A6F5F00A3DA24 /* CJLabelTest */,
336 | 438257991C1A6F5F00A3DA24 /* CJLabelTestTests */,
337 | 438257A41C1A6F5F00A3DA24 /* CJLabelTestUITests */,
338 | );
339 | };
340 | /* End PBXProject section */
341 |
342 | /* Begin PBXResourcesBuildPhase section */
343 | 4382577F1C1A6F5F00A3DA24 /* Resources */ = {
344 | isa = PBXResourcesBuildPhase;
345 | buildActionMask = 2147483647;
346 | files = (
347 | B483EC8A1FBAC7AD000FCE06 /* FirstDetailViewController.xib in Resources */,
348 | 438257941C1A6F5F00A3DA24 /* LaunchScreen.storyboard in Resources */,
349 | B4C8931C1EB881EA00AC9E42 /* SecondDetailViewController.xib in Resources */,
350 | 438257911C1A6F5F00A3DA24 /* Assets.xcassets in Resources */,
351 | B4928B9F1FB4003D0017A25C /* Example.json in Resources */,
352 | 4382578F1C1A6F5F00A3DA24 /* Main.storyboard in Resources */,
353 | B40ACCA81EB30CF3006DB787 /* AttributedTableViewCell.xib in Resources */,
354 | );
355 | runOnlyForDeploymentPostprocessing = 0;
356 | };
357 | 438257981C1A6F5F00A3DA24 /* Resources */ = {
358 | isa = PBXResourcesBuildPhase;
359 | buildActionMask = 2147483647;
360 | files = (
361 | );
362 | runOnlyForDeploymentPostprocessing = 0;
363 | };
364 | 438257A31C1A6F5F00A3DA24 /* Resources */ = {
365 | isa = PBXResourcesBuildPhase;
366 | buildActionMask = 2147483647;
367 | files = (
368 | );
369 | runOnlyForDeploymentPostprocessing = 0;
370 | };
371 | /* End PBXResourcesBuildPhase section */
372 |
373 | /* Begin PBXSourcesBuildPhase section */
374 | 4382577D1C1A6F5F00A3DA24 /* Sources */ = {
375 | isa = PBXSourcesBuildPhase;
376 | buildActionMask = 2147483647;
377 | files = (
378 | 43C1652E25C92B1B00EAA38B /* CJLabel+UIMenu.m in Sources */,
379 | B483EC891FBAC7AD000FCE06 /* FirstDetailViewController.m in Sources */,
380 | 4382578C1C1A6F5F00A3DA24 /* ViewController.m in Sources */,
381 | B40ACCA71EB30CF3006DB787 /* AttributedTableViewCell.m in Sources */,
382 | B431D5321D0545E700228988 /* CJLabel.m in Sources */,
383 | B4C8931B1EB881EA00AC9E42 /* SecondDetailViewController.m in Sources */,
384 | B45AC7ED1F8A7181006B5336 /* CJLabelConfigure.m in Sources */,
385 | 438257891C1A6F5F00A3DA24 /* AppDelegate.m in Sources */,
386 | 438257861C1A6F5F00A3DA24 /* main.m in Sources */,
387 | );
388 | runOnlyForDeploymentPostprocessing = 0;
389 | };
390 | 438257961C1A6F5F00A3DA24 /* Sources */ = {
391 | isa = PBXSourcesBuildPhase;
392 | buildActionMask = 2147483647;
393 | files = (
394 | 4382579F1C1A6F5F00A3DA24 /* CJLabelTestTests.m in Sources */,
395 | );
396 | runOnlyForDeploymentPostprocessing = 0;
397 | };
398 | 438257A11C1A6F5F00A3DA24 /* Sources */ = {
399 | isa = PBXSourcesBuildPhase;
400 | buildActionMask = 2147483647;
401 | files = (
402 | 438257AA1C1A6F5F00A3DA24 /* CJLabelTestUITests.m in Sources */,
403 | );
404 | runOnlyForDeploymentPostprocessing = 0;
405 | };
406 | /* End PBXSourcesBuildPhase section */
407 |
408 | /* Begin PBXTargetDependency section */
409 | 4382579C1C1A6F5F00A3DA24 /* PBXTargetDependency */ = {
410 | isa = PBXTargetDependency;
411 | target = 438257801C1A6F5F00A3DA24 /* CJLabelTest */;
412 | targetProxy = 4382579B1C1A6F5F00A3DA24 /* PBXContainerItemProxy */;
413 | };
414 | 438257A71C1A6F5F00A3DA24 /* PBXTargetDependency */ = {
415 | isa = PBXTargetDependency;
416 | target = 438257801C1A6F5F00A3DA24 /* CJLabelTest */;
417 | targetProxy = 438257A61C1A6F5F00A3DA24 /* PBXContainerItemProxy */;
418 | };
419 | /* End PBXTargetDependency section */
420 |
421 | /* Begin PBXVariantGroup section */
422 | 4382578D1C1A6F5F00A3DA24 /* Main.storyboard */ = {
423 | isa = PBXVariantGroup;
424 | children = (
425 | 4382578E1C1A6F5F00A3DA24 /* Base */,
426 | );
427 | name = Main.storyboard;
428 | sourceTree = "";
429 | };
430 | 438257921C1A6F5F00A3DA24 /* LaunchScreen.storyboard */ = {
431 | isa = PBXVariantGroup;
432 | children = (
433 | 438257931C1A6F5F00A3DA24 /* Base */,
434 | );
435 | name = LaunchScreen.storyboard;
436 | sourceTree = "";
437 | };
438 | /* End PBXVariantGroup section */
439 |
440 | /* Begin XCBuildConfiguration section */
441 | 438257AC1C1A6F5F00A3DA24 /* Debug */ = {
442 | isa = XCBuildConfiguration;
443 | buildSettings = {
444 | ALWAYS_SEARCH_USER_PATHS = NO;
445 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
446 | CLANG_CXX_LIBRARY = "libc++";
447 | CLANG_ENABLE_MODULES = YES;
448 | CLANG_ENABLE_OBJC_ARC = YES;
449 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
450 | CLANG_WARN_BOOL_CONVERSION = YES;
451 | CLANG_WARN_COMMA = YES;
452 | CLANG_WARN_CONSTANT_CONVERSION = YES;
453 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
454 | CLANG_WARN_EMPTY_BODY = YES;
455 | CLANG_WARN_ENUM_CONVERSION = YES;
456 | CLANG_WARN_INFINITE_RECURSION = YES;
457 | CLANG_WARN_INT_CONVERSION = YES;
458 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
459 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
461 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
462 | CLANG_WARN_STRICT_PROTOTYPES = YES;
463 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
464 | CLANG_WARN_UNREACHABLE_CODE = YES;
465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
466 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
467 | COPY_PHASE_STRIP = NO;
468 | DEBUG_INFORMATION_FORMAT = dwarf;
469 | ENABLE_STRICT_OBJC_MSGSEND = YES;
470 | ENABLE_TESTABILITY = YES;
471 | GCC_C_LANGUAGE_STANDARD = gnu99;
472 | GCC_DYNAMIC_NO_PIC = NO;
473 | GCC_NO_COMMON_BLOCKS = YES;
474 | GCC_OPTIMIZATION_LEVEL = 0;
475 | GCC_PREPROCESSOR_DEFINITIONS = (
476 | "DEBUG=1",
477 | "$(inherited)",
478 | );
479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
481 | GCC_WARN_UNDECLARED_SELECTOR = YES;
482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
483 | GCC_WARN_UNUSED_FUNCTION = YES;
484 | GCC_WARN_UNUSED_VARIABLE = YES;
485 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
486 | MTL_ENABLE_DEBUG_INFO = YES;
487 | ONLY_ACTIVE_ARCH = YES;
488 | SDKROOT = iphoneos;
489 | TARGETED_DEVICE_FAMILY = "1,2";
490 | };
491 | name = Debug;
492 | };
493 | 438257AD1C1A6F5F00A3DA24 /* Release */ = {
494 | isa = XCBuildConfiguration;
495 | buildSettings = {
496 | ALWAYS_SEARCH_USER_PATHS = NO;
497 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
498 | CLANG_CXX_LIBRARY = "libc++";
499 | CLANG_ENABLE_MODULES = YES;
500 | CLANG_ENABLE_OBJC_ARC = YES;
501 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
502 | CLANG_WARN_BOOL_CONVERSION = YES;
503 | CLANG_WARN_COMMA = YES;
504 | CLANG_WARN_CONSTANT_CONVERSION = YES;
505 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
506 | CLANG_WARN_EMPTY_BODY = YES;
507 | CLANG_WARN_ENUM_CONVERSION = YES;
508 | CLANG_WARN_INFINITE_RECURSION = YES;
509 | CLANG_WARN_INT_CONVERSION = YES;
510 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
511 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
512 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
513 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
514 | CLANG_WARN_STRICT_PROTOTYPES = YES;
515 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
516 | CLANG_WARN_UNREACHABLE_CODE = YES;
517 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
518 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
519 | COPY_PHASE_STRIP = NO;
520 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
521 | ENABLE_NS_ASSERTIONS = NO;
522 | ENABLE_STRICT_OBJC_MSGSEND = YES;
523 | GCC_C_LANGUAGE_STANDARD = gnu99;
524 | GCC_NO_COMMON_BLOCKS = YES;
525 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
526 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
527 | GCC_WARN_UNDECLARED_SELECTOR = YES;
528 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
529 | GCC_WARN_UNUSED_FUNCTION = YES;
530 | GCC_WARN_UNUSED_VARIABLE = YES;
531 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
532 | MTL_ENABLE_DEBUG_INFO = NO;
533 | SDKROOT = iphoneos;
534 | TARGETED_DEVICE_FAMILY = "1,2";
535 | VALIDATE_PRODUCT = YES;
536 | };
537 | name = Release;
538 | };
539 | 438257AF1C1A6F5F00A3DA24 /* Debug */ = {
540 | isa = XCBuildConfiguration;
541 | buildSettings = {
542 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
543 | CODE_SIGN_ENTITLEMENTS = CJLabelTest/CJLabelTest.entitlements;
544 | CODE_SIGN_IDENTITY = "Apple Development";
545 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
546 | CODE_SIGN_STYLE = Automatic;
547 | DEVELOPMENT_TEAM = 2G9KQAD4K5;
548 | FRAMEWORK_SEARCH_PATHS = (
549 | "$(inherited)",
550 | "$(PROJECT_DIR)",
551 | "$(PROJECT_DIR)/CJLabelTest",
552 | );
553 | INFOPLIST_FILE = CJLabelTest/Info.plist;
554 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
555 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
556 | OTHER_LDFLAGS = "";
557 | PRODUCT_BUNDLE_IDENTIFIER = Jin.Lian.Test;
558 | PRODUCT_NAME = "$(TARGET_NAME)";
559 | PROVISIONING_PROFILE_SPECIFIER = "";
560 | };
561 | name = Debug;
562 | };
563 | 438257B01C1A6F5F00A3DA24 /* Release */ = {
564 | isa = XCBuildConfiguration;
565 | buildSettings = {
566 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
567 | CODE_SIGN_ENTITLEMENTS = CJLabelTest/CJLabelTest.entitlements;
568 | CODE_SIGN_IDENTITY = "Apple Development";
569 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
570 | CODE_SIGN_STYLE = Automatic;
571 | DEVELOPMENT_TEAM = 2G9KQAD4K5;
572 | FRAMEWORK_SEARCH_PATHS = (
573 | "$(inherited)",
574 | "$(PROJECT_DIR)",
575 | "$(PROJECT_DIR)/CJLabelTest",
576 | );
577 | INFOPLIST_FILE = CJLabelTest/Info.plist;
578 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
579 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
580 | OTHER_LDFLAGS = "";
581 | PRODUCT_BUNDLE_IDENTIFIER = Jin.Lian.Test;
582 | PRODUCT_NAME = "$(TARGET_NAME)";
583 | PROVISIONING_PROFILE_SPECIFIER = "";
584 | };
585 | name = Release;
586 | };
587 | 438257B21C1A6F5F00A3DA24 /* Debug */ = {
588 | isa = XCBuildConfiguration;
589 | buildSettings = {
590 | BUNDLE_LOADER = "$(TEST_HOST)";
591 | INFOPLIST_FILE = CJLabelTestTests/Info.plist;
592 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
593 | PRODUCT_BUNDLE_IDENTIFIER = cj.CJLabelTestTests;
594 | PRODUCT_NAME = "$(TARGET_NAME)";
595 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CJLabelTest.app/CJLabelTest";
596 | };
597 | name = Debug;
598 | };
599 | 438257B31C1A6F5F00A3DA24 /* Release */ = {
600 | isa = XCBuildConfiguration;
601 | buildSettings = {
602 | BUNDLE_LOADER = "$(TEST_HOST)";
603 | INFOPLIST_FILE = CJLabelTestTests/Info.plist;
604 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
605 | PRODUCT_BUNDLE_IDENTIFIER = cj.CJLabelTestTests;
606 | PRODUCT_NAME = "$(TARGET_NAME)";
607 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CJLabelTest.app/CJLabelTest";
608 | };
609 | name = Release;
610 | };
611 | 438257B51C1A6F5F00A3DA24 /* Debug */ = {
612 | isa = XCBuildConfiguration;
613 | buildSettings = {
614 | INFOPLIST_FILE = CJLabelTestUITests/Info.plist;
615 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
616 | PRODUCT_BUNDLE_IDENTIFIER = cj.CJLabelTestUITests;
617 | PRODUCT_NAME = "$(TARGET_NAME)";
618 | TEST_TARGET_NAME = CJLabelTest;
619 | USES_XCTRUNNER = YES;
620 | };
621 | name = Debug;
622 | };
623 | 438257B61C1A6F5F00A3DA24 /* Release */ = {
624 | isa = XCBuildConfiguration;
625 | buildSettings = {
626 | INFOPLIST_FILE = CJLabelTestUITests/Info.plist;
627 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
628 | PRODUCT_BUNDLE_IDENTIFIER = cj.CJLabelTestUITests;
629 | PRODUCT_NAME = "$(TARGET_NAME)";
630 | TEST_TARGET_NAME = CJLabelTest;
631 | USES_XCTRUNNER = YES;
632 | };
633 | name = Release;
634 | };
635 | /* End XCBuildConfiguration section */
636 |
637 | /* Begin XCConfigurationList section */
638 | 4382577C1C1A6F5F00A3DA24 /* Build configuration list for PBXProject "CJLabelTest" */ = {
639 | isa = XCConfigurationList;
640 | buildConfigurations = (
641 | 438257AC1C1A6F5F00A3DA24 /* Debug */,
642 | 438257AD1C1A6F5F00A3DA24 /* Release */,
643 | );
644 | defaultConfigurationIsVisible = 0;
645 | defaultConfigurationName = Release;
646 | };
647 | 438257AE1C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTest" */ = {
648 | isa = XCConfigurationList;
649 | buildConfigurations = (
650 | 438257AF1C1A6F5F00A3DA24 /* Debug */,
651 | 438257B01C1A6F5F00A3DA24 /* Release */,
652 | );
653 | defaultConfigurationIsVisible = 0;
654 | defaultConfigurationName = Release;
655 | };
656 | 438257B11C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTestTests" */ = {
657 | isa = XCConfigurationList;
658 | buildConfigurations = (
659 | 438257B21C1A6F5F00A3DA24 /* Debug */,
660 | 438257B31C1A6F5F00A3DA24 /* Release */,
661 | );
662 | defaultConfigurationIsVisible = 0;
663 | defaultConfigurationName = Release;
664 | };
665 | 438257B41C1A6F5F00A3DA24 /* Build configuration list for PBXNativeTarget "CJLabelTestUITests" */ = {
666 | isa = XCConfigurationList;
667 | buildConfigurations = (
668 | 438257B51C1A6F5F00A3DA24 /* Debug */,
669 | 438257B61C1A6F5F00A3DA24 /* Release */,
670 | );
671 | defaultConfigurationIsVisible = 0;
672 | defaultConfigurationName = Release;
673 | };
674 | /* End XCConfigurationList section */
675 | };
676 | rootObject = 438257791C1A6F5F00A3DA24 /* Project object */;
677 | }
678 |
--------------------------------------------------------------------------------