├── .gitignore ├── GRichLabel ├── GMatching │ ├── GMatcherExpression.h │ ├── GMatcherExpression.m │ ├── GMatcherProcessor │ │ ├── GACMatcherProcessor.h │ │ ├── GACMatcherProcessor.m │ │ ├── GKMPMatcherProcessor.h │ │ ├── GKMPMatcherProcessor.m │ │ ├── GRegexMatcherProcessor.h │ │ ├── GRegexMatcherProcessor.m │ │ ├── IMatcherObject.h │ │ └── IMatcherProcessor.h │ └── GMatcherUtil │ │ ├── GMatcherResult.h │ │ ├── GMatcherResult.m │ │ ├── GThreadSafeArray.h │ │ ├── GThreadSafeArray.m │ │ ├── GThreadSafeDictionary.h │ │ └── GThreadSafeDictionary.m ├── RichLabel │ ├── Common │ │ ├── GAttributedConfiguration.h │ │ ├── GAttributedConfiguration.m │ │ ├── GAttributedStringFactory.h │ │ ├── GAttributedStringFactory.m │ │ ├── GAttributedToken.h │ │ ├── GAttributedToken.m │ │ ├── GDrawTextBuilder.h │ │ ├── GDrawTextBuilder.m │ │ ├── GEmojiConfigManager.h │ │ ├── GEmojiConfigManager.m │ │ ├── GMenuContextProtocol.h │ │ ├── GRichLabel+TextMenuConfiguration.h │ │ ├── GRichLabel+TextMenuConfiguration.m │ │ ├── GTextMenuConfiguration.h │ │ ├── GTextMenuConfiguration.m │ │ ├── GTokenAppearance.h │ │ ├── GTokenAppearance.m │ │ ├── NSAttributedString+GRichLabel.h │ │ ├── NSAttributedString+GRichLabel.m │ │ ├── NSMutableAttributedString+GRichLabel.h │ │ ├── NSMutableAttributedString+GRichLabel.m │ │ ├── NSParagraphStyle+GRichLabel.h │ │ └── NSParagraphStyle+GRichLabel.m │ ├── GRichLabel.h │ ├── GRichLabel.m │ └── Internal │ │ ├── GCursor.h │ │ ├── GCursor.m │ │ ├── GEmojiRunDelegate.h │ │ ├── GEmojiRunDelegate.m │ │ ├── GLineLayout.h │ │ ├── GLineLayout.m │ │ ├── GMagnifiter.h │ │ ├── GMagnifiter.m │ │ ├── GMagnifiterWindow.h │ │ ├── GMagnifiterWindow.m │ │ ├── GSelectionView.h │ │ ├── GSelectionView.m │ │ ├── GTextUtils.h │ │ ├── GTextUtils.m │ │ ├── UIView+GText.h │ │ └── UIView+GText.m └── Throttle │ ├── GThrottleInvoke.h │ └── GThrottleInvoke.m ├── GRichLabelExample.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── GRichLabelExample.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── GRichLabelExample ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── novelbg.imageset │ │ ├── Contents.json │ │ └── novelbg@2x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── ChatTableViewCell.h ├── ChatTableViewCell.m ├── ChatViewController.h ├── ChatViewController.m ├── DemoTextMenuConfig.h ├── DemoTextMenuConfig.m ├── Info.plist ├── LabelViewController.h ├── LabelViewController.m ├── ViewController.h ├── ViewController.m ├── emoji │ ├── anwei@2x.png │ ├── dabian@2x.png │ ├── daku@2x.png │ └── expression.plist ├── main.m ├── novelReadViewController.h ├── novelReadViewController.m ├── novelabcd.jpg ├── novelabcd@2x.jpg ├── titlebar_bt_search03@2x.png └── titlebar_bt_search03@3x.png ├── GRichLabelExampleTests ├── GRichLabelExampleTests.m └── Info.plist ├── GRichLabelExampleUITests ├── GRichLabelExampleUITests.m └── Info.plist ├── LICENSE ├── Podfile ├── Podfile.lock ├── Pods ├── GBigbang │ ├── GBigbang │ │ ├── GBigbang.h │ │ ├── GBigbangBox.h │ │ ├── GBigbangBox.m │ │ ├── GTagFlowCell.h │ │ ├── GTagFlowCell.m │ │ ├── GTagFlowContainer.h │ │ ├── GTagFlowContainer.m │ │ ├── GTagFlowLayout.h │ │ ├── GTagFlowLayout.m │ │ ├── GTagFlowView.h │ │ └── GTagFlowView.m │ ├── LICENSE │ └── README.md ├── GMenuController │ ├── GMenuController │ │ ├── GMenuController.h │ │ ├── GMenuController.m │ │ ├── GMenuControllerHeader.h │ │ └── Internal │ │ │ ├── GAdjustButton.h │ │ │ ├── GAdjustButton.m │ │ │ ├── GMenuDefaultView.h │ │ │ ├── GMenuDefaultView.m │ │ │ ├── GMenuEffectsWindow.h │ │ │ ├── GMenuEffectsWindow.m │ │ │ ├── GMenuViewContainer.h │ │ │ └── GMenuViewContainer.m │ ├── LICENSE │ └── README.md ├── Headers │ ├── Private │ │ ├── GBigbang │ │ │ ├── GBigbang.h │ │ │ ├── GBigbangBox.h │ │ │ ├── GTagFlowCell.h │ │ │ ├── GTagFlowContainer.h │ │ │ ├── GTagFlowLayout.h │ │ │ └── GTagFlowView.h │ │ ├── GMenuController │ │ │ ├── GAdjustButton.h │ │ │ ├── GMenuController.h │ │ │ ├── GMenuControllerHeader.h │ │ │ ├── GMenuDefaultView.h │ │ │ ├── GMenuEffectsWindow.h │ │ │ └── GMenuViewContainer.h │ │ └── YYAsyncLayer │ │ │ ├── YYAsyncLayer.h │ │ │ ├── YYSentinel.h │ │ │ └── YYTransaction.h │ └── Public │ │ ├── GBigbang │ │ ├── GBigbang.h │ │ ├── GBigbangBox.h │ │ ├── GTagFlowCell.h │ │ ├── GTagFlowContainer.h │ │ ├── GTagFlowLayout.h │ │ └── GTagFlowView.h │ │ ├── GMenuController │ │ ├── GAdjustButton.h │ │ ├── GMenuController.h │ │ ├── GMenuControllerHeader.h │ │ ├── GMenuDefaultView.h │ │ ├── GMenuEffectsWindow.h │ │ └── GMenuViewContainer.h │ │ └── YYAsyncLayer │ │ ├── YYAsyncLayer.h │ │ ├── YYSentinel.h │ │ └── YYTransaction.h ├── Manifest.lock ├── Pods.xcodeproj │ └── project.pbxproj ├── Target Support Files │ ├── GBigbang │ │ ├── GBigbang-dummy.m │ │ ├── GBigbang-prefix.pch │ │ └── GBigbang.xcconfig │ ├── GMenuController │ │ ├── GMenuController-dummy.m │ │ ├── GMenuController-prefix.pch │ │ └── GMenuController.xcconfig │ ├── Pods-GRichLabelExample │ │ ├── Pods-GRichLabelExample-acknowledgements.markdown │ │ ├── Pods-GRichLabelExample-acknowledgements.plist │ │ ├── Pods-GRichLabelExample-dummy.m │ │ ├── Pods-GRichLabelExample-frameworks.sh │ │ ├── Pods-GRichLabelExample-resources.sh │ │ ├── Pods-GRichLabelExample.debug.xcconfig │ │ └── Pods-GRichLabelExample.release.xcconfig │ ├── Pods-GRichLabelExampleTests │ │ ├── Pods-GRichLabelExampleTests-acknowledgements.markdown │ │ ├── Pods-GRichLabelExampleTests-acknowledgements.plist │ │ ├── Pods-GRichLabelExampleTests-dummy.m │ │ ├── Pods-GRichLabelExampleTests-frameworks.sh │ │ ├── Pods-GRichLabelExampleTests-resources.sh │ │ ├── Pods-GRichLabelExampleTests.debug.xcconfig │ │ └── Pods-GRichLabelExampleTests.release.xcconfig │ ├── Pods-GRichLabelExampleUITests │ │ ├── Pods-GRichLabelExampleUITests-acknowledgements.markdown │ │ ├── Pods-GRichLabelExampleUITests-acknowledgements.plist │ │ ├── Pods-GRichLabelExampleUITests-dummy.m │ │ ├── Pods-GRichLabelExampleUITests-frameworks.sh │ │ ├── Pods-GRichLabelExampleUITests-resources.sh │ │ ├── Pods-GRichLabelExampleUITests.debug.xcconfig │ │ └── Pods-GRichLabelExampleUITests.release.xcconfig │ └── YYAsyncLayer │ │ ├── YYAsyncLayer-dummy.m │ │ ├── YYAsyncLayer-prefix.pch │ │ └── YYAsyncLayer.xcconfig └── YYAsyncLayer │ ├── LICENSE │ ├── README.md │ └── YYAsyncLayer │ ├── YYAsyncLayer.h │ ├── YYAsyncLayer.m │ ├── YYSentinel.h │ ├── YYSentinel.m │ ├── YYTransaction.h │ └── YYTransaction.m ├── README.md └── screenshot ├── 001.png ├── 002.png └── 003.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherExpression.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMatcherExpression.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GMatcherResult.h" 11 | #import "IMatcherObject.h" 12 | typedef NS_ENUM(NSUInteger, GMatchingOption) { 13 | GMatchingOption_Auto, 14 | GMatchingOption_KMP, 15 | GMatchingOption_AC, 16 | GMatchingOption_Regex, 17 | }; 18 | 19 | @interface GMatcherExpression : NSObject 20 | 21 | + (GMatcherExpression *)matcherExpressionWithPattern:(NSString*)pattern; 22 | + (GMatcherExpression *)matcherExpressionWithPatterns:(NSArray*)patterns; 23 | + (GMatcherExpression *)matcherExpressionWithPattern:(NSString*)pattern option:(GMatchingOption)option; 24 | + (GMatcherExpression *)matcherExpressionWithPatterns:(NSArray*)patterns option:(GMatchingOption)option; 25 | 26 | + (GMatcherExpression *)matcherExpressionWithObjectPatterns:(NSArray>*)patterns; 27 | + (GMatcherExpression *)matcherExpressionWithObjectPatterns:(NSArray>*)patterns option:(GMatchingOption)option; 28 | 29 | - (void)enumerateMatchesInString:(NSString *)string usingBlock:(void (NS_NOESCAPE ^)(GMatcherResult * result, BOOL *stop))block; 30 | 31 | - (NSArray *)matchesInString:(NSString *)string; 32 | 33 | - (NSUInteger)numberOfMatchesInString:(NSString *)string ; 34 | 35 | - (NSTextCheckingResult *)firstMatchInString:(NSString *)string; 36 | 37 | - (NSRange)rangeOfFirstMatchInString:(NSString *)string; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherExpression.m: -------------------------------------------------------------------------------- 1 | // 2 | // GMatcherExpression.m 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GMatcherExpression.h" 10 | #import "IMatcherProcessor.h" 11 | #import "GKMPMatcherProcessor.h" 12 | #import "GACMatcherProcessor.h" 13 | #import "GRegexMatcherProcessor.h" 14 | @interface GMatcherExpression() 15 | @property (nonatomic, assign) GMatchingOption matchingOption; 16 | @property (nonatomic, strong) NSArray * pattens; 17 | @property (nonatomic, strong) id matcherProcessor; 18 | @end 19 | 20 | @implementation GMatcherExpression 21 | 22 | + (GMatcherExpression *)matcherExpressionWithPattern:(NSString*)pattern 23 | { 24 | NSParameterAssert(pattern); 25 | if (!pattern) return nil; 26 | return [[GMatcherExpression alloc] initWithPatterns:@[pattern] option:GMatchingOption_Auto]; 27 | } 28 | 29 | + (GMatcherExpression *)matcherExpressionWithPatterns:(NSArray*)patterns 30 | { 31 | NSParameterAssert(patterns); 32 | if (!patterns) return nil; 33 | return [[GMatcherExpression alloc] initWithPatterns:patterns option:GMatchingOption_Auto]; 34 | } 35 | 36 | + (GMatcherExpression *)matcherExpressionWithPattern:(NSString*)pattern option:(GMatchingOption)option 37 | { 38 | NSParameterAssert(pattern); 39 | if (!pattern) return nil; 40 | return [[GMatcherExpression alloc] initWithPatterns:@[pattern] option:option]; 41 | } 42 | + (GMatcherExpression *)matcherExpressionWithPatterns:(NSArray*)patterns option:(GMatchingOption)option 43 | { 44 | NSParameterAssert(patterns); 45 | if (!patterns) return nil; 46 | return [[GMatcherExpression alloc] initWithPatterns:patterns option:option]; 47 | } 48 | 49 | + (GMatcherExpression *)matcherExpressionWithObjectPatterns:(NSArray>*)patterns 50 | { 51 | NSParameterAssert(patterns); 52 | if (!patterns) return nil; 53 | return [[GMatcherExpression alloc] initWithPatterns:patterns option:GMatchingOption_Auto]; 54 | } 55 | 56 | + (GMatcherExpression *)matcherExpressionWithObjectPatterns:(NSArray>*)patterns option:(GMatchingOption)option 57 | { 58 | NSParameterAssert(patterns); 59 | if (!patterns) return nil; 60 | return [[GMatcherExpression alloc] initWithPatterns:patterns option:option]; 61 | } 62 | 63 | - (instancetype)initWithPatterns:(NSArray *)patterns option:(GMatchingOption)option 64 | { 65 | self = [super init]; 66 | if (self) { 67 | self.matchingOption = option; 68 | self.pattens = patterns; 69 | 70 | self.matcherProcessor = [self createMatcherProcessor]; 71 | [self.matcherProcessor matcherExpressionWithPatterns:patterns]; 72 | } 73 | return self; 74 | } 75 | 76 | - (id)createMatcherProcessor 77 | { 78 | switch (self.matchingOption) { 79 | case GMatchingOption_Auto: 80 | return [self createAutoMatcherProcessor]; 81 | break; 82 | case GMatchingOption_KMP: 83 | return [self createKMPMatcherProcessor]; 84 | break; 85 | case GMatchingOption_AC: 86 | return [self createACMatcherProcessor]; 87 | break; 88 | case GMatchingOption_Regex: 89 | return [self createRegularMatcherProcessor]; 90 | break; 91 | default: 92 | break; 93 | } 94 | return nil; 95 | } 96 | 97 | - (id)createAutoMatcherProcessor 98 | { 99 | if (self.pattens.count > 10) { 100 | return [self createACMatcherProcessor]; 101 | } else { 102 | return [self createKMPMatcherProcessor]; 103 | } 104 | } 105 | 106 | - (id)createKMPMatcherProcessor 107 | { 108 | return [[GKMPMatcherProcessor alloc] init]; 109 | } 110 | 111 | - (id)createACMatcherProcessor 112 | { 113 | return [[GACMatcherProcessor alloc] init]; 114 | } 115 | 116 | - (id)createRegularMatcherProcessor 117 | { 118 | return [[GRegexMatcherProcessor alloc] init]; 119 | } 120 | 121 | #pragma mark - Public Method 122 | 123 | - (void)enumerateMatchesInString:(NSString *)string usingBlock:(void (NS_NOESCAPE ^)(GMatcherResult * result, BOOL *stop))block 124 | { 125 | [self.matcherProcessor enumerateMatchesInString:string usingBlock:block]; 126 | } 127 | 128 | - (NSArray *)matchesInString:(NSString *)string 129 | { 130 | return [self.matcherProcessor matchesInString:string]; 131 | } 132 | 133 | - (NSUInteger)numberOfMatchesInString:(NSString *)string 134 | { 135 | NSArray * array = [self.matcherProcessor matchesInString:string]; 136 | return array.count; 137 | } 138 | 139 | - (GMatcherResult *)firstMatchInString:(NSString *)string 140 | { 141 | __block GMatcherResult *matcher = nil; 142 | [self enumerateMatchesInString:string usingBlock:^(GMatcherResult *result, BOOL *stop) { 143 | matcher = result; 144 | *stop = YES; 145 | }]; 146 | return matcher; 147 | } 148 | 149 | - (NSRange)rangeOfFirstMatchInString:(NSString *)string 150 | { 151 | __block GMatcherResult *matcher = nil; 152 | [self enumerateMatchesInString:string usingBlock:^(GMatcherResult *result, BOOL *stop) { 153 | matcher = result; 154 | *stop = YES; 155 | }]; 156 | return matcher.range; 157 | } 158 | @end 159 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherProcessor/GACMatcherProcessor.h: -------------------------------------------------------------------------------- 1 | // 2 | // GACMatcherProcessor.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "IMatcherProcessor.h" 11 | @interface GACMatcherProcessor : NSObject 12 | 13 | /** 14 | 插入匹配字符串 15 | 16 | @param aString 匹配串 17 | */ 18 | - (void)insertString:(NSString*)aString; 19 | - (void)insertStrings:(NSArray*)strings; 20 | 21 | 22 | /** 23 | 生成/销毁 匹配树 24 | */ 25 | - (void)build; 26 | - (void)destroy; 27 | 28 | /** 29 | 查询匹配结果. 30 | 31 | @param searchStr 需要查询匹配结果的字符串 32 | @return 匹配结果集合 33 | */ 34 | - (NSArray*)searchString:(NSString*)searchStr; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherProcessor/GKMPMatcherProcessor.h: -------------------------------------------------------------------------------- 1 | // 2 | // GKMPMatcherProcessor.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/19. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "IMatcherProcessor.h" 11 | 12 | @interface GKMPMatcherProcessor : NSObject 13 | - (void)insertString:(NSString*)aString; 14 | - (NSArray*)searchString:(NSString*)searchStr; 15 | @end 16 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherProcessor/GRegexMatcherProcessor.h: -------------------------------------------------------------------------------- 1 | // 2 | // GRegexMatcherProcessor.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "IMatcherProcessor.h" 11 | #import "IMatcherObject.h" 12 | @interface GRegexMatcherProcessor : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherProcessor/GRegexMatcherProcessor.m: -------------------------------------------------------------------------------- 1 | // 2 | // GRegexMatcherProcessor.m 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GRegexMatcherProcessor.h" 10 | #import "GThreadSafeArray.h" 11 | #import "GThreadSafeDictionary.h" 12 | #import "IMatcherObject.h" 13 | @interface GRegexMatcherProcessor() 14 | @property (nonatomic, strong) GThreadSafeArray * safeArray; 15 | @property (nonatomic, strong) GThreadSafeDictionary * safeMap; 16 | @property (nonatomic, strong) GThreadSafeDictionary * userInfoMap; 17 | @end 18 | 19 | @implementation GRegexMatcherProcessor 20 | 21 | - (GThreadSafeDictionary *)userInfoMap 22 | { 23 | if (!_userInfoMap) { 24 | _userInfoMap = [GThreadSafeDictionary dictionary]; 25 | } 26 | return _userInfoMap; 27 | } 28 | 29 | - (GThreadSafeArray *)safeArray 30 | { 31 | if (!_safeArray) { 32 | _safeArray = [GThreadSafeArray array]; 33 | } 34 | return _safeArray; 35 | } 36 | 37 | - (GThreadSafeDictionary*)safeMap 38 | { 39 | if (!_safeMap) { 40 | _safeMap = [GThreadSafeDictionary dictionary]; 41 | } 42 | return _safeMap; 43 | } 44 | 45 | - (void)processRegularExpression:(NSString*)pattern userInfo:(id)userinfo 46 | { 47 | NSRegularExpression * regular = [self.safeMap objectForKey:pattern]; 48 | if (regular) { 49 | return; 50 | } 51 | NSError *error; 52 | NSRegularExpression * regularNew = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionIgnoreMetacharacters error:&error]; 53 | if (!error) { 54 | [self.safeMap setObject:regularNew forKey:pattern]; 55 | [self.userInfoMap setObject:userinfo forKey:pattern]; 56 | } 57 | } 58 | 59 | - (void)matcherExpressionWithPatterns:(NSArray*)patterns 60 | { 61 | [patterns enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 62 | if ([obj isKindOfClass:[NSString class]]) { 63 | [self processRegularExpression:obj userInfo:nil]; 64 | } else if ([obj respondsToSelector:NSSelectorFromString(@"getPatternString")]) { 65 | NSString * pattern = [obj performSelector:NSSelectorFromString(@"getPatternString")]; 66 | [self processRegularExpression:pattern userInfo:obj]; 67 | } 68 | }]; 69 | } 70 | 71 | - (NSArray *)matchesInString:(NSString *)string 72 | { 73 | __block NSMutableArray * array = [NSMutableArray array]; 74 | [self.safeMap iterateWitHandler:^BOOL(id key, NSRegularExpression* obj) { 75 | if ([obj isKindOfClass:[NSRegularExpression class]]) { 76 | NSArray * result = [obj matchesInString:string options:0 range:NSMakeRange(0, string.length)]; 77 | [array addObjectsFromArray:result]; 78 | } 79 | return NO; 80 | }]; 81 | return array.copy; 82 | } 83 | 84 | - (void)enumerateMatchesInString:(NSString *)string usingBlock:(void (NS_NOESCAPE ^)(GMatcherResult * result, BOOL *stop))block 85 | { 86 | [self.safeMap iterateWitHandler:^BOOL(id key, NSRegularExpression* obj) { 87 | id userinfo = [self.userInfoMap objectForKey:key]; 88 | if ([obj isKindOfClass:[NSRegularExpression class]]) { 89 | [obj enumerateMatchesInString:string options:0 range:NSMakeRange(0, string.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { 90 | if (block) { 91 | BOOL st = NO; 92 | GMatcherResult * matcher = [GMatcherResult result:key location:result.range.location userInfo:userinfo]; 93 | block(matcher,&st); 94 | if (st) { 95 | *stop = YES; 96 | } 97 | } 98 | }]; 99 | } 100 | return NO; 101 | }]; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherProcessor/IMatcherObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMatcherObject.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/19. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol IMatcherObject 12 | 13 | @required 14 | - (NSString *)getPatternString; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherProcessor/IMatcherProcessor.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMatcherProcessor.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GMatcherResult.h" 11 | @protocol IMatcherProcessor 12 | 13 | - (void)matcherExpressionWithPatterns:(NSArray*)patterns; 14 | - (NSArray *)matchesInString:(NSString *)string; 15 | - (void)enumerateMatchesInString:(NSString *)string usingBlock:(void (NS_NOESCAPE ^)(GMatcherResult * result, BOOL *stop))block; 16 | 17 | @end 18 | 19 | 20 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherUtil/GMatcherResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMatcherResult.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GMatcherResult : NSObject 12 | 13 | @property (nonatomic, assign) NSRange range; 14 | @property (nonatomic, copy) NSString* string; 15 | @property (nonatomic, strong) id info; 16 | 17 | + (instancetype)result:(NSString*)string location:(long)location userInfo:(id)info; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherUtil/GMatcherResult.m: -------------------------------------------------------------------------------- 1 | // 2 | // GMatcherResult.m 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GMatcherResult.h" 10 | 11 | @implementation GMatcherResult 12 | 13 | + (instancetype)result:(NSString*)string location:(long)location userInfo:(id)info 14 | { 15 | GMatcherResult * result = [[GMatcherResult alloc] init]; 16 | result.range = NSMakeRange(location, [string length]); 17 | result.string = string; 18 | result.info = info; 19 | return result; 20 | } 21 | 22 | - (NSString*)description 23 | { 24 | return [NSString stringWithFormat:@"%@,%@", NSStringFromRange(_range), _string]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherUtil/GThreadSafeArray.h: -------------------------------------------------------------------------------- 1 | // 2 | // GThreadSafeArray.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GThreadSafeArray : NSObject 12 | 13 | + (instancetype)array; 14 | 15 | - (NSUInteger)count; 16 | 17 | - (id)objectAtIndex:(NSUInteger)index; 18 | 19 | - (void)addObject:(id)object; 20 | 21 | - (void)removeObject:(id)object; 22 | 23 | - (void)removeAllObject; 24 | 25 | - (void)iterateWitHandler:(BOOL(^)(id element))handler; 26 | 27 | @end 28 | 29 | 30 | @interface NSArray (Safely) 31 | 32 | - (id)objectAtIndexSafely:(NSUInteger)index; 33 | 34 | @end 35 | 36 | @interface NSMutableArray (Safely) 37 | 38 | - (void)addObjectSafely:(id)anObject; 39 | - (void)insertObjectSafely:(id)anObject atIndex:(NSUInteger)index; 40 | - (void)removeObjectAtIndexSafely:(NSUInteger)index; 41 | - (void)replaceObjectAtIndexSafely:(NSUInteger)index withObject:(id)anObject; 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherUtil/GThreadSafeArray.m: -------------------------------------------------------------------------------- 1 | // 2 | // GThreadSafeArray.m 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GThreadSafeArray.h" 10 | 11 | @interface GThreadSafeArray() 12 | @property (nonatomic, strong) NSLock* lock; 13 | @property (nonatomic, strong) NSMutableArray* array; 14 | @end 15 | 16 | @implementation GThreadSafeArray 17 | 18 | + (instancetype)array 19 | { 20 | GThreadSafeArray * array = [[GThreadSafeArray alloc] init]; 21 | return array; 22 | } 23 | 24 | - (instancetype)init 25 | { 26 | if (self = [super init]) { 27 | self.array = [NSMutableArray array]; 28 | self.lock = [[NSLock alloc] init]; 29 | } 30 | return self; 31 | } 32 | 33 | - (NSUInteger)count 34 | { 35 | while (![self.lock tryLock]) { 36 | usleep(10*1000); 37 | } 38 | 39 | NSUInteger num = self.array.count; 40 | 41 | [self.lock unlock]; 42 | 43 | return num; 44 | } 45 | 46 | 47 | - (id)objectAtIndex:(NSUInteger)index; 48 | { 49 | while (![self.lock tryLock]) { 50 | usleep(10*1000); 51 | } 52 | 53 | if (self.array.count <= index) { 54 | [_lock unlock]; 55 | return nil; 56 | } 57 | id object = [self.array objectAtIndex:index]; 58 | 59 | [_lock unlock]; 60 | 61 | return object; 62 | } 63 | 64 | 65 | - (void)addObject:(id)object 66 | { 67 | if (!object) return; 68 | 69 | while (![self.lock tryLock]) { 70 | usleep(10*1000); 71 | } 72 | [self.array addObject:object]; 73 | 74 | [self.lock unlock]; 75 | } 76 | 77 | 78 | - (void)removeObject:(id)object 79 | { 80 | if (!object) { 81 | return; 82 | } 83 | 84 | while (![_lock tryLock]) { 85 | usleep(10 * 1000); 86 | } 87 | 88 | [self.array removeObject:object]; 89 | 90 | [self.lock unlock]; 91 | } 92 | 93 | 94 | - (void)removeAllObject 95 | { 96 | while (![_lock tryLock]) { 97 | usleep(10 * 1000); 98 | } 99 | 100 | [self.array removeAllObjects]; 101 | 102 | [self.lock unlock]; 103 | } 104 | 105 | - (void)iterateWitHandler:(BOOL(^)(id element))handler 106 | { 107 | if (!handler) { 108 | return; 109 | } 110 | 111 | while (![_lock tryLock]) { 112 | usleep(10 * 1000); 113 | } 114 | 115 | for (id element in self.array) { 116 | BOOL result = handler(element); 117 | 118 | if (result) { 119 | break; 120 | } 121 | } 122 | 123 | handler = nil; 124 | 125 | [_lock unlock]; 126 | } 127 | 128 | @end 129 | 130 | @implementation NSArray (Safely) 131 | 132 | - (id)objectAtIndexSafely:(NSUInteger)index { 133 | if (index >= self.count) { 134 | return nil; 135 | } 136 | return [self objectAtIndex:index]; 137 | } 138 | 139 | @end 140 | 141 | 142 | @implementation NSMutableArray (Safely) 143 | 144 | - (void)addObjectSafely:(id)anObject { 145 | if (anObject != nil) { 146 | [self addObject:anObject]; 147 | } 148 | } 149 | 150 | - (void)insertObjectSafely:(id)anObject atIndex:(NSUInteger)index { 151 | if (anObject != nil ) { 152 | [self insertObject:anObject atIndex:index]; 153 | } 154 | } 155 | 156 | - (void)removeObjectAtIndexSafely:(NSUInteger)index { 157 | if (index < [self count]) { 158 | [self removeObjectAtIndex:index]; 159 | } 160 | } 161 | 162 | - (void)replaceObjectAtIndexSafely:(NSUInteger)index withObject:(id)anObject { 163 | if (anObject != nil && index < [self count]) { 164 | [self replaceObjectAtIndex:index withObject:anObject]; 165 | } 166 | } 167 | 168 | 169 | @end 170 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherUtil/GThreadSafeDictionary.h: -------------------------------------------------------------------------------- 1 | // 2 | // GThreadSafeDictionary.h 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GThreadSafeDictionary : NSObject 12 | 13 | + (instancetype)dictionary; 14 | 15 | - (NSUInteger)count; 16 | 17 | - (NSArray*)allKeys; 18 | 19 | - (__kindof id)objectForKey:(NSString*)key; 20 | 21 | - (void)setObject:(id)value forKey:(NSString *)key; 22 | 23 | - (void)removeObjectForKey:(id)key; 24 | 25 | - (void)removeAllObjects; 26 | 27 | - (void)iterateWitHandler:(BOOL(^)(id key, id obj))handler; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /GRichLabel/GMatching/GMatcherUtil/GThreadSafeDictionary.m: -------------------------------------------------------------------------------- 1 | // 2 | // GThreadSafeDictionary.m 3 | // GMatchingKit 4 | // 5 | // Created by GIKI on 2018/4/18. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GThreadSafeDictionary.h" 10 | @interface GThreadSafeDictionary () 11 | 12 | @property(nonatomic, strong) NSMutableDictionary * dict; 13 | @property (nonatomic, strong) NSLock * lock; 14 | 15 | @end 16 | 17 | @implementation GThreadSafeDictionary 18 | 19 | + (instancetype)dictionary 20 | { 21 | GThreadSafeDictionary * dict = [[GThreadSafeDictionary alloc] init]; 22 | return dict; 23 | } 24 | 25 | - (instancetype)init 26 | { 27 | if (self = [super init]) { 28 | self.dict = [NSMutableDictionary dictionary]; 29 | _lock = [[NSLock alloc] init]; 30 | } 31 | return self; 32 | } 33 | 34 | -(void)dealloc 35 | { 36 | _dict = nil; 37 | _lock = nil; 38 | } 39 | 40 | - (NSUInteger)count 41 | { 42 | while (![_lock tryLock]) { 43 | usleep(10*1000); 44 | } 45 | 46 | NSUInteger num = [self.dict count]; 47 | 48 | [_lock unlock]; 49 | 50 | return num; 51 | } 52 | 53 | - (NSArray*)allKeys 54 | { 55 | while (![_lock tryLock]) { 56 | usleep(10*1000); 57 | } 58 | 59 | NSArray * allkeys = [self.dict allKeys]; 60 | 61 | [_lock unlock]; 62 | 63 | return allkeys; 64 | } 65 | 66 | - (__kindof id)objectForKey:(NSString*)key 67 | { 68 | if (key.length <= 0) { 69 | return nil; 70 | } 71 | 72 | while (![_lock tryLock]) { 73 | usleep(10*1000); 74 | } 75 | 76 | id object = [self.dict objectForKey:key]; 77 | 78 | [_lock unlock]; 79 | 80 | return object; 81 | } 82 | 83 | - (void)setObject:(id)value forKey:(NSString *)key 84 | { 85 | if (!value || key.length <= 0) { 86 | return; 87 | } 88 | 89 | while (![_lock tryLock]) { 90 | usleep(10*1000); 91 | } 92 | [self.dict setObject:value forKey:key]; 93 | 94 | [_lock unlock]; 95 | 96 | } 97 | 98 | - (void)removeObjectForKey:(id)key 99 | { 100 | if (key == nil) { 101 | return; 102 | } 103 | 104 | while (![_lock tryLock]) { 105 | usleep(10 * 1000); 106 | } 107 | 108 | [self.dict removeObjectForKey:key]; 109 | 110 | [_lock unlock]; 111 | 112 | } 113 | 114 | - (void)removeAllObjects 115 | { 116 | while (![_lock tryLock]) { 117 | usleep(10 * 1000); 118 | } 119 | 120 | [self.dict removeAllObjects]; 121 | 122 | 123 | [_lock unlock]; 124 | } 125 | 126 | - (void)iterateWitHandler:(BOOL(^)(id key, id obj))handler 127 | { 128 | if (!handler) { 129 | return; 130 | } 131 | 132 | while (![_lock tryLock]) { 133 | usleep(10 * 1000); 134 | } 135 | [self.dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 136 | if (!handler) { 137 | *stop = YES; 138 | } 139 | BOOL result = handler(key,obj); 140 | if (result) { 141 | *stop = YES; 142 | } 143 | }]; 144 | handler = nil; 145 | 146 | [_lock unlock]; 147 | } 148 | @end 149 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GAttributedConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // GAttributedConfiguration.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/4. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "GAttributedStringFactory.h" 12 | #import "GAttributedToken.h" 13 | /** 14 | A quick attribute configuration class 15 | Don't need to deal with complex NSAttributedString 16 | */ 17 | 18 | @interface GAttributedConfiguration : NSObject 19 | 20 | @property (nonatomic, strong) NSString * text; 21 | 22 | /** 23 | 文本颜色 24 | @default : [UIColor blackColor] 25 | */ 26 | @property (nonatomic, strong) UIColor * textColor; 27 | 28 | /** 29 | 自定义高亮文字颜色 30 | @brief: 如果在GAttributedToken中设置的tokenColor,此属性不生效. 31 | 32 | @default: [UIColor blueColor] 33 | */ 34 | @property (nonatomic, strong) UIColor * tokenTextColor; 35 | 36 | /** 37 | 文字Font 38 | @brief:如果在GAttributedToken中设置的tokenFont,高亮展示文本不跟随此属性.. 39 | 40 | @default: 14 41 | */ 42 | @property (nonatomic, strong) UIFont *font; 43 | 44 | /** 45 | 行间距 46 | 47 | @default:1 48 | */ 49 | @property (nonatomic, assign) CGFloat linespace; 50 | 51 | /** 52 | 换行方式 53 | 54 | @default:kCTLineBreakByWordWrapping 55 | */ 56 | @property (nonatomic, assign) CTLineBreakMode lineBreakMode; 57 | 58 | /** 59 | 截断AttributedString 60 | 61 | @defalut:nil 62 | */ 63 | @property (nonatomic, copy) NSAttributedString *truncationToken; 64 | 65 | /** 66 | 文本对齐方式 67 | 68 | @default:kCTLineBreakByWordWrapping,如果想要文本排版左右对齐,请使用kCTTextAlignmentJustified 69 | */ 70 | @property (nonatomic, assign) CTTextAlignment textAlignment; 71 | 72 | /** 73 | 首行缩进 74 | 75 | @default : 0 76 | */ 77 | @property (nonatomic, assign) CGFloat lineIndent; 78 | 79 | /** 80 | 自定义文本显示属性数组 81 | */ 82 | @property (nonatomic, strong) NSArray * tokenPatternConfigs; 83 | 84 | /** 85 | 正则属性数组 86 | */ 87 | @property (nonatomic, strong) NSArray * regexPatternConifgs; 88 | 89 | + (instancetype)attributedConfig:(NSString*)text; 90 | + (instancetype)attributedConfig:(NSString*)text color:(UIColor*)color font:(UIFont*)font; 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GAttributedConfiguration.m: -------------------------------------------------------------------------------- 1 | // 2 | // GAttributedConfiguration.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/4. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GAttributedConfiguration.h" 10 | 11 | @implementation GAttributedConfiguration 12 | 13 | + (instancetype)attributedConfig:(NSString*)text 14 | { 15 | return [GAttributedConfiguration attributedConfig:text color:nil font:nil]; 16 | } 17 | 18 | + (instancetype)attributedConfig:(NSString*)text color:(UIColor*)color font:(UIFont*)font 19 | { 20 | GAttributedConfiguration *defalut = [[GAttributedConfiguration alloc] init]; 21 | if (text) defalut.text = text; 22 | if (color) defalut.textColor = color; 23 | if (font) defalut.font = font; 24 | return defalut; 25 | } 26 | 27 | - (instancetype)init 28 | { 29 | self = [super init]; 30 | if (self) { 31 | self.textColor = [UIColor blackColor]; 32 | self.tokenTextColor = [UIColor blueColor]; 33 | self.font = [UIFont systemFontOfSize:14]; 34 | self.linespace = 0; 35 | self.lineBreakMode = kCTLineBreakByWordWrapping; 36 | self.textAlignment = kCTLineBreakByWordWrapping; 37 | // self.numberOfLines = 1; 38 | self.lineIndent = 0; 39 | } 40 | return self; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GAttributedStringFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // GAttributedStringFactory.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/4. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | /** 14 | A AttributedString processing factory. Generate 'GAttributedConfiguration' through 'NSMutableAttributedString'. 15 | */ 16 | @class GAttributedConfiguration ,GDrawTextBuilder; 17 | 18 | @interface GAttributedStringFactory : NSObject 19 | 20 | /** 21 | create DrawTextBuilder 22 | DrawTextBuilder is a used to draw the rich text library class. 23 | @param config GAttributedConfiguration 24 | @return DrawTextBuilder 25 | */ 26 | + (GDrawTextBuilder*)createDrawTextBuilderWithAttributedConfig:(GAttributedConfiguration*)config boundSize:(CGSize)size; 27 | 28 | /** 29 | create AttributedString 30 | 31 | @param config GAttributedConfiguration 32 | @return NSMutableAttributedString 33 | */ 34 | + (NSMutableAttributedString*)createAttributedStringWithAttributedConfig:(GAttributedConfiguration*)config; 35 | 36 | /** 37 | calculate rich Text Height 38 | 39 | @param string attributedString 40 | @param width textContainer Max Width 41 | @return rich Text height 42 | */ 43 | + (CGFloat)getRichLabelHeightWithAttributedString:(NSAttributedString*)string MaxContianerWidth:(CGFloat)width; 44 | 45 | + (CGSize)getRichLabelDrawSizeWithAttributedString:(NSAttributedString*)string MaxContianerWidth:(CGFloat)width; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GAttributedToken.h: -------------------------------------------------------------------------------- 1 | // 2 | // GAttributedToken.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/12. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "GTokenAppearance.h" 12 | 13 | UIKIT_EXTERN NSString *const kGAttributeTokenHighlightName; 14 | 15 | UIKIT_EXTERN NSString *const kGAttributeTokenUnderlineName; 16 | 17 | UIKIT_EXTERN NSString *const kGAttributeTokenAttachmentName; 18 | 19 | UIKIT_EXTERN NSString *const kGAttributeTokenTruncationName; 20 | /// 表情.link等被替换的字符串 21 | UIKIT_EXTERN NSString *const kGAttributeTokenReplaceStringName; 22 | 23 | @class GAttributedToken; 24 | typedef void(^GAttributedTokenClick)(GAttributedToken *token); 25 | 26 | 27 | @interface GAttributedToken : NSObject 28 | ///文本token 29 | @property (nonatomic, strong) NSString * textToken; 30 | 31 | ///正则token 与textToken同时存在时不生效. 32 | @property (nonatomic, strong) NSString * regexToken; 33 | 34 | ///tokenColor 不传默认使用GAttributedConfiguration中的tokenTextColor 35 | @property (nonatomic, strong) UIColor * tokenColor; 36 | 37 | ///tokenFont 不传默认使用GAttributedConfiguration中的font 38 | @property (nonatomic, strong) UIFont *tokenFont; 39 | 40 | /// token Range 41 | @property (nonatomic, assign,readonly) NSRange tokenRange; 42 | 43 | /// 用于存储tokenInfo信息. 44 | @property (nonatomic, strong) id tokenInfo; 45 | 46 | /// token 点击 47 | @property (nonatomic, copy) GAttributedTokenClick tokenClickBlock; 48 | 49 | /** 50 | token 点击样式 51 | default: appearance.cornerRadius = 4; 52 | appearance.fillColor = [UIColor colorWithRed:69/255.0 green:111/255.0 blue:238/255.0 alpha:0.6]; 53 | */ 54 | @property (nonatomic, strong) GTokenAppearance * tokenAppearance; 55 | 56 | + (instancetype)attributedTextToken:(NSString*)textToken; 57 | + (instancetype)attributedTextToken:(NSString*)textToken color:(UIColor*)color; 58 | + (instancetype)attributedTextToken:(NSString*)textToken color:(UIColor*)color font:(UIFont*)font; 59 | 60 | + (instancetype)attributedRegexToken:(NSString*)textToken; 61 | + (instancetype)attributedRegexToken:(NSString*)textToken color:(UIColor*)color; 62 | + (instancetype)attributedRegexToken:(NSString*)textToken color:(UIColor*)color font:(UIFont*)font; 63 | @end 64 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GAttributedToken.m: -------------------------------------------------------------------------------- 1 | // 2 | // GAttributedToken.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/12. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GAttributedToken.h" 10 | 11 | NSString *const kGAttributeTokenHighlightName = @"kGAttributeTokenHighlightName"; 12 | NSString *const kGAttributeTokenUnderlineName = @"kGAttributeTokenUnderlineName"; 13 | NSString *const kGAttributeTokenAttachmentName = @"\uFFFC"; 14 | NSString *const kGAttributeTokenTruncationName = @"\u2026"; 15 | NSString *const kGAttributeTokenReplaceStringName = @"kGAttributeTokenReplaceStringName"; 16 | 17 | @implementation GAttributedToken 18 | 19 | + (instancetype)attributedTextToken:(NSString*)textToken 20 | { 21 | return [GAttributedToken attributedTextToken:textToken color:nil font:nil]; 22 | } 23 | 24 | + (instancetype)attributedTextToken:(NSString*)textToken color:(UIColor*)color 25 | { 26 | return [GAttributedToken attributedTextToken:textToken color:color font:nil]; 27 | } 28 | 29 | + (instancetype)attributedTextToken:(NSString*)textToken color:(UIColor*)color font:(UIFont*)font 30 | { 31 | GAttributedToken * token = [GAttributedToken new]; 32 | token.textToken = textToken; 33 | token.tokenColor = color; 34 | token.tokenFont = font; 35 | GTokenAppearance * appearance = [GTokenAppearance new]; 36 | appearance.cornerRadius = 4; 37 | appearance.fillColor = [UIColor colorWithRed:69/255.0 green:111/255.0 blue:238/255.0 alpha:0.6]; 38 | token.tokenAppearance = appearance; 39 | return token; 40 | } 41 | 42 | + (instancetype)attributedRegexToken:(NSString*)regexToken 43 | { 44 | return [GAttributedToken attributedRegexToken:regexToken color:nil font:nil]; 45 | } 46 | 47 | + (instancetype)attributedRegexToken:(NSString*)regexToken color:(UIColor*)color 48 | { 49 | return [GAttributedToken attributedRegexToken:regexToken color:color font:nil]; 50 | } 51 | 52 | + (instancetype)attributedRegexToken:(NSString*)regexToken color:(UIColor*)color font:(UIFont*)font 53 | { 54 | GAttributedToken * token = [GAttributedToken new]; 55 | token.regexToken = regexToken; 56 | token.tokenColor = color; 57 | token.tokenFont = font; 58 | GTokenAppearance * appearance = [GTokenAppearance new]; 59 | appearance.cornerRadius = 4; 60 | appearance.fillColor = [UIColor colorWithRed:58/255 green:59/255 blue:205/255 alpha:0.6]; 61 | token.tokenAppearance = appearance; 62 | return token; 63 | } 64 | 65 | #pragma mark - protocol Method 66 | - (NSString *)getPatternString 67 | { 68 | return self.textToken; 69 | } 70 | @end 71 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GDrawTextBuilder.h: -------------------------------------------------------------------------------- 1 | // 2 | // GDrawTextBuilder.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/17. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "GLineLayout.h" 12 | @class GAttributedToken; 13 | @interface GDrawTextBuilder : NSObject 14 | 15 | @property (nonatomic, strong,readonly) NSAttributedString *attributedString; 16 | @property (nonatomic, assign,readonly) CGSize boundSize; 17 | @property (nonatomic, assign,readonly) UIEdgeInsets edgeInsets; 18 | @property (nonatomic, assign,readonly) CGRect pathRect; 19 | @property (nonatomic, strong,readonly) NSArray *lineLayouts; 20 | @property (nonatomic, assign,readonly) CTFramesetterRef frameSetter; 21 | @property (nonatomic, assign,readonly) CTFrameRef ctFrame; 22 | @property (nonatomic, assign,readonly) BOOL hasEmojiImage; 23 | @property (nonatomic, strong,readonly) NSAttributedString *truncationToken; 24 | 25 | 26 | + (instancetype)buildDrawTextSize:(CGSize)size attributedString:(NSAttributedString*)string; 27 | + (instancetype)buildDrawTextSize:(CGSize)size insert:(UIEdgeInsets)edgeInsets attributedString:(NSAttributedString*)string; 28 | - (void)setFixedLineHeight:(BOOL)fixedLineHeight; 29 | 30 | - (void)drawAttributedText:(CGContextRef)context cancel:(BOOL (^)(void))cancel; 31 | 32 | @end 33 | 34 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GEmojiConfigManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // GEmojiConfigManager.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/6. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GEmojiConfigManager : NSObject 12 | 13 | + (GEmojiConfigManager *)sharedInstance; 14 | 15 | - (void)setEmojiConfigs:(NSDictionary*)configs; 16 | 17 | - (NSDictionary*)getEmojiConfigs; 18 | 19 | - (NSString*)getEmojiImageName:(NSString*)emojiCode; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GEmojiConfigManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // GEmojiConfigManager.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/6. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GEmojiConfigManager.h" 10 | 11 | @interface GEmojiConfigManager () 12 | @property (nonatomic, strong) NSDictionary * emojiDict; 13 | @end 14 | 15 | @implementation GEmojiConfigManager 16 | 17 | + (GEmojiConfigManager *)sharedInstance 18 | { 19 | static GEmojiConfigManager *INST = nil; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | INST = [[GEmojiConfigManager alloc] init]; 23 | }); 24 | return INST; 25 | } 26 | 27 | - (instancetype)init 28 | { 29 | self = [super init]; 30 | if (self) { 31 | _emojiDict = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"expression" ofType:@"plist"]]; 32 | } 33 | return self; 34 | } 35 | 36 | - (void)setEmojiConfigs:(NSDictionary*)configs 37 | { 38 | if (!configs) return; 39 | _emojiDict = configs; 40 | } 41 | 42 | - (NSDictionary*)getEmojiConfigs 43 | { 44 | return _emojiDict; 45 | } 46 | 47 | - (NSString*)getEmojiImageName:(NSString*)emojiCode 48 | { 49 | return [_emojiDict objectForKey:emojiCode]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GMenuContextProtocol.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // GMenuContextProtocol.h 4 | // GRichLabel 5 | // 6 | // Created by GIKI on 2017/11/7. 7 | // Copyright © 2017年 GIKI. All rights reserved. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | @class GRichLabel; 14 | @protocol GMenuContextProtocol 15 | 16 | @required 17 | - (void)ShowRichLabelTextMenuWithTargetRect:(CGRect)targetRect; 18 | - (void)HideRichLabelTextMenu; 19 | 20 | @optional 21 | - (void)ConfigMenuWithRichLabel:(GRichLabel*)richLabel; 22 | @end 23 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GRichLabel+TextMenuConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // GRichLabel+TextMenuConfiguration.h 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2018/5/29. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GRichLabel.h" 10 | #import "GMenuContextProtocol.h" 11 | 12 | @interface GRichLabelWeakReference : NSObject 13 | @property (nonatomic, weak) id weakObj; 14 | + (instancetype)weakReference:(id)weakObj; 15 | @end 16 | 17 | @interface GRichLabel (TextMenuConfiguration) 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GRichLabel+TextMenuConfiguration.m: -------------------------------------------------------------------------------- 1 | // 2 | // GRichLabel+TextMenuConfiguration.m 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2018/5/29. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GRichLabel+TextMenuConfiguration.h" 10 | 11 | @implementation GRichLabelWeakReference 12 | + (instancetype)weakReference:(id)weakObj 13 | { 14 | GRichLabelWeakReference * weak = [GRichLabelWeakReference new]; 15 | weak.weakObj = weakObj; 16 | return weak; 17 | } 18 | @end 19 | 20 | @implementation GRichLabel (TextMenuConfiguration) 21 | 22 | - (BOOL)canBecomeFirstResponder 23 | { 24 | return YES; 25 | } 26 | 27 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { 28 | /* 29 | ------------------------------------------------------ 30 | Default menu actions list: 31 | cut: Cut 32 | copy: Copy 33 | select: Select 34 | selectAll: Select All 35 | paste: Paste 36 | delete: Delete 37 | _promptForReplace: Replace... 38 | _transliterateChinese: 简⇄繁 39 | _showTextStyleOptions: 𝐁𝐼𝐔 40 | _define: Define 41 | _addShortcut: Add... 42 | _accessibilitySpeak: Speak 43 | _accessibilitySpeakLanguageSelection: Speak... 44 | _accessibilityPauseSpeaking: Pause Speak 45 | makeTextWritingDirectionRightToLeft: ⇋ 46 | makeTextWritingDirectionLeftToRight: ⇌ 47 | 48 | ------------------------------------------------------ 49 | Default attribute modifier list: 50 | toggleBoldface: 51 | toggleItalics: 52 | toggleUnderline: 53 | increaseSize: 54 | decreaseSize: 55 | */ 56 | 57 | if (self.getSelectRange.length > 0) { 58 | 59 | if (action == @selector(copy:)) { 60 | return YES; 61 | } 62 | if (action == @selector(selectAll:)) { 63 | 64 | return self.getSelectRange.length < self.textBuilder.attributedString.string.length; 65 | } 66 | 67 | NSString *selString = NSStringFromSelector(action); 68 | if ([selString hasSuffix:@"define:"] && [selString hasPrefix:@"_"]) { 69 | return [self getCurrentViewController] != nil; 70 | } 71 | } 72 | return NO; 73 | } 74 | 75 | - (void)ShowRichLabelTextMenuWithTargetRect:(CGRect)targetRect 76 | { 77 | if (!self.isFirstResponder) { 78 | [self becomeFirstResponder]; 79 | } 80 | UIMenuController *menuController = [UIMenuController sharedMenuController]; 81 | [menuController setTargetRect:targetRect inView:self]; 82 | [menuController update]; 83 | [menuController setMenuVisible:YES animated:YES]; 84 | } 85 | 86 | - (void)HideRichLabelTextMenu 87 | { 88 | UIMenuController *menu = [UIMenuController sharedMenuController]; 89 | [menu setMenuVisible:NO animated:YES]; 90 | } 91 | 92 | /** 93 | copy Method 94 | 95 | @param sender sender 96 | */ 97 | - (void)copy:(id)sender 98 | { 99 | UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; 100 | [pasteboard setString:[NSString stringWithFormat:@"%@",[self getSelectText]]]; 101 | [self resetSelection]; 102 | } 103 | 104 | /** 105 | select All Method 106 | 107 | @param sender sender 108 | */ 109 | - (void)selectAll:(id)sender 110 | { 111 | [self setSelectAllRange]; 112 | [self showSelectionView]; 113 | [self showTextMenu]; 114 | } 115 | 116 | - (void)_define:(id)sender 117 | { 118 | NSString *string = [self getSelectText]; 119 | if (string.length == 0) return; 120 | BOOL resign = [self resignFirstResponder]; 121 | if (!resign) return; 122 | [self resetSelection]; 123 | 124 | UIReferenceLibraryViewController* vc = [[UIReferenceLibraryViewController alloc] initWithTerm:string]; 125 | vc.view.backgroundColor = [UIColor whiteColor]; 126 | [[self getCurrentViewController] presentViewController:vc animated:YES completion:^{}]; 127 | } 128 | 129 | @end 130 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GTextMenuConfiguration.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTextMenuConfiguration.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/27. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "GMenuContextProtocol.h" 12 | @class GRichLabel; 13 | @interface GTextMenuConfiguration : NSObject 14 | 15 | @property (nonatomic, strong) NSArray * menuItems; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GTextMenuConfiguration.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTextMenuConfiguration.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/27. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GTextMenuConfiguration.h" 10 | #import "GRichLabel.h" 11 | #import "GMenuController.h" 12 | #import "NSMutableAttributedString+GRichLabel.h" 13 | #import "NSAttributedString+GRichLabel.h" 14 | #import "UIView+GText.h" 15 | 16 | @interface GTextMenuConfiguration () 17 | @property (nonatomic, weak) GRichLabel * richLabel; 18 | @property (nonatomic, strong) NSString * selectText; 19 | @property (nonatomic, strong) NSArray * innerItems; 20 | @end 21 | 22 | @implementation GTextMenuConfiguration 23 | 24 | - (instancetype)init 25 | { 26 | self = [super init]; 27 | if (self) { 28 | GMenuItem *copyItem = [[GMenuItem alloc] initWithTitle:@"拷贝" target:self action:@selector(copyItem:)]; 29 | GMenuItem *selectAllItem = [[GMenuItem alloc] initWithTitle:@"全选" target:self action:@selector(selectAllItem:)]; 30 | GMenuItem *shareItem = [[GMenuItem alloc] initWithTitle:@"共享" target:self action:@selector(shareItem:)]; 31 | 32 | NSArray *items = [NSArray arrayWithObjects:copyItem,selectAllItem,shareItem,nil]; 33 | self.menuItems = items; 34 | self.innerItems = items; 35 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resetSelection) name:GMenuControllerDidHideMenuNotification object:nil]; 36 | } 37 | return self; 38 | } 39 | 40 | - (void)ConfigMenuWithRichLabel:(GRichLabel*)richLabel 41 | { 42 | self.richLabel = richLabel; 43 | } 44 | 45 | - (void)ShowRichLabelTextMenuWithTargetRect:(CGRect)targetRect 46 | { 47 | if (self.menuItems.count == 0) return; 48 | 49 | GMenuController *menuController = [GMenuController sharedMenuController]; 50 | menuController.menuViewContainer.hasAutoHide = NO; 51 | NSArray *items = self.menuItems; 52 | [menuController setMenuItems:items]; 53 | 54 | [menuController setTargetRect:targetRect inView:self.richLabel]; 55 | [menuController setMenuVisible:YES animated:YES]; 56 | 57 | } 58 | 59 | - (void)resetSelection 60 | { 61 | if ([self.richLabel getSelectRange].length > 0) { 62 | [self.richLabel resetSelection]; 63 | } 64 | } 65 | 66 | - (void)HideRichLabelTextMenu 67 | { 68 | 69 | GMenuController *menu = [GMenuController sharedMenuController]; 70 | [menu setMenuVisible:NO animated:YES]; 71 | } 72 | 73 | /** 74 | copy Method 75 | 76 | @param sender sender 77 | */ 78 | - (void)copyItem:(id)sender 79 | { 80 | UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; 81 | [pasteboard setString:[NSString stringWithFormat:@"%@",[self.richLabel getSelectText]]]; 82 | 83 | [self.richLabel resetSelection]; 84 | } 85 | 86 | /** 87 | select All Method 88 | 89 | @param sender sender 90 | */ 91 | - (void)selectAllItem:(id)sender 92 | { 93 | [self.richLabel setSelectAllRange]; 94 | [self.richLabel showSelectionView]; 95 | [self showAfterSelectAllMenu]; 96 | } 97 | 98 | /** 99 | share Method 100 | 101 | @param sender sender 102 | */ 103 | - (void)shareItem:(id)sender 104 | { 105 | NSString *textToShare = [NSString stringWithFormat:@"%@",[self.richLabel getSelectText]]; 106 | 107 | NSArray *activityItems = @[textToShare]; 108 | 109 | UIActivityViewController *activityVC = [[UIActivityViewController alloc]initWithActivityItems:activityItems applicationActivities:nil]; 110 | __weak typeof(self) weakSelf = self; 111 | 112 | [[self.richLabel getCurrentViewController] presentViewController:activityVC animated:YES completion:^{ 113 | [weakSelf.richLabel resetSelection]; 114 | }]; 115 | 116 | } 117 | 118 | /** 119 | 显示全部选择后的menu 120 | */ 121 | - (void)showAfterSelectAllMenu 122 | { 123 | GMenuItem *copyItem = [[GMenuItem alloc] initWithTitle:@"拷贝" target:self action:@selector(copyItem:)]; 124 | GMenuItem *shareItem = [[GMenuItem alloc] initWithTitle:@"共享" target:self action:@selector(shareItem:)]; 125 | NSArray *items = [NSArray arrayWithObjects:copyItem,shareItem,nil]; 126 | self.menuItems = items; 127 | [self.richLabel showTextMenu]; 128 | self.menuItems = self.innerItems; 129 | } 130 | 131 | 132 | @end 133 | 134 | 135 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GTokenAppearance.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTokenAppearance.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/12. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface GTokenAppearance : NSObject 13 | 14 | @property (nonatomic, assign) CGFloat strokeWidth; 15 | @property (nonatomic, strong) UIColor *strokeColor; 16 | @property (nonatomic, assign) CGFloat cornerRadius; 17 | @property (nonatomic, strong) UIColor *fillColor; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/GTokenAppearance.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTokenAppearance.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/12. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GTokenAppearance.h" 10 | 11 | @implementation GTokenAppearance 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Common/NSParagraphStyle+GRichLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSParagraphStyle+GRichLabel.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/10/29. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | /** 11 | Provides extensions for `NSParagraphStyle` to work with CoreText. 12 | */ 13 | @interface NSParagraphStyle (GRichLabel) 14 | /** 15 | Creates a new NSParagraphStyle object from the CoreText Style. 16 | 17 | @param CTStyle CoreText Paragraph Style. 18 | 19 | @return a new NSParagraphStyle 20 | */ 21 | + (NSParagraphStyle *)g_styleWithCTStyle:(CTParagraphStyleRef)CTStyle; 22 | 23 | /** 24 | Creates and returns a CoreText Paragraph Style. (need call CFRelease() after used) 25 | */ 26 | - (CTParagraphStyleRef)g_CTStyle CF_RETURNS_RETAINED; 27 | @end 28 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/GRichLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // GRichLabel.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/8/30. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GAttributedConfiguration.h" 11 | #import "GDrawTextBuilder.h" 12 | #import "NSAttributedString+GRichLabel.h" 13 | #import "GMenuContextProtocol.h" 14 | 15 | /** 16 | 文本选择 17 | richLabel在scrollview等具有滚动功能的视图上。开启可以滚动选择文字。 18 | - GRichLabelScrollerAutoSelectNone: 不开启滚动选择 19 | - GRichLabelScrollerAutoSelectLine: 开启滚动选择,默认向下滚动一行 20 | - GRichLabelScrollerAutoSelectAll: 开启滚动选择,默认滚动到控件顶部或者底部 21 | */ 22 | typedef NS_ENUM(NSUInteger, GRichLabelScrollerAutoSelect) { 23 | GRichLabelScrollerAutoSelectNone, 24 | GRichLabelScrollerAutoSelectLine, 25 | GRichLabelScrollerAutoSelectAll, 26 | }; 27 | @interface GRichLabel : UIView 28 | 29 | @property (nonatomic, strong) NSString * text; 30 | 31 | @property (nonatomic, strong) NSAttributedString *attributedString; 32 | 33 | /// A quick attribute configuration class 34 | /// Don't need to deal with complex NSAttributedString 35 | @property (nonatomic, strong) GAttributedConfiguration * attributedConfig; 36 | 37 | ///GDrawTextBuilder class is a readonly class stores text layout result. 38 | @property (nonatomic, strong) GDrawTextBuilder *textBuilder; 39 | 40 | @property (nonatomic, strong) id menuConfiguration; 41 | 42 | /** 43 | 是否开启异步绘制 44 | 45 | @brief: 内部使用YYAsyncLayer提供异步绘制任务 46 | @default: NO 47 | */ 48 | @property (nonatomic, assign) BOOL displaysAsynchronously; 49 | 50 | /** 51 | 是否开启文本选择操作 52 | 53 | @default: NO 54 | */ 55 | @property (nonatomic, assign) BOOL canSelect; 56 | 57 | /** 58 | 最小的选择范围 59 | 60 | @brief: 开启canSelect 生效 61 | @default: 1 如果想要微信聊天那种效果,可设置为MAXFLOAT,会自动选择全部文本 62 | */ 63 | @property (nonatomic, assign) CGFloat minSelectRange; 64 | 65 | /** 66 | 是否开启固定行高 67 | default:NO 68 | */ 69 | @property (nonatomic, assign) BOOL isFixedLineHeight; 70 | 71 | /** 72 | richlabel的容器view 73 | 如果开启canSelect,并且richLabel在scrollview等具有滚动功能的view上. 74 | 滚动会和select文字选择冲突. 75 | 需要把scrollview 传入来处理滚动事件 76 | */ 77 | @property (nonatomic, weak ) __kindof UIScrollView *contentScrollView; 78 | 79 | /** 80 | 是否可以滚动选择 81 | richLabel在scrollview等具有滚动功能的视图上。开启可以滚动选择文字。 82 | default:GRichLabelScrollerAutoSelectNone 83 | */ 84 | @property (nonatomic, assign) GRichLabelScrollerAutoSelect scrollerAutoSelectType; 85 | 86 | 87 | /** 88 | 选择框颜色 89 | @default: 219,226,250 90 | */ 91 | @property (nonatomic, strong) UIColor * selectionColor; 92 | 93 | /** 94 | 光标颜色 95 | @default: blueColor 96 | */ 97 | @property (nonatomic, strong) UIColor * cursorColor; 98 | 99 | /** 100 | rich label 最大行数 101 | 102 | @Default value is 1. 103 | 0 means no limit. 104 | */ 105 | //@property (nonatomic, assign) NSUInteger numberOfLines; //// 暂未实现 106 | 107 | #pragma mark - public Method 108 | 109 | /** 110 | 选中全部文本区域 111 | */ 112 | - (void)setSelectAllRange; 113 | 114 | /** 115 | 重置文本选择区域 116 | */ 117 | - (void)resetSelection; 118 | 119 | /** 120 | 展示SelectionView 121 | */ 122 | - (void)showSelectionView; 123 | 124 | /** 125 | 展示menu 126 | */ 127 | - (void)showTextMenu; 128 | 129 | /** 130 | 隐藏Menu 131 | */ 132 | - (void)hideTextMenu; 133 | 134 | /** 135 | 获取当前选中文本 136 | 137 | @return NSString 138 | */ 139 | - (NSString*)getSelectText; 140 | 141 | /** 142 | 获取当前选中范围 143 | 144 | @return NSRange 145 | */ 146 | - (NSRange)getSelectRange; 147 | 148 | /** 149 | 获取当前richLabel所在的控制器 150 | 151 | @return UIViewController 152 | */ 153 | - (UIViewController*)getCurrentViewController; 154 | @end 155 | 156 | UIKIT_EXTERN NSNotificationName const GRichLabelWillSelectNotification; 157 | UIKIT_EXTERN NSNotificationName const GRichLabelDidSelectNotification; 158 | UIKIT_EXTERN NSNotificationName const GRichLabelDidCancelSelectNotification; 159 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GCursor.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCursor.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/8/29. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | typedef NS_ENUM(NSUInteger, GCursorDirection) { 13 | GCursorDirectionLeft, 14 | GCursorDirectionRight, 15 | }; 16 | 17 | @interface GCursor : UIView 18 | 19 | @property (nonatomic,assign) GCursorDirection direction; 20 | @property (nonatomic,assign) CGFloat cursorHeight; 21 | @property (nonatomic,strong) UIColor *color; 22 | 23 | + (instancetype)cursorWithDirection:(GCursorDirection)direction; 24 | 25 | - (CGRect)enlargeTouchRect; 26 | - (void)showPoint:(CGPoint)point; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GCursor.m: -------------------------------------------------------------------------------- 1 | // 2 | // GCursor.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/8/29. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GCursor.h" 10 | 11 | #define kCursorW 2 12 | #define kGestureW 22 13 | #define kDotGestureW 16 14 | 15 | @interface GCursor() 16 | { 17 | UIImageView *_dotView; 18 | } 19 | 20 | @end 21 | 22 | @implementation GCursor 23 | 24 | + (instancetype)cursorWithDirection:(GCursorDirection)direction 25 | { 26 | GCursor *cursor = [[GCursor alloc] init]; 27 | cursor.direction = direction; 28 | return cursor; 29 | } 30 | 31 | - (instancetype)initWithFrame:(CGRect)frame 32 | { 33 | self = [super initWithFrame:frame]; 34 | if (self) { 35 | 36 | _dotView = [[UIImageView alloc] init];//WithImage:[UIImage imageNamed:@"r_drag-dot.png" 37 | 38 | CGRect dotRect = _dotView.frame; 39 | dotRect.size = CGSizeMake(12,12); 40 | _dotView.frame = dotRect; 41 | _dotView.layer.cornerRadius = 6; 42 | _dotView.layer.masksToBounds = YES; 43 | self.color = [UIColor blueColor]; 44 | self.clipsToBounds = NO; 45 | self.cursorHeight = 26; 46 | } 47 | return self; 48 | } 49 | 50 | - (void)setColor:(UIColor *)color 51 | { 52 | _color = color; 53 | self.backgroundColor = color; 54 | _dotView.backgroundColor = color; 55 | } 56 | 57 | - (void)setDirection:(GCursorDirection)direction 58 | { 59 | _direction = direction; 60 | } 61 | 62 | - (void)showPoint:(CGPoint)point 63 | { 64 | if (![self.subviews containsObject:_dotView]) { 65 | [self addSubview:_dotView]; 66 | } 67 | 68 | CGRect dotRect = _dotView.frame; 69 | 70 | if (_direction == GCursorDirectionRight) { 71 | self.frame = CGRectMake(point.x, point.y - _cursorHeight, kCursorW, _cursorHeight); 72 | dotRect.origin.x = -(dotRect.size.width - kCursorW)/2; 73 | dotRect.origin.y = _cursorHeight - dotRect.size.height*0.3 ; 74 | } else if (_direction == GCursorDirectionLeft) { 75 | self.frame = CGRectMake(point.x - kCursorW, point.y - _cursorHeight, kCursorW, _cursorHeight); 76 | dotRect.origin.x = -(dotRect.size.width - kCursorW)/2; 77 | dotRect.origin.y = - dotRect.size.height*0.7; 78 | } else { 79 | [_dotView removeFromSuperview]; 80 | } 81 | _dotView.frame = dotRect; 82 | } 83 | 84 | - (CGRect)enlargeTouchRect 85 | { 86 | CGRect rect = CGRectInset(self.frame, -kGestureW, -kGestureW); 87 | UIEdgeInsets insets = {0}; 88 | if (_direction == GCursorDirectionRight) { 89 | insets.right = -kDotGestureW; 90 | } else if (_direction == GCursorDirectionLeft) { 91 | insets.left = -kDotGestureW; 92 | } 93 | rect = UIEdgeInsetsInsetRect(rect, insets); 94 | return rect; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GEmojiRunDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // GEmojiRunDelegate.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/6. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface GEmojiRunDelegate : NSObject 14 | 15 | @property(nonatomic,strong) UIFont *textFont; 16 | @property(nonatomic,strong) NSString *emojiImageName; 17 | 18 | @property (nonatomic, assign,readonly) CGRect bounds; 19 | 20 | - (CTRunDelegateRef)GetCTRunDelegate; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GEmojiRunDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // GEmojiRunDelegate.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/6. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GEmojiRunDelegate.h" 10 | #import 11 | #import "GTextUtils.h" 12 | static void DeallocCallback(void *ref) { 13 | GEmojiRunDelegate *delegate = (__bridge_transfer GEmojiRunDelegate *)(ref); 14 | delegate = nil; 15 | } 16 | 17 | static CGFloat AscentCallback(void *ref) { 18 | GEmojiRunDelegate *delegate = (__bridge GEmojiRunDelegate *)(ref); 19 | return GTextEmojiGetAscentWithFontSize(delegate.textFont.pointSize);; 20 | } 21 | 22 | static CGFloat DecentCallback(void *ref) { 23 | GEmojiRunDelegate *delegate = (__bridge GEmojiRunDelegate *)(ref); 24 | return GTextEmojiGetDescentWithFontSize(delegate.textFont.pointSize); 25 | } 26 | 27 | static CGFloat WidthCallback(void *ref) { 28 | GEmojiRunDelegate *delegate = (__bridge GEmojiRunDelegate *)(ref); 29 | CGRect bound = delegate.bounds; 30 | return bound.size.width + 2 * bound.origin.x; 31 | } 32 | 33 | @interface GEmojiRunDelegate() 34 | @property (nonatomic, assign,readwrite) CGRect bounds; 35 | @end 36 | 37 | @implementation GEmojiRunDelegate 38 | 39 | - (CTRunDelegateRef)GetCTRunDelegate CF_RETURNS_RETAINED { 40 | CTRunDelegateCallbacks callbacks; 41 | callbacks.version = kCTRunDelegateCurrentVersion; 42 | callbacks.dealloc = DeallocCallback; 43 | callbacks.getAscent = AscentCallback; 44 | callbacks.getDescent = DecentCallback; 45 | callbacks.getWidth = WidthCallback; 46 | return CTRunDelegateCreate(&callbacks, (__bridge_retained void *)(self.copy)); 47 | } 48 | 49 | - (instancetype)init 50 | { 51 | self = [super init]; 52 | if (self) { 53 | 54 | } 55 | return self; 56 | } 57 | 58 | - (void)setTextFont:(UIFont *)textFont 59 | { 60 | _textFont = textFont; 61 | self.bounds = GTextEmojiGetGlyphBoundingRectWithFontSize(textFont.pointSize); 62 | } 63 | 64 | - (id)copyWithZone:(NSZone *)zone { 65 | 66 | typeof(self) one = [self.class new]; 67 | one.emojiImageName = self.emojiImageName; 68 | one.textFont = self.textFont; 69 | return one; 70 | } 71 | @end 72 | 73 | 74 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GLineLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // GLineLayout.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/6. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface GLineLayout : NSObject 13 | 14 | @property (nonatomic, assign) CGRect rect; 15 | @property (nonatomic, strong) id line; 16 | @property (nonatomic, assign) CGFloat linespace; 17 | @property (nonatomic, assign) CGFloat linePonintY; 18 | + (instancetype)line:(id)line Layout:(CGRect)rect; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GLineLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // GLineLayout.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/6. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GLineLayout.h" 10 | 11 | @implementation GLineLayout 12 | 13 | + (instancetype)line:(id)line Layout:(CGRect)rect 14 | { 15 | GLineLayout *layout = [GLineLayout new]; 16 | layout.line = line; 17 | layout.rect = rect; 18 | return layout; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GMagnifiter.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMagnifiter.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/8/29. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, GTextMagnifierType) { 12 | GTextMagnifierTypeCaret, 13 | GTextMagnifierTypeRanged, 14 | }; 15 | 16 | @interface GMagnifiter : UIView 17 | 18 | + (id)magnifierWithType:(GTextMagnifierType)type; 19 | 20 | @property (nonatomic, weak) UIView *hostView; 21 | @property (nonatomic, readonly) GTextMagnifierType type; 22 | @property (nonatomic, readonly) CGSize fitSize; 23 | @property (nonatomic, readonly) CGSize snapshotSize; 24 | @property (nonatomic, strong) UIImage *snapshot; 25 | @property (nonatomic, assign) CGPoint hostCaptureCenter; 26 | @property (nonatomic, assign) CGPoint hostPopoverCenter; 27 | 28 | @property (nonatomic) BOOL captureDisabled; 29 | @property (nonatomic) BOOL captureFadeAnimation; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GMagnifiterWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMagnifiterWindow.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/2. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define GMagnifiterINST [GMagnifiterWindow sharedWindow] 12 | 13 | @class GMagnifiter; 14 | @interface GMagnifiterWindow : UIWindow 15 | 16 | + (instancetype)sharedWindow; 17 | 18 | - (void)showMagnifier:(GMagnifiter *)magnifiter; 19 | 20 | - (void)moveMagnifier:(GMagnifiter *)magnifiter; 21 | 22 | - (void)hideMagnifier:(GMagnifiter *)magnifiter; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/GSelectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GSelectionView.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/1. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GCursor.h" 11 | #import "GTokenAppearance.h" 12 | 13 | @interface GSelectionView : UIView 14 | 15 | @property (nonatomic, strong) GCursor * leftCursor; 16 | @property (nonatomic, strong) GCursor * rightCursor; 17 | 18 | @property (nonatomic, copy) NSArray *selectionRects; 19 | @property (nonatomic, copy) NSArray *highlightRects; 20 | 21 | @property (nonatomic, strong) UIColor * selectionColor; 22 | @property (nonatomic, strong) UIColor * highlightColor; 23 | 24 | @property (nonatomic, assign) CGFloat linespace; 25 | @property (nonatomic, assign) CGRect pathRect; 26 | 27 | - (void)showSelectionView:(NSArray*)selectionRects showCursor:(BOOL)isShowCursor; 28 | 29 | - (void)hideSelectionView; 30 | 31 | - (void)showHighlightViewWithRects:(NSArray*)hightLightRects withAppearance:(GTokenAppearance*)appearance; 32 | 33 | - (void)hideHighlightView; 34 | 35 | - (BOOL)isCursorContainsPoint:(CGPoint)point; 36 | - (BOOL)isLeftCursorContainsPoint:(CGPoint)point; 37 | - (BOOL)isRightCursorContainsPoint:(CGPoint)point; 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/UIView+GText.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+GText.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/2. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (GText) 12 | 13 | - (CGPoint)convertPoint:(CGPoint)point fromViewOrWindow:(UIView *)view; 14 | - (CGPoint)convertPoint:(CGPoint)point toViewOrWindow:(UIView *)view ; 15 | @end 16 | -------------------------------------------------------------------------------- /GRichLabel/RichLabel/Internal/UIView+GText.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+GText.m 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/9/2. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "UIView+GText.h" 10 | 11 | @implementation UIView (GText) 12 | 13 | - (CGPoint)convertPoint:(CGPoint)point fromViewOrWindow:(UIView *)view 14 | { 15 | if (!view) { 16 | if ([self isKindOfClass:[UIWindow class]]) { 17 | return [((UIWindow *)self) convertPoint:point fromWindow:nil]; 18 | } else { 19 | return [self convertPoint:point fromView:nil]; 20 | } 21 | } 22 | 23 | UIWindow *from = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window; 24 | UIWindow *to = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window; 25 | if ((!from || !to) || (from == to)) return [self convertPoint:point fromView:view]; 26 | point = [from convertPoint:point fromView:view]; 27 | point = [to convertPoint:point fromWindow:from]; 28 | point = [self convertPoint:point fromView:to]; 29 | return point; 30 | } 31 | 32 | - (CGPoint)convertPoint:(CGPoint)point toViewOrWindow:(UIView *)view 33 | { 34 | if (!view) { 35 | if ([self isKindOfClass:[UIWindow class]]) { 36 | return [((UIWindow *)self) convertPoint:point toWindow:nil]; 37 | } else { 38 | return [self convertPoint:point toView:nil]; 39 | } 40 | } 41 | 42 | UIWindow *from = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window; 43 | UIWindow *to = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window; 44 | if ((!from || !to) || (from == to)) return [self convertPoint:point toView:view]; 45 | point = [self convertPoint:point toView:from]; 46 | point = [to convertPoint:point fromWindow:from]; 47 | point = [view convertPoint:point fromView:to]; 48 | return point; 49 | } 50 | @end 51 | -------------------------------------------------------------------------------- /GRichLabel/Throttle/GThrottleInvoke.h: -------------------------------------------------------------------------------- 1 | // 2 | // GThrottleInvoke.h 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2018/4/26. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GThrottleInvoke : NSObject 12 | 13 | + (void)throttle:(NSTimeInterval)threshold block:(dispatch_block_t)block; 14 | + (void)throttle:(NSTimeInterval)threshold queue:(dispatch_queue_t)queue block:(dispatch_block_t)block; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /GRichLabel/Throttle/GThrottleInvoke.m: -------------------------------------------------------------------------------- 1 | // 2 | // GThrottleInvoke.m 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2018/4/26. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GThrottleInvoke.h" 10 | #define ThreadCallStackSymbol [NSThread callStackSymbols][1] 11 | @implementation GThrottleInvoke 12 | 13 | + (NSMutableDictionary *)scheduledSources 14 | { 15 | static NSMutableDictionary *_sources = nil; 16 | static dispatch_once_t token; 17 | dispatch_once(&token, ^{ 18 | _sources = [NSMutableDictionary dictionary]; 19 | }); 20 | return _sources; 21 | } 22 | 23 | + (void)throttle:(NSTimeInterval)threshold block:(dispatch_block_t)block 24 | { 25 | [self _throttle:threshold queue:dispatch_get_main_queue() key:ThreadCallStackSymbol block:block]; 26 | } 27 | 28 | + (void)throttle:(NSTimeInterval)threshold queue:(dispatch_queue_t)queue block:(dispatch_block_t)block 29 | { 30 | [self _throttle:threshold queue:queue key:ThreadCallStackSymbol block:block]; 31 | } 32 | 33 | + (void)_throttle:(NSTimeInterval)threshold queue:(dispatch_queue_t)queue key:(NSString *)key block:(dispatch_block_t)block 34 | { 35 | NSMutableDictionary *scheduledSources = self.scheduledSources; 36 | 37 | dispatch_source_t source = scheduledSources[key]; 38 | 39 | if (source) { 40 | dispatch_source_cancel(source); 41 | } 42 | 43 | source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 44 | dispatch_source_set_timer(source, dispatch_time(DISPATCH_TIME_NOW, threshold * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0); 45 | dispatch_source_set_event_handler(source, ^{ 46 | block(); 47 | dispatch_source_cancel(source); 48 | [scheduledSources removeObjectForKey:key]; 49 | }); 50 | dispatch_resume(source); 51 | 52 | scheduledSources[key] = source; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /GRichLabelExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GRichLabelExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /GRichLabelExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /GRichLabelExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2017/9/7. 6 | // Copyright © 2017年 GIKI. 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 | -------------------------------------------------------------------------------- /GRichLabelExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2017/9/7. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /GRichLabelExample/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 | } -------------------------------------------------------------------------------- /GRichLabelExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /GRichLabelExample/Assets.xcassets/novelbg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "novelbg@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /GRichLabelExample/Assets.xcassets/novelbg.imageset/novelbg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/GRichLabelExample/Assets.xcassets/novelbg.imageset/novelbg@2x.png -------------------------------------------------------------------------------- /GRichLabelExample/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 | -------------------------------------------------------------------------------- /GRichLabelExample/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 | 48 | 56 | 64 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /GRichLabelExample/ChatTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTableViewCell.h 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2018/5/28. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GRichLabel.h" 11 | 12 | @interface ChatTableViewCell : UITableViewCell 13 | - (void)configTextBuilder:(GDrawTextBuilder*)textBuilder; 14 | @property (nonatomic, weak) UITableView *weakTableView; 15 | @end 16 | -------------------------------------------------------------------------------- /GRichLabelExample/ChatTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTableViewCell.m 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2018/5/28. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "ChatTableViewCell.h" 10 | 11 | #define GColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] 12 | 13 | #define GColorRGBA(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:a] 14 | // 随机色 15 | #define GRandomColor GColor(arc4random_uniform(256), arc4random_uniform(256), arc4random_uniform(256)) 16 | @interface ChatTableViewCell() 17 | @property (nonatomic, strong) UIImageView * avatarView; 18 | @property (nonatomic, strong) UILabel * nameLabel; 19 | @property (nonatomic, strong) UIView * cardView; 20 | @property (nonatomic, strong) GRichLabel * richLabel; 21 | @property (nonatomic, strong) GDrawTextBuilder * textBuilder; 22 | @end 23 | @implementation ChatTableViewCell 24 | 25 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 26 | { 27 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 28 | if (self) { 29 | 30 | self.selectionStyle = UITableViewCellSelectionStyleNone; 31 | [self.contentView addSubview:({ 32 | _avatarView = [[UIImageView alloc] init]; 33 | _avatarView.layer.cornerRadius = 18; 34 | _avatarView.layer.masksToBounds = YES; 35 | _avatarView.backgroundColor = GRandomColor; 36 | _avatarView; 37 | })]; 38 | [self.contentView addSubview:({ 39 | _nameLabel = [UILabel new]; 40 | _nameLabel.text = @"GIKI"; 41 | _nameLabel.font = [UIFont systemFontOfSize:16]; 42 | _nameLabel.textColor = [UIColor blackColor]; 43 | _nameLabel; 44 | })]; 45 | [self.contentView addSubview:({ 46 | _cardView = [UIView new]; 47 | _cardView.backgroundColor =GColor(175,228,110); 48 | _cardView.layer.cornerRadius = 4; 49 | _cardView; 50 | })]; 51 | [self.cardView addSubview:({ 52 | _richLabel = [[GRichLabel alloc] init]; 53 | _richLabel.canSelect = YES; 54 | _richLabel.displaysAsynchronously = YES; 55 | _richLabel.cursorColor = GColor(70, 142, 47); 56 | _richLabel.selectionColor = GColorRGBA(34, 139, 34,1); 57 | _richLabel; 58 | })]; 59 | } 60 | return self; 61 | } 62 | 63 | - (void)layoutSubviews 64 | { 65 | [super layoutSubviews]; 66 | self.avatarView.frame = CGRectMake(12, 12, 36, 36); 67 | self.nameLabel.frame = CGRectMake(CGRectGetMaxX(self.avatarView.frame)+16, 12+18-11, 200, 22); 68 | self.cardView.frame = CGRectMake(12+36, CGRectGetMaxY(self.nameLabel.frame) + 12, self.textBuilder.boundSize.width+16, self.textBuilder.boundSize.height+16); 69 | self.richLabel.frame = CGRectMake(8, 8, self.textBuilder.boundSize.width, self.textBuilder.boundSize.height); 70 | 71 | } 72 | 73 | - (void)configTextBuilder:(GDrawTextBuilder*)textBuilder 74 | { 75 | [self.richLabel resetSelection]; 76 | self.avatarView.backgroundColor = GRandomColor; 77 | self.textBuilder = textBuilder; 78 | self.richLabel.textBuilder = textBuilder; 79 | } 80 | 81 | - (void)setWeakTableView:(UITableView *)weakTableView 82 | { 83 | self.richLabel.contentScrollView = weakTableView; 84 | self.richLabel.scrollerAutoSelectType = GRichLabelScrollerAutoSelectAll; 85 | self.richLabel.minSelectRange = CGFLOAT_MAX; 86 | } 87 | 88 | + (CGFloat)getTextHeight:(GDrawTextBuilder*)textBuilder 89 | { 90 | return 0; 91 | } 92 | @end 93 | -------------------------------------------------------------------------------- /GRichLabelExample/ChatViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChatViewController.h 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2018/5/28. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @interface ChatViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /GRichLabelExample/ChatViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChatViewController.m 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2018/5/28. 6 | // Copyright © 2018年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "ChatViewController.h" 10 | #import "ChatTableViewCell.h" 11 | 12 | @interface ChatViewController() 13 | @property (nonatomic, strong) UITableView * tableView; 14 | @property (nonatomic, strong) NSArray * texts; 15 | @property (nonatomic, strong) NSArray * datas; 16 | @end 17 | 18 | @implementation ChatViewController 19 | 20 | - (void)viewDidLoad 21 | { 22 | [self loadData]; 23 | [self loadUI]; 24 | } 25 | 26 | - (void)loadUI 27 | { 28 | self.view.backgroundColor = [UIColor whiteColor]; 29 | self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; 30 | self.tableView.delegate = self; 31 | self.tableView.dataSource = self; 32 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 33 | 34 | [self.view addSubview:self.tableView]; 35 | } 36 | 37 | - (void)viewDidLayoutSubviews 38 | { 39 | [super viewDidLayoutSubviews]; 40 | self.tableView.frame = CGRectMake(0, CGRectGetMaxY(self.navigationController.navigationBar.frame), self.view.bounds.size.width, self.view.bounds.size.height - CGRectGetMaxY(self.navigationController.navigationBar.frame)); 41 | } 42 | 43 | #pragma mark -- TableView DataSource 44 | 45 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 46 | { 47 | return 1; 48 | } 49 | 50 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 51 | { 52 | return self.datas.count; 53 | } 54 | 55 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 56 | { 57 | ChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"tableViewCell"]; 58 | if (!cell) { 59 | cell = [[ChatTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"tableViewCell"]; 60 | } 61 | GDrawTextBuilder * builder = self.datas[indexPath.row]; 62 | cell.weakTableView = self.tableView; 63 | [cell configTextBuilder:builder]; 64 | return cell; 65 | } 66 | 67 | 68 | #pragma mark -- TableView Delegate 69 | 70 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 71 | { 72 | 73 | } 74 | 75 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 76 | { 77 | GDrawTextBuilder * builder = self.datas[indexPath.row]; 78 | return builder.boundSize.height+80; 79 | } 80 | 81 | #pragma mark - loadDatas 82 | 83 | - (void)loadData 84 | { 85 | __block NSMutableArray * arrayM = [NSMutableArray array]; 86 | [self.texts enumerateObjectsUsingBlock:^(NSString* obj, NSUInteger idx, BOOL * _Nonnull stop) { 87 | GAttributedConfiguration * configuraltion = [GAttributedConfiguration attributedConfig:obj color:[UIColor blackColor] font:[UIFont systemFontOfSize:18]]; 88 | NSAttributedString * attribute = [GAttributedStringFactory createAttributedStringWithAttributedConfig:configuraltion]; 89 | CGSize size = [GAttributedStringFactory getRichLabelDrawSizeWithAttributedString:attribute MaxContianerWidth:self.view.frame.size.width-12-36-12-36]; 90 | GDrawTextBuilder * builder = [GAttributedStringFactory createDrawTextBuilderWithAttributedConfig:configuraltion boundSize:size]; 91 | [arrayM addObject:builder]; 92 | }]; 93 | self.datas = arrayM.copy; 94 | NSMutableArray * arrayNew = [NSMutableArray array]; 95 | for (int i =0 ; i<20; i++) { 96 | [arrayNew addObjectsFromArray:arrayM.copy]; 97 | } 98 | self.datas = arrayNew.copy; 99 | } 100 | 101 | - (NSArray *)texts 102 | { 103 | if (!_texts) { 104 | _texts =@[@"生态环境风险是未来可能发生的环境问题及其影响后果。生态环境安全是国家安全的重要组成部分,是经济社会持续健康发展的重要保障。生态环境特别是大气、水、土壤污染严重", 105 | @"我国自然生态环境先天不足,整体生态环境系统脆弱。同时,在长期的发展过程中,我们也积累了大量生态环境问题,各类环境污染呈高发态势,逐渐累积的生态风险不容忽视。一方面,资源短缺。", 106 | @"《2016中国环境状况公报》", 107 | @"😀😄😂😌🤩☹️😳😡🤬🇦🇪🏸🏐🚒🚄🛫✈️🚉🚂🚝☮️✝️☪️⚂♞♤☆", 108 | @"😀😄😂😌土壤污染潜在风险不断累😀😄😂😌积。部分地区土壤污染较重,耕地土壤环境质量堪忧,工矿业废弃地土壤环境问题突出。", 109 | @"生态环境风险防范是一个系统和完整的体系。生态环境风险具有高度的多样性和复杂性。其表现形式十分复杂:大气污染、水污染、土壤污染、水土流失、自然灾害、荒漠化、生态系统退化、海洋环境问题、新型污染物、农村环境问题、气候变化、环境污染事故、环境社会性群体事件等都有关系。同时,造成这些问题的来源也十分复杂:工农业生产、资源开发、城乡居民生活、物流交换、国内外贸易等都相关。这些活动所涉及的主体也非常多:各级决策者、生产企业、社会大众、资源开发者等都涉及。因此,生态环境风险防范必将是一个系统和完整的体系。《中共中央关于全面深化改革若干重大问题的决定》提出:建设生态文明,必须建立系统完整的生态文明制度体系,实行最严格的源头保护制度、损害赔偿制度、责任追究制度,完善环境治理和生态修复制度,用制度保护生态环境。习近平指出:“要把生态环境风险纳入常态化管理,系统构建全过程、多层级生态环境风险防范体系。", 110 | @"构建多元共治的治理体系。从中央到地方,从主管部门到民间🚂🚝环保组织,从企业到个人,生态环境治理不🏐🚒🚄🛫仅仅是各级党政领导干部工作中最重要的组成部分,更应深入到每个人日常生活的点点滴滴中。政府应当让🏐🚒🚄🛫企业和公众广泛参与进来,强化企业防治污染的主体责任,让其主动承担防治责任;调动群众的积极性,保障他们的话语权,群策群力,共治共享,形成环境共治模式。\n  扭转环境恶化、提高环境质量是广大人民群众的热切期盼。要系统构建全过程、多层级生态环境风险防范体系,解决生态问题,规避环境风险,保证美丽中国的愿景充分实现。🏸✈️🚉", 111 | @" 过去几天,在杭州西北部郊区的很多银行出现了这样的场景:有人晕倒,有人打架,办理业务的长队排出1公里……\n他们都是去冻结银行存款、登记摇号买房。而其中大部分人的目标是华夏四季和融信澜天这两个楼盘。"]; 112 | } 113 | return _texts; 114 | } 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /GRichLabelExample/DemoTextMenuConfig.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoTextMenuConfig.h 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2017/11/7. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GMenuContextProtocol.h" 11 | 12 | @interface DemoTextMenuConfig : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /GRichLabelExample/DemoTextMenuConfig.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoTextMenuConfig.m 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2017/11/7. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "DemoTextMenuConfig.h" 10 | #import "GRichLabel.h" 11 | #import "GMenuController.h" 12 | #import "GBigbang.h" 13 | #import "NSMutableAttributedString+GRichLabel.h" 14 | #import "NSAttributedString+GRichLabel.h" 15 | 16 | 17 | @interface DemoTextMenuConfig () 18 | @property (nonatomic, weak) GRichLabel * richLabel; 19 | @property (nonatomic, strong) NSString * selectText; 20 | @property (nonatomic, strong) GTagFlowContainer *container; 21 | @property (nonatomic, strong) NSArray * menuItems; 22 | @property (nonatomic, strong) NSArray * oriMenuItems; 23 | @end 24 | 25 | @implementation DemoTextMenuConfig 26 | 27 | - (instancetype)init 28 | { 29 | self = [super init]; 30 | if (self) { 31 | 32 | GMenuItem *copyItem = [[GMenuItem alloc] initWithTitle:@"拷贝" target:self action:@selector(copyItem:)]; 33 | GMenuItem *selectAllItem = [[GMenuItem alloc] initWithTitle:@"全选" target:self action:@selector(selectAllItem:)]; 34 | GMenuItem *shareItem = [[GMenuItem alloc] initWithTitle:@"共享" target:self action:@selector(shareItem:)]; 35 | GMenuItem *bigbangItem = [[GMenuItem alloc] initWithTitle:@"bigbang" target:self action:@selector(bigbang:)]; 36 | GMenuItem *underlineItem = [[GMenuItem alloc] initWithTitle:@"划线" target:self action:@selector(underline:)]; 37 | GMenuItem *underlineItem1 = [[GMenuItem alloc] initWithTitle:@"划线2" target:self action:@selector(underline:)]; 38 | GMenuItem *underlineItem2 = [[GMenuItem alloc] initWithTitle:@"划线3" target:self action:@selector(underline:)]; 39 | // 40 | NSArray *items = [NSArray arrayWithObjects:copyItem,selectAllItem,shareItem,bigbangItem,underlineItem,underlineItem1,underlineItem2,nil]; 41 | self.menuItems = items; 42 | self.oriMenuItems = items; 43 | GTagFlowContainer *container = [GTagFlowContainer new]; 44 | 45 | self.container = container; 46 | 47 | self.container.actionBlock = ^(NSString *actionTitle, NSString *newText) { 48 | 49 | }; 50 | 51 | } 52 | return self; 53 | } 54 | 55 | 56 | - (void)ConfigMenuWithRichLabel:(GRichLabel*)richLabel 57 | { 58 | self.richLabel = richLabel; 59 | } 60 | 61 | - (void)ShowRichLabelTextMenuWithTargetRect:(CGRect)targetRect 62 | { 63 | if (self.menuItems.count == 0) return; 64 | 65 | GMenuController *menuController = [GMenuController sharedMenuController]; 66 | menuController.menuViewContainer.hasAutoHide = NO; 67 | NSArray *items = self.menuItems; 68 | [menuController setMenuItems:items]; 69 | [menuController setTargetRect:targetRect inView:self.richLabel]; 70 | [menuController setMenuVisible:YES animated:YES]; 71 | 72 | } 73 | 74 | - (void)HideRichLabelTextMenu 75 | { 76 | GMenuController *menu = [GMenuController sharedMenuController]; 77 | [menu setMenuVisible:NO animated:YES]; 78 | 79 | } 80 | 81 | /** 82 | copy Method 83 | 84 | @param sender sender 85 | */ 86 | - (void)copyItem:(id)sender 87 | { 88 | UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; 89 | [pasteboard setString:[NSString stringWithFormat:@"%@",[self.richLabel getSelectText]]]; 90 | 91 | [self.richLabel resetSelection]; 92 | } 93 | 94 | /** 95 | select All Method 96 | 97 | @param sender sender 98 | */ 99 | - (void)selectAllItem:(id)sender 100 | { 101 | [self.richLabel setSelectAllRange]; 102 | [self.richLabel showSelectionView]; 103 | [self showAfterSelectAllMenu]; 104 | } 105 | 106 | - (void)underline:(id)sender 107 | { 108 | NSRange selectRange = [self.richLabel getSelectRange]; 109 | [self.richLabel resetSelection]; 110 | 111 | NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithAttributedString:self.richLabel.attributedString]; 112 | [string g_setUnderlineStyle:NSUnderlineStyleSingle range:selectRange]; 113 | [string g_setUnderlineColor:[UIColor redColor] range:selectRange]; 114 | GDrawTextBuilder *buider = [GDrawTextBuilder buildDrawTextSize:self.richLabel.textBuilder.boundSize insert:self.richLabel.textBuilder.edgeInsets attributedString:string.copy]; 115 | self.richLabel.textBuilder = buider; 116 | 117 | } 118 | 119 | - (void)bigbang:(id)sender 120 | { 121 | NSArray * array = [GBigbangBox bigBang:[self.richLabel getSelectText]]; 122 | __block NSMutableArray *flows = [NSMutableArray array]; 123 | [array enumerateObjectsUsingBlock:^(GBigbangItem * obj, NSUInteger idx, BOOL * _Nonnull stop) { 124 | GTagFlowLayout *layout = [GTagFlowLayout tagFlowLayoutWithText:obj.text]; 125 | [flows addObject:layout]; 126 | if (obj.isSymbolOrEmoji) { 127 | layout.appearance.backgroundColor = [UIColor grayColor]; 128 | layout.appearance.textColor = [UIColor blackColor]; 129 | } 130 | }]; 131 | [self.container configDatas:flows.copy]; 132 | [self.container show]; 133 | [self.richLabel resetSelection]; 134 | } 135 | 136 | /** 137 | share Method 138 | 139 | @param sender sender 140 | */ 141 | - (void)shareItem:(id)sender 142 | { 143 | NSString *textToShare = [NSString stringWithFormat:@"%@",[self.richLabel getSelectText]]; 144 | 145 | NSArray *activityItems = @[textToShare]; 146 | 147 | UIActivityViewController *activityVC = [[UIActivityViewController alloc]initWithActivityItems:activityItems applicationActivities:nil]; 148 | __weak typeof(self) weakSelf = self; 149 | 150 | [[self.richLabel getCurrentViewController] presentViewController:activityVC animated:YES completion:^{ 151 | [weakSelf.richLabel resetSelection]; 152 | }]; 153 | 154 | } 155 | 156 | /** 157 | 显示全部选择后的menu 158 | */ 159 | - (void)showAfterSelectAllMenu 160 | { 161 | GMenuItem *copyItem = [[GMenuItem alloc] initWithTitle:@"拷贝" target:self action:@selector(copyItem:)]; 162 | GMenuItem *shareItem = [[GMenuItem alloc] initWithTitle:@"共享" target:self action:@selector(shareItem:)]; 163 | NSArray *items = [NSArray arrayWithObjects:copyItem,shareItem,nil]; 164 | self.menuItems = items; 165 | [self.richLabel showTextMenu]; 166 | self.menuItems = self.oriMenuItems; 167 | } 168 | 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /GRichLabelExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /GRichLabelExample/LabelViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LabelViewController.h 3 | // GRichLabel 4 | // 5 | // Created by GIKI on 2017/8/29. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LabelViewController : UIViewController 12 | 13 | - (instancetype)initLabel:(int)type; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /GRichLabelExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2017/9/7. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /GRichLabelExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2017/9/7. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "LabelViewController.h" 11 | #import "novelReadViewController.h" 12 | #import "ChatViewController.h" 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | } 23 | - (IBAction)chatTextCell:(id)sender 24 | { 25 | ChatViewController * chat = [ChatViewController new]; 26 | [self.navigationController pushViewController:chat animated:YES]; 27 | } 28 | 29 | - (IBAction)richLabel:(id)sender 30 | { 31 | [self gotoViewController:1]; 32 | } 33 | - (IBAction)SelectRichLabel:(id)sender 34 | { 35 | [self gotoViewController:2]; 36 | } 37 | 38 | - (void)gotoViewController:(int)type 39 | { 40 | LabelViewController *vc = [[LabelViewController alloc] initLabel:type]; 41 | [self.navigationController pushViewController:vc animated:YES]; 42 | } 43 | - (IBAction)novelReader:(id)sender { 44 | novelReadViewController *vc = [[novelReadViewController alloc] init]; 45 | [self.navigationController pushViewController:vc animated:YES]; 46 | } 47 | 48 | - (void)didReceiveMemoryWarning { 49 | [super didReceiveMemoryWarning]; 50 | // Dispose of any resources that can be recreated. 51 | } 52 | 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /GRichLabelExample/emoji/anwei@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/GRichLabelExample/emoji/anwei@2x.png -------------------------------------------------------------------------------- /GRichLabelExample/emoji/dabian@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/GRichLabelExample/emoji/dabian@2x.png -------------------------------------------------------------------------------- /GRichLabelExample/emoji/daku@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/GRichLabelExample/emoji/daku@2x.png -------------------------------------------------------------------------------- /GRichLabelExample/emoji/expression.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | [大便] 6 | dabian@2x.png 7 | [哭笑不得] 8 | daku@2x.png 9 | 10 | 11 | -------------------------------------------------------------------------------- /GRichLabelExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // GRichLabelExample 4 | // 5 | // Created by GIKI on 2017/9/7. 6 | // Copyright © 2017年 GIKI. 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 | -------------------------------------------------------------------------------- /GRichLabelExample/novelReadViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // novelReadViewController.h 3 | // GRichTextExample 4 | // 5 | // Created by GIKI on 2017/9/15. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @interface novelReadViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /GRichLabelExample/novelReadViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // novelReadViewController.m 3 | // GRichTextExample 4 | // 5 | // Created by GIKI on 2017/9/15. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "novelReadViewController.h" 10 | #import "GRichLabel.h" 11 | #import "DemoTextMenuConfig.h" 12 | @interface novelReadViewController () 13 | @property (nonatomic, strong) UIImageView * backgroundImage; 14 | @property (nonatomic, strong) GRichLabel * richLabel; 15 | @end 16 | 17 | @implementation novelReadViewController 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | self.view.backgroundColor = [UIColor whiteColor]; 23 | [self.view addSubview:({ 24 | _backgroundImage = [[UIImageView alloc] init]; 25 | UIImage *image = [UIImage imageNamed:@"novelbg"]; 26 | _backgroundImage.image = image; 27 | _backgroundImage; 28 | })]; 29 | 30 | [self.view addSubview:({ 31 | _richLabel = [[GRichLabel alloc] init]; 32 | _richLabel.canSelect = YES; 33 | _richLabel.menuConfiguration = [DemoTextMenuConfig new]; 34 | _richLabel; 35 | })]; 36 | _backgroundImage.frame = self.view.bounds; 37 | _richLabel.frame = CGRectMake(0, 70, self.view.frame.size.width, self.view.frame.size.height-80);//self.view.frame.size.height-64 38 | [self setrichContent]; 39 | } 40 | 41 | - (void) viewDidLayoutSubviews 42 | { 43 | [super viewDidLayoutSubviews]; 44 | 45 | } 46 | 47 | 48 | - (void)setrichContent 49 | { 50 | GAttributedConfiguration *layout = [GAttributedConfiguration new]; 51 | layout.text = [self content]; 52 | layout.linespace = 3; 53 | layout.textAlignment = kCTTextAlignmentJustified; 54 | layout.font = [UIFont systemFontOfSize:16]; 55 | layout.textColor = [UIColor blackColor]; 56 | layout.lineIndent = 2; 57 | NSAttributedString *config = [GAttributedStringFactory createAttributedStringWithAttributedConfig:layout]; 58 | GDrawTextBuilder *buider = [GDrawTextBuilder buildDrawTextSize:self.richLabel.frame.size insert:UIEdgeInsetsMake(20, 20,10, 20) attributedString:config]; 59 | self.richLabel.textBuilder = buider;//[GAttributedStringFactory createDrawTextBuilderWithAttributedConfig:layout boundSize:CGSizeMake(self.richLabel.frame.size.width, self.richLabel.frame.size.height)]; 60 | } 61 | 62 | - (NSString*)content { 63 | 64 | return @"第1章[哭笑不得]\n时间定格[哭笑不得]在2009年5月12日凌晨两点,地点为上京市,国家心脏外的一处荒郊\n七辆黑色轿车在荒郊上极速的行驶着,两辆在前,两辆在后,两辆靠在两侧,护着中间的一辆黑色奔驰。军用的大功效引擎发出流畅的声响,车身完全由高性能铝合金所造,挡风玻璃上隐隐可看到呈螺旋状的防弹图痕,没有车牌照,没有特殊军用标识,不禁让人怀疑,这样的车队是怎样从那座森严的首都大门里走出来的。 \n一个小时之后,车队驶进了城郊一处并不起眼的土黄色建筑,四名身着迷彩服的士兵走上前来,示意车上的人停车接受检查,前方的一辆车门打开,身穿黑色西装的年轻人下了车,递过一张深红色的牌子,士兵检查了半晌,沉声说道:“我需要向上级请示。”\n男人眉梢一挑,口气急迫,微微带着丝怒气,压低声音说道:“这上面有华司令的签字,你还需要向什么人请示?”\n士兵面无表情的继续说道:“少校,上级刚刚下达命令,除了首长本人亲至,其他人进入军事禁地一律需要华司令和张参谋长两人的共同署名,否则一律不予放行。”\n“你……”\n“李阳。”\n一个低沉的声音突然从身后的车内响起,黑色奔驰缓缓开上前来,司机摇下车窗,露出里面一张略略有些疲倦的苍老面孔,士兵看了一惊,猛地立正站好,敬了一个军礼,说道:“首长好!”\n华司令淡淡点了点头:“现在我们可以进去了吧?”\n士兵微微有些迟疑,说道:“报告首长,张参谋长命令说军事禁区内不得行车,一律步行。”\n华司令眉头轻轻皱起,拍了拍自己的腿,说道:“我也需要步行?"; 65 | 66 | } 67 | 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /GRichLabelExample/novelabcd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/GRichLabelExample/novelabcd.jpg -------------------------------------------------------------------------------- /GRichLabelExample/novelabcd@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/GRichLabelExample/novelabcd@2x.jpg -------------------------------------------------------------------------------- /GRichLabelExample/titlebar_bt_search03@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/GRichLabelExample/titlebar_bt_search03@2x.png -------------------------------------------------------------------------------- /GRichLabelExample/titlebar_bt_search03@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/GRichLabelExample/titlebar_bt_search03@3x.png -------------------------------------------------------------------------------- /GRichLabelExampleTests/GRichLabelExampleTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // GRichLabelExampleTests.m 3 | // GRichLabelExampleTests 4 | // 5 | // Created by GIKI on 2017/9/7. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GRichLabelExampleTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation GRichLabelExampleTests 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 | -------------------------------------------------------------------------------- /GRichLabelExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /GRichLabelExampleUITests/GRichLabelExampleUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // GRichLabelExampleUITests.m 3 | // GRichLabelExampleUITests 4 | // 5 | // Created by GIKI on 2017/9/7. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GRichLabelExampleUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation GRichLabelExampleUITests 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 | -------------------------------------------------------------------------------- /GRichLabelExampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 keGong 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 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | pod 'YYAsyncLayer' 4 | pod ‘GMenuController’ 5 | pod ‘GBigbang’ 6 | target 'GRichLabelExample' do 7 | # Uncomment the next line if you're using Swift or would like to use dynamic frameworks 8 | # use_frameworks! 9 | 10 | # Pods for GRichLabelExample 11 | 12 | target 'GRichLabelExampleTests' do 13 | inherit! :search_paths 14 | # Pods for testing 15 | end 16 | 17 | target 'GRichLabelExampleUITests' do 18 | inherit! :search_paths 19 | # Pods for testing 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - GBigbang (0.0.1) 3 | - GMenuController (0.0.6) 4 | - YYAsyncLayer (1.0) 5 | 6 | DEPENDENCIES: 7 | - GBigbang 8 | - GMenuController 9 | - YYAsyncLayer 10 | 11 | SPEC REPOS: 12 | https://github.com/cocoapods/specs.git: 13 | - GBigbang 14 | - GMenuController 15 | - YYAsyncLayer 16 | 17 | SPEC CHECKSUMS: 18 | GBigbang: 1b89811891ec0f84b1def9ca031fa5baffcfc4f0 19 | GMenuController: c7712947635f972cd00e62a52b6276bfab24bfc6 20 | YYAsyncLayer: df6b01ebc695c5f6b4c15dd5c23fa3e081eb25c0 21 | 22 | PODFILE CHECKSUM: b8e2c421cf398ce3825533aaf48074aeb9911975 23 | 24 | COCOAPODS: 1.6.1 25 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GBigbang.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBigbang.h 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/24. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #ifndef GBigbang_h 10 | #define GBigbang_h 11 | #import "GBigbangBox.h" 12 | #import "GTagFlowLayout.h" 13 | #import "GTagFlowView.h" 14 | #import "GTagFlowContainer.h" 15 | #endif /* GBigbang_h */ 16 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GBigbangBox.h: -------------------------------------------------------------------------------- 1 | // 2 | // GBigbangBox.h 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/19. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class GBigbangItem; 12 | 13 | @interface GBigbangBox : NSObject 14 | 15 | + (NSArray*)bigBang:(NSString*)string; 16 | 17 | @end 18 | 19 | @interface GBigbangItem : NSObject 20 | @property (nonatomic, copy,readonly) NSString * text; 21 | @property (nonatomic, assign,readonly) BOOL isSymbolOrEmoji; 22 | +(instancetype)bigbangText:(NSString*)text isSymbol:(BOOL)isSymbol; 23 | @end 24 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GBigbangBox.m: -------------------------------------------------------------------------------- 1 | // 2 | // GBigbangBox.m 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/19. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GBigbangBox.h" 10 | 11 | @implementation GBigbangBox 12 | 13 | +(NSArray *)bigBang:(NSString *)string 14 | { 15 | __block NSMutableArray * array = [NSMutableArray array]; 16 | [string enumerateSubstringsInRange:NSMakeRange(0, string.length) options:NSStringEnumerationByWords usingBlock:^(NSString *subString, NSRange subStringRange, NSRange enclosingRange, BOOL *stop){ 17 | 18 | if (subStringRange.location > enclosingRange.location) { 19 | NSRange preRange = NSMakeRange(enclosingRange.location, subStringRange.location); 20 | NSString * preString = [string substringWithRange:preRange]; 21 | [preString enumerateSubstringsInRange:NSMakeRange(0, preString.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring1, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) { 22 | [array addObject:[GBigbangItem bigbangText:substring1 isSymbol:YES]]; 23 | }]; 24 | } 25 | 26 | if (![GBigbangBox isEmptAllSapce:subString]) { 27 | [array addObject:[GBigbangItem bigbangText:subString isSymbol:NO]]; 28 | } 29 | 30 | if (subStringRange.location > enclosingRange.location && (subStringRange.location + subStringRange.length) < (enclosingRange.location +enclosingRange.length)) { 31 | 32 | NSRange nextRange = NSMakeRange(subStringRange.location + subStringRange.length, (enclosingRange.location +enclosingRange.length)-(subStringRange.location + subStringRange.length)); 33 | NSString * nextString = [string substringWithRange:nextRange]; 34 | [nextString enumerateSubstringsInRange:NSMakeRange(0, nextString.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring2, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) { 35 | [array addObject:[GBigbangItem bigbangText:substring2 isSymbol:YES]]; 36 | }]; 37 | } 38 | 39 | if (subStringRange.length 0) { 43 | [subStr enumerateSubstringsInRange:NSMakeRange(0, subStr.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring3, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) { 44 | [array addObject:[GBigbangItem bigbangText:substring3 isSymbol:YES]]; 45 | }]; 46 | } 47 | } 48 | }]; 49 | return array.copy; 50 | } 51 | 52 | 53 | /** 54 | 是否为nil 或者全部为空格 55 | 56 | @return <#return value description#> 57 | */ 58 | + (BOOL)isEmptAllSapce:(NSString*)string 59 | { 60 | 61 | if (!string) { 62 | return YES; 63 | } else { 64 | if (![string isKindOfClass:[NSString class]]) { 65 | return NO; 66 | } 67 | //A character set containing only the whitespace characters space (U+0020) and tab (U+0009) and the newline and nextline characters (U+000A–U+000D, U+0085). 68 | NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 69 | 70 | //Returns a new string made by removing from both ends of the receiver characters contained in a given character set. 71 | NSString *trimedString = [string stringByTrimmingCharactersInSet:set]; 72 | 73 | if ([trimedString length] == 0) { 74 | return YES; 75 | } else { 76 | return NO; 77 | } 78 | } 79 | } 80 | 81 | @end 82 | 83 | @interface GBigbangItem() 84 | @property (nonatomic, copy,readwrite) NSString * text; 85 | @property (nonatomic, assign,readwrite) BOOL isSymbolOrEmoji; 86 | @end 87 | 88 | 89 | @implementation GBigbangItem 90 | 91 | +(instancetype)bigbangText:(NSString*)text isSymbol:(BOOL)isSymbol 92 | { 93 | GBigbangItem *item = [GBigbangItem new]; 94 | item.text = text; 95 | item.isSymbolOrEmoji = isSymbol; 96 | return item; 97 | } 98 | @end 99 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GTagFlowCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTagFlowCell.h 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/13. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | @class GTagFlowLayout; 11 | @interface GTagFlowCell : UICollectionViewCell 12 | 13 | - (void)configFlowLayout:(GTagFlowLayout*)flowLayout; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GTagFlowCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTagFlowCell.m 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/13. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GTagFlowCell.h" 10 | #import "GTagFlowLayout.h" 11 | 12 | @interface GTagFlowCell() 13 | @property (nonatomic, strong) UILabel * tagLabel; 14 | 15 | @end 16 | 17 | @implementation GTagFlowCell 18 | 19 | - (instancetype)initWithFrame:(CGRect)frame 20 | { 21 | self = [super initWithFrame:frame]; 22 | if (self) { 23 | [self.contentView addSubview:({ 24 | _tagLabel = [UILabel new]; 25 | _tagLabel.textColor = [UIColor grayColor]; 26 | _tagLabel.font = [UIFont systemFontOfSize:12]; 27 | _tagLabel.textAlignment = NSTextAlignmentCenter; 28 | _tagLabel; 29 | })]; 30 | 31 | 32 | } 33 | return self; 34 | } 35 | 36 | - (void)layoutSubviews 37 | { 38 | [super layoutSubviews]; 39 | self.tagLabel.frame = self.bounds; 40 | } 41 | 42 | - (void)configFlowLayout:(GTagFlowLayout*)flowLayout 43 | { 44 | if (flowLayout.isSelected) { 45 | if (flowLayout.appearance.selectTextColor) { 46 | _tagLabel.textColor = flowLayout.appearance.selectTextColor; 47 | } 48 | if (flowLayout.appearance.selectBackgroundColor) { 49 | _tagLabel.backgroundColor = flowLayout.appearance.selectBackgroundColor; 50 | } 51 | if (flowLayout.appearance.textFont) { 52 | _tagLabel.font = flowLayout.appearance.textFont; 53 | } 54 | _tagLabel.text = flowLayout.text; 55 | self.layer.cornerRadius = flowLayout.appearance.cornerRadius; 56 | self.layer.masksToBounds = YES; 57 | if (flowLayout.appearance.borderWidth) { 58 | _tagLabel.layer.borderWidth = flowLayout.appearance.borderWidth; 59 | _tagLabel.layer.borderColor = flowLayout.appearance.selectBorderColor.CGColor; 60 | } 61 | 62 | _tagLabel.layer.cornerRadius = flowLayout.appearance.cornerRadius; 63 | _tagLabel.layer.masksToBounds = YES; 64 | } else { 65 | _tagLabel.textColor = flowLayout.appearance.textColor; 66 | _tagLabel.font = flowLayout.appearance.textFont; 67 | _tagLabel.backgroundColor = flowLayout.appearance.backgroundColor; 68 | _tagLabel.text = flowLayout.text; 69 | 70 | _tagLabel.layer.borderWidth = flowLayout.appearance.borderWidth; 71 | _tagLabel.layer.borderColor = flowLayout.appearance.borderColor.CGColor; 72 | 73 | self.layer.cornerRadius = flowLayout.appearance.cornerRadius; 74 | self.layer.masksToBounds = YES; 75 | _tagLabel.layer.cornerRadius = flowLayout.appearance.cornerRadius; 76 | _tagLabel.layer.masksToBounds = YES; 77 | } 78 | } 79 | @end 80 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GTagFlowContainer.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTagFlowContainer.h 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/20. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GTagFlowView.h" 11 | 12 | #define kGPopContainerHiddenKey @"kGPopContainerHiddenKey" 13 | 14 | typedef void(^GTagFlowActionBlock) (NSString *actionTitle,NSString*newText); 15 | 16 | @interface GTagFlowContainer : UIView 17 | 18 | /// actionBtn 点击block 19 | @property (nonatomic, copy) GTagFlowActionBlock actionBlock; 20 | @property (nonatomic, strong) NSArray * actionBtnItems; 21 | 22 | @property (nonatomic, strong,readonly) GTagFlowView * flowView; 23 | 24 | 25 | - (void)configDatas:(NSArray*)flowDatas; 26 | - (void)show; 27 | - (void)hide; 28 | @end 29 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GTagFlowLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTagFlowLayout.h 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/13. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GBigbangBox.h" 11 | @interface GTagFlowAppearance : NSObject 12 | @property (nonatomic, strong) UIColor * textColor; 13 | @property (nonatomic, strong) UIColor * backgroundColor; 14 | @property (nonatomic, strong) UIColor * selectBackgroundColor; 15 | @property (nonatomic, strong) UIColor * selectTextColor; 16 | 17 | @property (nonatomic, assign) CGFloat borderWidth; 18 | @property (nonatomic, strong) UIColor * borderColor; 19 | @property (nonatomic, strong) UIColor * selectBorderColor; 20 | 21 | @property (nonatomic, strong) UIFont * textFont; 22 | @property (nonatomic, assign) CGFloat cornerRadius; 23 | 24 | @property (nonatomic, assign) CGFloat itemHeight; 25 | 26 | @end 27 | 28 | @interface GTagFlowLayout : NSObject 29 | 30 | @property (nonatomic, strong) NSString * text; 31 | 32 | @property (nonatomic, assign) CGSize itemSize; 33 | 34 | @property (nonatomic, strong) GTagFlowAppearance * appearance; 35 | 36 | @property (nonatomic, assign) BOOL isSelected; 37 | 38 | /** 39 | init Method 40 | 41 | @param text <#text description#> 42 | @return <#return value description#> 43 | */ 44 | + (instancetype)tagFlowLayoutWithText:(NSString*)text; 45 | + (instancetype)tagFlowLayoutWithText:(NSString*)text withAppearance:(GTagFlowAppearance*)appearance; 46 | 47 | /** 48 | factory Method 49 | 50 | @param items <#items description#> 51 | @param appearance <#appearance description#> 52 | @return <#return value description#> 53 | */ 54 | + (NSArray*)factoryFolwLayoutWithItems:(NSArray*)items withAppearance:(GTagFlowAppearance*)appearance; 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GTagFlowLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTagFlowLayout.m 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/13. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #define GTagColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] 10 | 11 | #import "GTagFlowLayout.h" 12 | 13 | @implementation GTagFlowAppearance 14 | - (instancetype)init 15 | { 16 | self = [super init]; 17 | if (self) { 18 | self.cornerRadius = 4; 19 | self.textColor = GTagColor(71, 71, 71); 20 | self.backgroundColor = [UIColor whiteColor]; 21 | self.selectTextColor = GTagColor(252, 253, 255); 22 | self.selectBackgroundColor = GTagColor(74, 150, 247); 23 | 24 | self.borderWidth = 0; 25 | self.borderColor = [UIColor redColor]; 26 | self.selectBorderColor = [UIColor blackColor]; 27 | 28 | self.textFont = [UIFont systemFontOfSize:12]; 29 | self.itemHeight = 30; 30 | 31 | } 32 | return self; 33 | } 34 | @end 35 | 36 | @implementation GTagFlowLayout 37 | 38 | + (instancetype)tagFlowLayoutWithText:(NSString*)text 39 | { 40 | GTagFlowLayout *flow = [[GTagFlowLayout alloc] init]; 41 | flow.text = text; 42 | return flow; 43 | } 44 | 45 | + (instancetype)tagFlowLayoutWithText:(NSString *)text withAppearance:(GTagFlowAppearance *)appearance 46 | { 47 | GTagFlowLayout *flow = [[GTagFlowLayout alloc] init]; 48 | flow.text = text; 49 | flow.appearance = appearance; 50 | return flow; 51 | } 52 | 53 | - (instancetype)init 54 | { 55 | self = [super init]; 56 | if (self) { 57 | self.appearance = [GTagFlowAppearance new]; 58 | } 59 | return self; 60 | } 61 | 62 | - (void)setText:(NSString *)text 63 | { 64 | _text = text; 65 | [self calculateItemSize]; 66 | } 67 | 68 | - (void)setAppearance:(GTagFlowAppearance *)appearance 69 | { 70 | if (appearance) { 71 | _appearance = appearance; 72 | [self calculateItemSize]; 73 | } 74 | } 75 | 76 | - (void)calculateItemSize 77 | { 78 | if (self.text.length <= 0 || !self.appearance) { 79 | return; 80 | } 81 | 82 | NSDictionary *attributes = @{NSFontAttributeName:self.appearance.textFont}; 83 | CGRect rect = [self.text boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) 84 | options:NSStringDrawingUsesLineFragmentOrigin 85 | attributes:attributes 86 | context:nil]; 87 | CGSize size = rect.size; 88 | if (self.appearance.itemHeight > 0) { 89 | size.height = self.appearance.itemHeight; 90 | } 91 | size.width += 18; 92 | self.itemSize = size; 93 | } 94 | 95 | + (NSArray*)factoryFolwLayoutWithItems:(NSArray*)items withAppearance:(GTagFlowAppearance*)appearance; 96 | { 97 | __block NSMutableArray * arrayM = [NSMutableArray arrayWithCapacity:items.count]; 98 | [items enumerateObjectsUsingBlock:^(GBigbangItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 99 | GTagFlowLayout *layout = [GTagFlowLayout tagFlowLayoutWithText:obj.text withAppearance:appearance]; 100 | [arrayM addObject:layout]; 101 | }]; 102 | return arrayM.copy; 103 | } 104 | @end 105 | -------------------------------------------------------------------------------- /Pods/GBigbang/GBigbang/GTagFlowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTagFlowView.h 3 | // GBigbangExample 4 | // 5 | // Created by GIKI on 2017/10/13. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GTagFlowLayout.h" 11 | 12 | typedef void(^GTagFlowViewHeightChanged) (CGFloat original,CGFloat newHeight); 13 | typedef void(^GTagFlowViewSelectedChanged) (BOOL hasSelected); 14 | 15 | @interface GTagFlowView : UIView 16 | 17 | @property (nonatomic, strong) UICollectionView * collectionView; 18 | /// 标签行间距 Default:10.f 19 | @property (nonatomic, assign) CGFloat lineSpacing; 20 | 21 | /// 标签间距 Default:4.f 22 | @property (nonatomic, assign) CGFloat interitemSpacing; 23 | 24 | /// collectionView EdgeInsets 25 | @property (nonatomic, assign) UIEdgeInsets edgeInsets; 26 | 27 | /// 是否支持滑动选择 Default:YES 28 | @property (nonatomic, assign) BOOL supportSlideSelection; 29 | 30 | /// 分词数组 31 | @property (nonatomic, strong ) NSArray * flowDatas; 32 | 33 | /// collectionView 高度改变回调 34 | @property (nonatomic, copy ) GTagFlowViewHeightChanged heightChangedBlock; 35 | 36 | /// 是否有选中词状态改变回调 37 | @property (nonatomic, copy ) GTagFlowViewSelectedChanged selectedChangedBlock; 38 | 39 | - (void)reloadDatas; 40 | 41 | /** 42 | 获取选择词/数组 43 | 44 | @param addingOrder 是否按照选择词顺序生成新词 45 | @return 新字符串 46 | */ 47 | - (NSString*)getNewTextOrderByAdding:(BOOL)addingOrder; 48 | - (NSArray *)filterAllSelectTitlesOrderByAdding:(BOOL)addingOrder; 49 | 50 | /// 按照原有排序生成新字符串 51 | - (NSString*)getNewTextstring; 52 | - (NSArray *)filterAllSelectTitles; 53 | - (NSArray *)filterNoSelectTitles; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Pods/GBigbang/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 GIKI 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 | -------------------------------------------------------------------------------- /Pods/GBigbang/README.md: -------------------------------------------------------------------------------- 1 | # GBigbang 2 | 大爆炸/分词/tagFlowView 3 | ============== 4 | 5 | [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/GIKICoder/GBigbang/blob/master/LICENSE)  6 | [![Support](https://img.shields.io/badge/support-iOS7+-blue.svg?style=flat)](https://www.apple.com/nl/ios/)  7 | 8 | 9 | 介绍 10 | ============== 11 | 这是一个分词组件.用与文本分词,列表展示.参考Pin的分词界面.和UC的bigbang界面. 12 | (该项目是 [GRichLabel](https://github.com/GIKICoder/GRichLabel) 文本选择复制功能的组件之一) 13 | 14 | 15 | 特性 16 | ============== 17 | - 可区分标点符号与表情. 18 | - 可自定义分词展现列表. 19 | - 分词列表支持滑动/点击选择. 20 | - 提供默认分词展现Container. 21 | - 22 | 23 | 用法 24 | ============== 25 | 26 | ### 基本用法 27 | ```objc 28 | 29 | NSString* selection = [self stringByEvaluatingJavaScriptFromString:@"window.getSelection().toString()"]; 30 | NSArray * array = [GBigbangBox bigBang:selection]; 31 | __block NSMutableArray *flows = [NSMutableArray array]; 32 | [array enumerateObjectsUsingBlock:^(GBigbangItem * obj, NSUInteger idx, BOOL * _Nonnull stop) { 33 | GTagFlowLayout *layout = [GTagFlowLayout tagFlowLayoutWithText:obj.text]; 34 | [flows addObject:layout]; 35 | if (obj.isSymbolOrEmoji) { 36 | layout.appearance.backgroundColor = [UIColor grayColor]; 37 | layout.appearance.textColor = [UIColor blackColor]; 38 | } 39 | }]; 40 | [self.container configDatas:flows.copy]; 41 | [self.container show]; 42 | 43 | ``` 44 | 45 | ```objc 46 | NSArray *items = [GBigbangBox bigBang:self.string]; 47 | 48 | NSArray * layouts = [GTagFlowLayout factoryFolwLayoutWithItems:items withAppearance:self.appearance]; 49 | self.flowView.flowDatas = layouts; 50 | [self.flowView reloadDatas]; 51 | ``` 52 | 安装 53 | ============== 54 | 55 | ### 手动添加 56 | 1. ` git clone https://github.com/GIKICoder/GBigbang.git ` 57 | 2. 选择`GBigbang`文件夹.拖入项目中即可. 58 | 59 | 60 | Demo 61 | ============== 62 | ### Demo 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Pods/GMenuController/GMenuController/GMenuController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMenuController.h 3 | // GMenuController 4 | // 5 | // Created by GIKI on 2017/9/27. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "GMenuControllerHeader.h" 12 | #import "GMenuViewContainer.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | 17 | @class GMenuItem,GMenuViewContainer; 18 | 19 | @interface GMenuController : NSObject 20 | 21 | + (GMenuController *)sharedMenuController; 22 | 23 | @property(nonatomic,getter=isMenuVisible) BOOL menuVisible; // default is NO 24 | 25 | @property(nonatomic) GMenuControllerArrowDirection arrowDirection ; // default is GMenuControllerArrowDefault 26 | 27 | @property(nullable, nonatomic,copy) NSArray *menuItems; // default is nil. these are in addition to the standard items 28 | 29 | - (void)setMenuVisible:(BOOL)menuVisible animated:(BOOL)animated; 30 | 31 | - (void)setTargetRect:(CGRect)targetRect inView:(UIView *)targetView; 32 | 33 | - (void)update; 34 | 35 | - (void)reset; /// reset menuViewContrainer configs 36 | 37 | @property(nonatomic,readonly) CGRect menuFrame; 38 | 39 | @property(nonatomic,readonly) GMenuViewContainer * menuViewContainer; 40 | 41 | @end 42 | 43 | NS_ASSUME_NONNULL_END 44 | 45 | NS_ASSUME_NONNULL_BEGIN 46 | @interface GMenuItem : NSObject 47 | - (instancetype)initWithTitle:(NSString *)title target:(id)target action:(SEL)action; 48 | - (instancetype)initWithTitle:(NSString *)title image:(UIImage*)image target:(id)target action:(SEL)action; 49 | @property(nonatomic,copy) NSString *title; 50 | @property (nonatomic, strong) UIImage * image; 51 | @property (nonatomic, strong) id target; 52 | @property(nonatomic) SEL action; 53 | @end 54 | NS_ASSUME_NONNULL_END 55 | -------------------------------------------------------------------------------- /Pods/GMenuController/GMenuController/GMenuController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GMenuController.m 3 | // GMenuController 4 | // 5 | // Created by GIKI on 2017/9/27. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GMenuController.h" 10 | #import "GMenuEffectsWindow.h" 11 | #import "GMenuViewContainer.h" 12 | NSNotificationName const GMenuControllerWillShowMenuNotification = @"GMenuControllerWillShowMenuNotification_private"; 13 | NSNotificationName const GMenuControllerDidShowMenuNotification= @"GMenuControllerDidShowMenuNotification_private"; 14 | NSNotificationName const GMenuControllerWillHideMenuNotification= @"GMenuControllerWillHideMenuNotification_private"; 15 | NSNotificationName const GMenuControllerDidHideMenuNotification= @"GMenuControllerDidHideMenuNotification_private"; 16 | NSNotificationName const GMenuControllerMenuFrameDidChangeNotification= @"GMenuControllerMenuFrameDidChangeNotification_private"; 17 | 18 | @interface GMenuController() 19 | { 20 | BOOL _showMenu; 21 | } 22 | @property (nonatomic, strong,readwrite) GMenuViewContainer * menuViewContainer; 23 | @property (nonatomic, weak) UIView * targetView; 24 | @end 25 | 26 | @implementation GMenuController 27 | 28 | #pragma mark - init 29 | 30 | - (instancetype)init 31 | { 32 | self = [super init]; 33 | if (self) { 34 | 35 | } 36 | return self; 37 | } 38 | 39 | #pragma mark - public Method 40 | 41 | + (GMenuController *)sharedMenuController 42 | { 43 | static GMenuController *inst = nil; 44 | 45 | static dispatch_once_t onceToken; 46 | dispatch_once(&onceToken, ^{ 47 | inst = [GMenuController new]; 48 | }); 49 | return inst; 50 | } 51 | 52 | - (BOOL)isMenuVisible 53 | { 54 | return _showMenu; 55 | } 56 | 57 | - (void)setMenuVisible:(BOOL)menuVisible 58 | { 59 | [self setMenuVisible:menuVisible animated:YES]; 60 | } 61 | 62 | - (void)setMenuVisible:(BOOL)menuVisible animated:(BOOL)animated 63 | { 64 | _showMenu = menuVisible; 65 | if (menuVisible) { 66 | [[NSNotificationCenter defaultCenter] postNotificationName:GMenuControllerWillShowMenuNotification object:nil]; 67 | [self showMenuWithAnimated:animated]; 68 | } else { 69 | [[NSNotificationCenter defaultCenter] postNotificationName:GMenuControllerWillHideMenuNotification object:nil]; 70 | [[GMenuEffectsWindow sharedWindow] hideMenu:self.menuViewContainer]; 71 | } 72 | } 73 | 74 | - (void)showMenuWithAnimated:(BOOL)animated 75 | { 76 | [[GMenuEffectsWindow sharedWindow] showMenu:self.menuViewContainer animation:YES]; 77 | } 78 | 79 | - (void)setTargetRect:(CGRect)targetRect inView:(UIView *)targetView 80 | { 81 | if (!self.menuViewContainer) return; 82 | self.targetView = targetView; 83 | [self.menuViewContainer setTargetRect:targetRect inView:targetView]; 84 | } 85 | 86 | - (void)setMenuItems:(NSArray *)menuItems 87 | { 88 | _menuItems = menuItems; 89 | self.menuViewContainer.menuItems = menuItems; 90 | } 91 | 92 | - (void)update 93 | { 94 | [self.menuViewContainer processMenuFrame]; 95 | } 96 | 97 | - (void)reset 98 | { 99 | [self.menuViewContainer initConfigs]; 100 | } 101 | 102 | - (CGRect)menuFrame 103 | { 104 | return self.menuViewContainer ? self.menuViewContainer.frame :CGRectZero; 105 | } 106 | 107 | - (void)setArrowDirection:(GMenuControllerArrowDirection)arrowDirection 108 | { 109 | _arrowDirection = arrowDirection; 110 | self.menuViewContainer.arrowDirection = arrowDirection; 111 | } 112 | 113 | - (GMenuViewContainer *)menuViewContainer 114 | { 115 | if (!_menuViewContainer) { 116 | _menuViewContainer = [GMenuViewContainer new]; 117 | } 118 | return _menuViewContainer; 119 | } 120 | @end 121 | 122 | @implementation GMenuItem 123 | 124 | - (instancetype)initWithTitle:(NSString *)title target:(id)target action:(SEL)action 125 | { 126 | self = [super init]; 127 | if (self) { 128 | self.title = title; 129 | self.target = target; 130 | self.action = action; 131 | } 132 | return self; 133 | } 134 | 135 | - (instancetype)initWithTitle:(NSString *)title image:(UIImage*)image target:(id)target action:(SEL)action 136 | { 137 | self = [super init]; 138 | if (self) { 139 | self.title = title; 140 | self.target = target; 141 | self.action = action; 142 | self.image = image; 143 | } 144 | return self; 145 | } 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /Pods/GMenuController/GMenuController/GMenuControllerHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMenuControllerHeader.h 3 | // GMenuController 4 | // 5 | // Created by GIKI on 2017/10/19. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #ifndef GMenuControllerHeader_h 10 | #define GMenuControllerHeader_h 11 | 12 | typedef NS_ENUM(NSUInteger, GMenuControllerArrowDirection) { 13 | GMenuControllerArrowDefault, // up or down based on screen location 14 | GMenuControllerArrowUp , // Forced upward. If the screen is not displayed, Will do anchor displacement 15 | GMenuControllerArrowDown , // Forced down 16 | }; 17 | 18 | UIKIT_EXTERN NSNotificationName const GMenuControllerWillShowMenuNotification; 19 | UIKIT_EXTERN NSNotificationName const GMenuControllerDidShowMenuNotification; 20 | UIKIT_EXTERN NSNotificationName const GMenuControllerWillHideMenuNotification; 21 | UIKIT_EXTERN NSNotificationName const GMenuControllerDidHideMenuNotification; 22 | UIKIT_EXTERN NSNotificationName const GMenuControllerMenuFrameDidChangeNotification; 23 | 24 | #endif /* GMenuControllerHeader_h */ 25 | -------------------------------------------------------------------------------- /Pods/GMenuController/GMenuController/Internal/GAdjustButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // GAdjustButton.h 3 | // GMenuController 4 | // 5 | // Created by GIKI on 2017/10/19. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | typedef NS_ENUM(NSUInteger, GAdjustButtonIMGPosition) { 13 | GAdjustButtonIMGPositionLeft = 0, //Default 14 | GAdjustButtonIMGPositionRight, 15 | GAdjustButtonIMGPositionTop, 16 | GAdjustButtonIMGPositionBottom, 17 | }; 18 | 19 | @interface GAdjustButton : UIButton 20 | 21 | @property (nonatomic, assign) GAdjustButtonIMGPosition imagePosition; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Pods/GMenuController/GMenuController/Internal/GMenuDefaultView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMenuDefaultView.h 3 | // GMenuController 4 | // 5 | // Created by GIKI on 2017/9/29. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GMenuControllerHeader.h" 11 | @class GMenuItem,GMenuViewContainer; 12 | @interface GMenuDefaultView : UIView 13 | @property (nonatomic, strong) NSArray* menuItems; 14 | @property (nonatomic, assign) CGSize maxSize; 15 | @property (nonatomic, assign) CGSize arrowSize; 16 | @property (nonatomic, assign) CGPoint anchorPoint; 17 | @property (nonatomic, strong) UIColor *menuTintColor; 18 | @property (nonatomic, assign) GMenuControllerArrowDirection CorrectDirection; 19 | +(instancetype)defaultView:(GMenuViewContainer*)container WithMenuItems:(NSArray*)menuItems MaxSize:(CGSize)maxSize arrowSize:(CGSize)arrowSize AnchorPoint:(CGPoint)anchorPoint; 20 | - (void)processLineWithMidX:(CGFloat)midX direction:(GMenuControllerArrowDirection)direction; 21 | @end 22 | -------------------------------------------------------------------------------- /Pods/GMenuController/GMenuController/Internal/GMenuEffectsWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMenuEffectsWindow.h 3 | // GMenuController 4 | // 5 | // Created by GIKI on 2017/9/27. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class GMenuViewContainer; 12 | @interface GMenuEffectsWindow : UIWindow 13 | 14 | + (instancetype)sharedWindow; 15 | 16 | - (void)showMenu:(GMenuViewContainer *)menu animation:(BOOL)animation; 17 | 18 | - (void)hideMenu:(GMenuViewContainer *)menu; 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /Pods/GMenuController/GMenuController/Internal/GMenuEffectsWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // GMenuEffectsWindow.m 3 | // GMenuController 4 | // 5 | // Created by GIKI on 2017/9/27. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import "GMenuEffectsWindow.h" 10 | #import "GMenuViewContainer.h" 11 | 12 | @interface GMenuEffectsWindow () 13 | @property (nonatomic, strong) UITapGestureRecognizer * tapGesture; 14 | @property (nonatomic, weak) GMenuViewContainer * currentMenu; 15 | @end 16 | 17 | @implementation GMenuEffectsWindow 18 | 19 | + (instancetype)sharedWindow 20 | { 21 | static GMenuEffectsWindow *inst = nil; 22 | if (inst == nil) { 23 | // iOS 9 compatible 24 | NSString *mode = [NSRunLoop currentRunLoop].currentMode; 25 | if (mode.length == 27 && 26 | [mode hasPrefix:@"UI"] && 27 | [mode hasSuffix:@"InitializationRunLoopMode"]) { 28 | return nil; 29 | } 30 | } 31 | 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | inst = [self new]; 35 | inst.frame = (CGRect){.size = GMenuScreenSize()}; 36 | inst.userInteractionEnabled = NO; 37 | inst.windowLevel = UIWindowLevelStatusBar + 1; 38 | inst.hidden = NO; 39 | 40 | // for iOS 9: 41 | inst.opaque = NO; 42 | inst.backgroundColor = [UIColor clearColor]; 43 | inst.layer.backgroundColor = [UIColor clearColor].CGColor; 44 | }); 45 | return inst; 46 | } 47 | 48 | - (void)showMenu:(GMenuViewContainer *)menu animation:(BOOL)animation 49 | { 50 | if(!menu) return; 51 | menu.alpha = 0; 52 | if(menu.superview != self) [self addSubview:menu]; 53 | self.currentMenu = menu; 54 | [self updateWindowLevel]; 55 | 56 | if (animation) { 57 | NSTimeInterval time = 0.16; 58 | [UIView animateWithDuration:time delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 59 | if (menu) { 60 | menu.alpha = 1; 61 | } 62 | [[NSNotificationCenter defaultCenter] postNotificationName:GMenuControllerDidShowMenuNotification object:nil]; 63 | } completion:^(BOOL finished) { 64 | 65 | }]; 66 | } else { 67 | menu.alpha = 1; 68 | [[NSNotificationCenter defaultCenter] postNotificationName:GMenuControllerDidShowMenuNotification object:nil]; 69 | } 70 | 71 | } 72 | 73 | - (void)hideMenu:(GMenuViewContainer *)menu 74 | { 75 | if (!menu) return; 76 | if (menu.superview != self) return; 77 | [menu initConfigs]; 78 | [menu removeFromSuperview]; 79 | menu = nil; 80 | [[NSNotificationCenter defaultCenter] postNotificationName:GMenuControllerDidHideMenuNotification object:nil]; 81 | } 82 | 83 | - (void)updateWindowLevel 84 | { 85 | UIApplication *app = [UIApplication sharedApplication]; 86 | if (!app) return; 87 | 88 | UIWindow *top = app.windows.lastObject; 89 | UIWindow *key = app.keyWindow; 90 | if (key && key.windowLevel > top.windowLevel) top = key; 91 | if (top == self) return; 92 | self.windowLevel = top.windowLevel + 1; 93 | } 94 | 95 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 96 | { 97 | if ([self pointInside:point withEvent:event] == NO) return nil; 98 | 99 | NSInteger count = self.subviews.count; 100 | UIView *fitView = nil; 101 | for (NSInteger i = count - 1; i >= 0; i--) { 102 | UIView *childView = self.subviews[i]; 103 | CGPoint childP = [self convertPoint:point toView:childView]; 104 | fitView = [childView hitTest:childP withEvent:event]; 105 | if (fitView) { 106 | break; 107 | } 108 | } 109 | if (fitView) { 110 | return fitView; 111 | } 112 | if (self.currentMenu && self.currentMenu.superview && self.currentMenu.hasAutoHide) { 113 | [self hideMenu:self.currentMenu]; 114 | } 115 | return nil; 116 | } 117 | 118 | CGSize GMenuScreenSize() { 119 | static CGSize size; 120 | static dispatch_once_t onceToken; 121 | dispatch_once(&onceToken, ^{ 122 | size = [UIScreen mainScreen].bounds.size; 123 | if (size.height < size.width) { 124 | CGFloat tmp = size.height; 125 | size.height = size.width; 126 | size.width = tmp; 127 | } 128 | }); 129 | return size; 130 | } 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /Pods/GMenuController/GMenuController/Internal/GMenuViewContainer.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMenuContainer.h 3 | // GMenuController 4 | // 5 | // Created by GIKI on 2017/9/27. 6 | // Copyright © 2017年 GIKI. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "GMenuDefaultView.h" 11 | #import "GAdjustButton.h" 12 | 13 | @interface GMenuViewContainer : UIView 14 | 15 | /** 16 | 点击非menuViewContainer 区域.自动隐藏. Defulat:YES 17 | */ 18 | @property (nonatomic, assign) BOOL hasAutoHide; 19 | 20 | /** 21 | menuView 22 | */ 23 | @property(nonatomic, strong) GMenuDefaultView *menuView; 24 | 25 | /** 26 | menuView 填充颜色 Defulat:[UIColor colorWithRed:26/255 green:26/288 blue:27/255 alpha:1] 27 | */ 28 | @property (nonatomic, strong) UIColor *fillColor; 29 | 30 | /** 31 | menuView 圆角 32 | */ 33 | @property(nonatomic, assign) CGFloat cornerRadius; 34 | 35 | /** 36 | 三角箭头的大小,defulat:CGSizeMake(17, 9.7) 系统menu箭头size 37 | */ 38 | @property(nonatomic, assign) CGSize arrowSize ; 39 | 40 | /** 41 | 箭头指向 42 | GMenuControllerArrow Default: 会自动计算合适的箭头方向 43 | */ 44 | @property(nonatomic, assign) GMenuControllerArrowDirection arrowDirection ; 45 | 46 | /** 47 | 如果menuItem 有图片. 指定图片与文字的排列. Default:GAdjustButtonIMGPositionLeft 48 | */ 49 | @property (nonatomic, assign) GAdjustButtonIMGPosition imagePosition; 50 | 51 | /** 52 | menuItemFont Default:14 53 | */ 54 | @property (nonatomic, strong) UIFont * menuItemFont; 55 | 56 | /** 57 | menuItemColor Default:whiteColor 58 | */ 59 | @property (nonatomic, strong) UIColor * menuItemTintColor; 60 | 61 | /** 62 | menuItemHighlightColor Default:lightGaryColor 63 | */ 64 | @property (nonatomic, strong) UIColor * menuItemHighlightColor; 65 | /** 66 | menu高度 Default:45.34f 67 | */ 68 | @property (nonatomic, assign) CGFloat menuViewHeight; 69 | 70 | /** 71 | menu最大宽度 72 | 默认screenWidth 73 | 真实宽度需要减去menuEdgeInset 74 | */ 75 | @property (nonatomic, assign) CGFloat maxMenuViewWidth; 76 | 77 | /** 78 | 箭头与目标view的间隙 79 | */ 80 | @property(nonatomic, assign) CGFloat arrowMargin; 81 | 82 | /** 83 | menuView 与屏幕边缘的间隙 84 | */ 85 | @property(nonatomic, assign) UIEdgeInsets menuEdgeInset; 86 | 87 | @property (nonatomic, strong) NSArray * menuItems; 88 | 89 | - (void)setTargetRect:(CGRect)targetRect inView:(UIView *)targetView; 90 | 91 | - (void)processMenuFrame; 92 | 93 | - (void)initConfigs; 94 | @end 95 | -------------------------------------------------------------------------------- /Pods/GMenuController/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 GIKI 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 | -------------------------------------------------------------------------------- /Pods/GMenuController/README.md: -------------------------------------------------------------------------------- 1 | GMenuController 2 | ============== 3 | [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/GIKICoder/GMenuController/blob/master/LICENSE)  4 | [![Support](https://img.shields.io/badge/support-iOS7+-blue.svg?style=flat)](https://www.apple.com/nl/ios/)  5 | [![CocoaPods](http://img.shields.io/cocoapods/v/GMenuController.svg?style=flat)](http://cocoapods.org/pods/GMenuController)  6 | 7 | 介绍 8 | ============== 9 | 具有和系统UIMenuController行为,交互一致的UI控件.相比UIMenuController.具有更加友好的使用方式. 10 | (该项目是 [GRichLabel](https://github.com/GIKICoder/GRichLabel) 文本选择复制功能的组件之一) 11 | 12 | 13 | 特性 14 | ============== 15 | - API与UIMenuController一致. 16 | - 支持MenuItem指定target.使用更加灵活 17 | - 支持显示图标加文字menu.图标可自定义位置 18 | - 支持更改menuview 外观设置 19 | - 无需对添加MenuController的控件 添加canBecomeFirstResponder等行为. 20 | - 相比UIMenuController. GMenuController具有更加简单,友好的使用方式. 21 | 22 | 用法 23 | ============== 24 | 25 | ### 基本用法 26 | ```objc 27 | // GMenuController (和 UIMenuController 用法一致) 28 | GMenuItem *item1 = [[GMenuItem alloc] initWithTitle:@"选择" target:self action:@selector(test)]; 29 | GMenuItem *item2 = [[GMenuItem alloc] initWithTitle:@"复制" target:self action:@selector(test)]; 30 | GMenuItem *item3 = [[GMenuItem alloc] initWithTitle:@"全选" target:self action:@selector(test)]; 31 | GMenuItem *item4= [[GMenuItem alloc] initWithTitle:@"收藏" target:self action:@selector(test)]; 32 | GMenuItem *item5 = [[GMenuItem alloc] initWithTitle:@"更多" target:self action:@selector(test)]; 33 | self.arr = @[item1,item2,item3,item4,item5]; 34 | 35 | [[GMenuController sharedMenuController] setMenuItems:self.arr]; 36 | [[GMenuController sharedMenuController] setTargetRect:sender.frame inView:self.view]; 37 | [[GMenuController sharedMenuController] setMenuVisible:YES]; 38 | 39 | ``` 40 | 41 | ```objc 42 | ///带有图片的menuView. 可指定图片位置 43 | GMenuItem *item1 = [[GMenuItem alloc] initWithTitle:@"选择" image:[UIImage imageNamed:@"star"] target:self action:@selector(test)]; 44 | GMenuItem *item2 = [[GMenuItem alloc] initWithTitle:@"复制" image:[UIImage imageNamed:@"star"] target:self action:@selector(test)]; 45 | GMenuItem *item3 = [[GMenuItem alloc] initWithTitle:@"全选" image:[UIImage imageNamed:@"star"] target:self action:@selector(test)]; 46 | GMenuItem *item4= [[GMenuItem alloc] initWithTitle:@"收藏" image:[UIImage imageNamed:@"star"] target:self action:@selector(test)]; 47 | GMenuItem *item5 = [[GMenuItem alloc] initWithTitle:@"更多" image:[UIImage imageNamed:@"star"] target:self action:@selector(test)]; 48 | GMenuItem *item6 = [[GMenuItem alloc] initWithTitle:@"可以有多个文字,行为与系统menuview完全一致" image:[UIImage imageNamed:@"star"] target:self action:@selector(test)]; 49 | NSArray* arr1 = @[item1,item2,item3,item4,item5,item6]; 50 | 51 | GMenuController.sharedMenuController.menuViewContainer.imagePosition = GAdjustButtonIMGPositionBottom; 52 | GMenuController.sharedMenuController.menuViewContainer.menuViewHeight = 65; 53 | [[GMenuController sharedMenuController] setMenuItems:arr1]; 54 | [[GMenuController sharedMenuController] setTargetRect:sender.frame inView:self.view]; 55 | [[GMenuController sharedMenuController] setMenuVisible:YES]; 56 | ``` 57 | 58 | ### other 59 | ```objc 60 | ///menuView弹出状态下.点击空白区域默认自动隐藏.如何不需要自动隐藏设置: 61 | [GMenuController sharedMenuController].menuViewContainer.hasAutoHide = NO; 62 | ``` 63 | 64 | ```objc 65 | ///更多menuView属性设置 66 | 67 | /** 68 | menuView 填充颜色 Defulat:[UIColor colorWithRed:26/255 green:26/288 blue:27/255 alpha:1] 69 | */ 70 | @property (nonatomic, strong) UIColor *fillColor; 71 | 72 | /** 73 | menuView 圆角 74 | */ 75 | @property(nonatomic, assign) CGFloat cornerRadius; 76 | 77 | /** 78 | 三角箭头的大小,defulat:CGSizeMake(17, 9.7) 系统menu箭头size 79 | */ 80 | @property(nonatomic, assign) CGSize arrowSize ; 81 | 82 | /** 83 | 箭头指向 84 | GMenuControllerArrow Default: 会自动计算合适的箭头方向 85 | */ 86 | @property(nonatomic, assign) GMenuControllerArrowDirection arrowDirection ; 87 | 88 | /** 89 | 如果menuItem 有图片. 指定图片与文字的排列. Default:GAdjustButtonIMGPositionLeft 90 | */ 91 | @property (nonatomic, assign) GAdjustButtonIMGPosition imagePosition; 92 | 93 | /** 94 | menuItemFont Default:14 95 | */ 96 | @property (nonatomic, strong) UIFont * menuItemFont; 97 | 98 | /** 99 | menuItemColor Default:whiteColor 100 | */ 101 | @property (nonatomic, strong) UIColor * menuItemTintColor; 102 | 103 | /** 104 | menuItemHighlightColor Default:lightGaryColor 105 | */ 106 | @property (nonatomic, strong) UIColor * menuItemHighlightColor; 107 | /** 108 | menu高度 Default:45.34f 109 | */ 110 | @property (nonatomic, assign) CGFloat menuViewHeight; 111 | 112 | /** 113 | menu最大宽度 114 | 默认screenWidth 115 | 真实宽度需要减去menuEdgeInset 116 | */ 117 | @property (nonatomic, assign) CGFloat maxMenuViewWidth; 118 | 119 | /** 120 | 箭头与目标view的间隙 121 | */ 122 | @property(nonatomic, assign) CGFloat arrowMargin; 123 | 124 | /** 125 | menuView 与屏幕边缘的间隙 126 | */ 127 | @property(nonatomic, assign) UIEdgeInsets menuEdgeInset; 128 | ``` 129 | 安装 130 | ============== 131 | 132 | ### CocoaPods 133 | 134 | 1. Update cocoapods to the latest version. 135 | 2. Add `pod 'GMenuController'` to your Podfile. 136 | 3. Run `pod install` or `pod update`. 137 | 4. Import "GMenuController.h" 138 | 139 | ### 手动添加 140 | 1. ` git clone https://github.com/GIKICoder/GMenuController.git ` 141 | 2. 选择`GMenuController`文件夹.拖入项目中即可. 142 | 143 | 144 | Demo 145 | ============== 146 | ### Demo 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /Pods/Headers/Private/GBigbang/GBigbang.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GBigbang.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GBigbang/GBigbangBox.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GBigbangBox.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GBigbang/GTagFlowCell.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GTagFlowCell.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GBigbang/GTagFlowContainer.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GTagFlowContainer.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GBigbang/GTagFlowLayout.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GTagFlowLayout.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GBigbang/GTagFlowView.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GTagFlowView.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GMenuController/GAdjustButton.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/Internal/GAdjustButton.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GMenuController/GMenuController.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/GMenuController.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GMenuController/GMenuControllerHeader.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/GMenuControllerHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GMenuController/GMenuDefaultView.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/Internal/GMenuDefaultView.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GMenuController/GMenuEffectsWindow.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/Internal/GMenuEffectsWindow.h -------------------------------------------------------------------------------- /Pods/Headers/Private/GMenuController/GMenuViewContainer.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/Internal/GMenuViewContainer.h -------------------------------------------------------------------------------- /Pods/Headers/Private/YYAsyncLayer/YYAsyncLayer.h: -------------------------------------------------------------------------------- 1 | ../../../YYAsyncLayer/YYAsyncLayer/YYAsyncLayer.h -------------------------------------------------------------------------------- /Pods/Headers/Private/YYAsyncLayer/YYSentinel.h: -------------------------------------------------------------------------------- 1 | ../../../YYAsyncLayer/YYAsyncLayer/YYSentinel.h -------------------------------------------------------------------------------- /Pods/Headers/Private/YYAsyncLayer/YYTransaction.h: -------------------------------------------------------------------------------- 1 | ../../../YYAsyncLayer/YYAsyncLayer/YYTransaction.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GBigbang/GBigbang.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GBigbang.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GBigbang/GBigbangBox.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GBigbangBox.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GBigbang/GTagFlowCell.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GTagFlowCell.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GBigbang/GTagFlowContainer.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GTagFlowContainer.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GBigbang/GTagFlowLayout.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GTagFlowLayout.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GBigbang/GTagFlowView.h: -------------------------------------------------------------------------------- 1 | ../../../GBigbang/GBigbang/GTagFlowView.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GMenuController/GAdjustButton.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/Internal/GAdjustButton.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GMenuController/GMenuController.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/GMenuController.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GMenuController/GMenuControllerHeader.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/GMenuControllerHeader.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GMenuController/GMenuDefaultView.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/Internal/GMenuDefaultView.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GMenuController/GMenuEffectsWindow.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/Internal/GMenuEffectsWindow.h -------------------------------------------------------------------------------- /Pods/Headers/Public/GMenuController/GMenuViewContainer.h: -------------------------------------------------------------------------------- 1 | ../../../GMenuController/GMenuController/Internal/GMenuViewContainer.h -------------------------------------------------------------------------------- /Pods/Headers/Public/YYAsyncLayer/YYAsyncLayer.h: -------------------------------------------------------------------------------- 1 | ../../../YYAsyncLayer/YYAsyncLayer/YYAsyncLayer.h -------------------------------------------------------------------------------- /Pods/Headers/Public/YYAsyncLayer/YYSentinel.h: -------------------------------------------------------------------------------- 1 | ../../../YYAsyncLayer/YYAsyncLayer/YYSentinel.h -------------------------------------------------------------------------------- /Pods/Headers/Public/YYAsyncLayer/YYTransaction.h: -------------------------------------------------------------------------------- 1 | ../../../YYAsyncLayer/YYAsyncLayer/YYTransaction.h -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - GBigbang (0.0.1) 3 | - GMenuController (0.0.6) 4 | - YYAsyncLayer (1.0) 5 | 6 | DEPENDENCIES: 7 | - GBigbang 8 | - GMenuController 9 | - YYAsyncLayer 10 | 11 | SPEC REPOS: 12 | https://github.com/cocoapods/specs.git: 13 | - GBigbang 14 | - GMenuController 15 | - YYAsyncLayer 16 | 17 | SPEC CHECKSUMS: 18 | GBigbang: 1b89811891ec0f84b1def9ca031fa5baffcfc4f0 19 | GMenuController: c7712947635f972cd00e62a52b6276bfab24bfc6 20 | YYAsyncLayer: df6b01ebc695c5f6b4c15dd5c23fa3e081eb25c0 21 | 22 | PODFILE CHECKSUM: b8e2c421cf398ce3825533aaf48074aeb9911975 23 | 24 | COCOAPODS: 1.6.1 25 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GBigbang/GBigbang-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_GBigbang : NSObject 3 | @end 4 | @implementation PodsDummy_GBigbang 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GBigbang/GBigbang-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GBigbang/GBigbang.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GBigbang 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/GBigbang" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GBigbang" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/GBigbang 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GMenuController/GMenuController-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_GMenuController : NSObject 3 | @end 4 | @implementation PodsDummy_GMenuController 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GMenuController/GMenuController-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/GMenuController/GMenuController.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/GMenuController 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/GMenuController" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GMenuController" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/GMenuController 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExample/Pods-GRichLabelExample-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## GBigbang 5 | 6 | MIT License 7 | 8 | Copyright (c) 2017 GIKI 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | ## GMenuController 30 | 31 | MIT License 32 | 33 | Copyright (c) 2017 GIKI 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy 36 | of this software and associated documentation files (the "Software"), to deal 37 | in the Software without restriction, including without limitation the rights 38 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 | copies of the Software, and to permit persons to whom the Software is 40 | furnished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be included in all 43 | copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 | SOFTWARE. 52 | 53 | 54 | ## YYAsyncLayer 55 | 56 | The MIT License (MIT) 57 | 58 | Copyright (c) 2015 ibireme 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining a copy 61 | of this software and associated documentation files (the "Software"), to deal 62 | in the Software without restriction, including without limitation the rights 63 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | copies of the Software, and to permit persons to whom the Software is 65 | furnished to do so, subject to the following conditions: 66 | 67 | The above copyright notice and this permission notice shall be included in all 68 | copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 76 | SOFTWARE. 77 | 78 | 79 | Generated by CocoaPods - https://cocoapods.org 80 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExample/Pods-GRichLabelExample-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | MIT License 18 | 19 | Copyright (c) 2017 GIKI 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | GBigbang 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | MIT License 49 | 50 | Copyright (c) 2017 GIKI 51 | 52 | Permission is hereby granted, free of charge, to any person obtaining a copy 53 | of this software and associated documentation files (the "Software"), to deal 54 | in the Software without restriction, including without limitation the rights 55 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | copies of the Software, and to permit persons to whom the Software is 57 | furnished to do so, subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall be included in all 60 | copies or substantial portions of the Software. 61 | 62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 67 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 68 | SOFTWARE. 69 | 70 | License 71 | MIT 72 | Title 73 | GMenuController 74 | Type 75 | PSGroupSpecifier 76 | 77 | 78 | FooterText 79 | The MIT License (MIT) 80 | 81 | Copyright (c) 2015 ibireme <ibireme@gmail.com> 82 | 83 | Permission is hereby granted, free of charge, to any person obtaining a copy 84 | of this software and associated documentation files (the "Software"), to deal 85 | in the Software without restriction, including without limitation the rights 86 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 87 | copies of the Software, and to permit persons to whom the Software is 88 | furnished to do so, subject to the following conditions: 89 | 90 | The above copyright notice and this permission notice shall be included in all 91 | copies or substantial portions of the Software. 92 | 93 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 94 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 95 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 96 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 97 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 98 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 99 | SOFTWARE. 100 | 101 | 102 | License 103 | MIT 104 | Title 105 | YYAsyncLayer 106 | Type 107 | PSGroupSpecifier 108 | 109 | 110 | FooterText 111 | Generated by CocoaPods - https://cocoapods.org 112 | Title 113 | 114 | Type 115 | PSGroupSpecifier 116 | 117 | 118 | StringsTable 119 | Acknowledgements 120 | Title 121 | Acknowledgements 122 | 123 | 124 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExample/Pods-GRichLabelExample-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GRichLabelExample : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GRichLabelExample 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExample/Pods-GRichLabelExample-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 91 | wait 92 | fi 93 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExample/Pods-GRichLabelExample-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExample/Pods-GRichLabelExample.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GBigbang" "${PODS_ROOT}/Headers/Public/GMenuController" "${PODS_ROOT}/Headers/Public/YYAsyncLayer" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GBigbang" "${PODS_CONFIGURATION_BUILD_DIR}/GMenuController" "${PODS_CONFIGURATION_BUILD_DIR}/YYAsyncLayer" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"GBigbang" -l"GMenuController" -l"YYAsyncLayer" -framework "QuartzCore" -framework "UIKit" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExample/Pods-GRichLabelExample.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GBigbang" "${PODS_ROOT}/Headers/Public/GMenuController" "${PODS_ROOT}/Headers/Public/YYAsyncLayer" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/GBigbang" "${PODS_CONFIGURATION_BUILD_DIR}/GMenuController" "${PODS_CONFIGURATION_BUILD_DIR}/YYAsyncLayer" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"GBigbang" -l"GMenuController" -l"YYAsyncLayer" -framework "QuartzCore" -framework "UIKit" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleTests/Pods-GRichLabelExampleTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleTests/Pods-GRichLabelExampleTests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleTests/Pods-GRichLabelExampleTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GRichLabelExampleTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GRichLabelExampleTests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleTests/Pods-GRichLabelExampleTests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 91 | wait 92 | fi 93 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleTests/Pods-GRichLabelExampleTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GBigbang" "${PODS_ROOT}/Headers/Public/GMenuController" "${PODS_ROOT}/Headers/Public/YYAsyncLayer" 3 | OTHER_LDFLAGS = $(inherited) -ObjC -framework "QuartzCore" -framework "UIKit" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 7 | PODS_ROOT = ${SRCROOT}/Pods 8 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleTests/Pods-GRichLabelExampleTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GBigbang" "${PODS_ROOT}/Headers/Public/GMenuController" "${PODS_ROOT}/Headers/Public/YYAsyncLayer" 3 | OTHER_LDFLAGS = $(inherited) -ObjC -framework "QuartzCore" -framework "UIKit" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 7 | PODS_ROOT = ${SRCROOT}/Pods 8 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleUITests/Pods-GRichLabelExampleUITests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleUITests/Pods-GRichLabelExampleUITests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleUITests/Pods-GRichLabelExampleUITests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_GRichLabelExampleUITests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_GRichLabelExampleUITests 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleUITests/Pods-GRichLabelExampleUITests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GBigbang" "${PODS_ROOT}/Headers/Public/GMenuController" "${PODS_ROOT}/Headers/Public/YYAsyncLayer" 3 | OTHER_LDFLAGS = $(inherited) -ObjC -framework "QuartzCore" -framework "UIKit" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 7 | PODS_ROOT = ${SRCROOT}/Pods 8 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-GRichLabelExampleUITests/Pods-GRichLabelExampleUITests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/GBigbang" "${PODS_ROOT}/Headers/Public/GMenuController" "${PODS_ROOT}/Headers/Public/YYAsyncLayer" 3 | OTHER_LDFLAGS = $(inherited) -ObjC -framework "QuartzCore" -framework "UIKit" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 7 | PODS_ROOT = ${SRCROOT}/Pods 8 | -------------------------------------------------------------------------------- /Pods/Target Support Files/YYAsyncLayer/YYAsyncLayer-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_YYAsyncLayer : NSObject 3 | @end 4 | @implementation PodsDummy_YYAsyncLayer 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/YYAsyncLayer/YYAsyncLayer-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/YYAsyncLayer/YYAsyncLayer.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYAsyncLayer 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/YYAsyncLayer" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YYAsyncLayer" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYAsyncLayer 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Pods/YYAsyncLayer/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ibireme 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 | -------------------------------------------------------------------------------- /Pods/YYAsyncLayer/YYAsyncLayer/YYAsyncLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYAsyncLayer.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/11. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | #if __has_include() 16 | FOUNDATION_EXPORT double YYAsyncLayerVersionNumber; 17 | FOUNDATION_EXPORT const unsigned char YYAsyncLayerVersionString[]; 18 | #import 19 | #import 20 | #else 21 | #import "YYSentinel.h" 22 | #import "YYTransaction.h" 23 | #endif 24 | 25 | @class YYAsyncLayerDisplayTask; 26 | 27 | NS_ASSUME_NONNULL_BEGIN 28 | 29 | /** 30 | The YYAsyncLayer class is a subclass of CALayer used for render contents asynchronously. 31 | 32 | @discussion When the layer need update it's contents, it will ask the delegate 33 | for a async display task to render the contents in a background queue. 34 | */ 35 | @interface YYAsyncLayer : CALayer 36 | /// Whether the render code is executed in background. Default is YES. 37 | @property BOOL displaysAsynchronously; 38 | @end 39 | 40 | 41 | /** 42 | The YYAsyncLayer's delegate protocol. The delegate of the YYAsyncLayer (typically a UIView) 43 | must implements the method in this protocol. 44 | */ 45 | @protocol YYAsyncLayerDelegate 46 | @required 47 | /// This method is called to return a new display task when the layer's contents need update. 48 | - (YYAsyncLayerDisplayTask *)newAsyncDisplayTask; 49 | @end 50 | 51 | 52 | /** 53 | A display task used by YYAsyncLayer to render the contents in background queue. 54 | */ 55 | @interface YYAsyncLayerDisplayTask : NSObject 56 | 57 | /** 58 | This block will be called before the asynchronous drawing begins. 59 | It will be called on the main thread. 60 | 61 | @param layer The layer. 62 | */ 63 | @property (nullable, nonatomic, copy) void (^willDisplay)(CALayer *layer); 64 | 65 | /** 66 | This block is called to draw the layer's contents. 67 | 68 | @discussion This block may be called on main thread or background thread, 69 | so is should be thread-safe. 70 | 71 | @param context A new bitmap content created by layer. 72 | @param size The content size (typically same as layer's bound size). 73 | @param isCancelled If this block returns `YES`, the method should cancel the 74 | drawing process and return as quickly as possible. 75 | */ 76 | @property (nullable, nonatomic, copy) void (^display)(CGContextRef context, CGSize size, BOOL(^isCancelled)(void)); 77 | 78 | /** 79 | This block will be called after the asynchronous drawing finished. 80 | It will be called on the main thread. 81 | 82 | @param layer The layer. 83 | @param finished If the draw process is cancelled, it's `NO`, otherwise it's `YES`; 84 | */ 85 | @property (nullable, nonatomic, copy) void (^didDisplay)(CALayer *layer, BOOL finished); 86 | 87 | @end 88 | 89 | NS_ASSUME_NONNULL_END 90 | -------------------------------------------------------------------------------- /Pods/YYAsyncLayer/YYAsyncLayer/YYSentinel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYSentinel.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/13. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | YYSentinel is a thread safe incrementing counter. 18 | It may be used in some multi-threaded situation. 19 | */ 20 | @interface YYSentinel : NSObject 21 | 22 | /// Returns the current value of the counter. 23 | @property (readonly) int32_t value; 24 | 25 | /// Increase the value atomically. 26 | /// @return The new value. 27 | - (int32_t)increase; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /Pods/YYAsyncLayer/YYAsyncLayer/YYSentinel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYSentinel.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/13. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYSentinel.h" 13 | #import 14 | 15 | @implementation YYSentinel { 16 | int32_t _value; 17 | } 18 | 19 | - (int32_t)value { 20 | return _value; 21 | } 22 | 23 | - (int32_t)increase { 24 | return OSAtomicIncrement32(&_value); 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Pods/YYAsyncLayer/YYAsyncLayer/YYTransaction.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTransaction.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | YYTransaction let you perform a selector once before current runloop sleep. 18 | */ 19 | @interface YYTransaction : NSObject 20 | 21 | /** 22 | Creates and returns a transaction with a specified target and selector. 23 | 24 | @param target A specified target, the target is retained until runloop end. 25 | @param selector A selector for target. 26 | 27 | @return A new transaction, or nil if an error occurs. 28 | */ 29 | + (YYTransaction *)transactionWithTarget:(id)target selector:(SEL)selector; 30 | 31 | /** 32 | Commit the trancaction to main runloop. 33 | 34 | @discussion It will perform the selector on the target once before main runloop's 35 | current loop sleep. If the same transaction (same target and same selector) has 36 | already commit to runloop in this loop, this method do nothing. 37 | */ 38 | - (void)commit; 39 | 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /Pods/YYAsyncLayer/YYAsyncLayer/YYTransaction.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTransaction.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTransaction.h" 13 | 14 | 15 | @interface YYTransaction() 16 | @property (nonatomic, strong) id target; 17 | @property (nonatomic, assign) SEL selector; 18 | @end 19 | 20 | static NSMutableSet *transactionSet = nil; 21 | 22 | static void YYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { 23 | if (transactionSet.count == 0) return; 24 | NSSet *currentSet = transactionSet; 25 | transactionSet = [NSMutableSet new]; 26 | [currentSet enumerateObjectsUsingBlock:^(YYTransaction *transaction, BOOL *stop) { 27 | #pragma clang diagnostic push 28 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 29 | [transaction.target performSelector:transaction.selector]; 30 | #pragma clang diagnostic pop 31 | }]; 32 | } 33 | 34 | static void YYTransactionSetup() { 35 | static dispatch_once_t onceToken; 36 | dispatch_once(&onceToken, ^{ 37 | transactionSet = [NSMutableSet new]; 38 | CFRunLoopRef runloop = CFRunLoopGetMain(); 39 | CFRunLoopObserverRef observer; 40 | 41 | observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(), 42 | kCFRunLoopBeforeWaiting | kCFRunLoopExit, 43 | true, // repeat 44 | 0xFFFFFF, // after CATransaction(2000000) 45 | YYRunLoopObserverCallBack, NULL); 46 | CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes); 47 | CFRelease(observer); 48 | }); 49 | } 50 | 51 | 52 | @implementation YYTransaction 53 | 54 | + (YYTransaction *)transactionWithTarget:(id)target selector:(SEL)selector{ 55 | if (!target || !selector) return nil; 56 | YYTransaction *t = [YYTransaction new]; 57 | t.target = target; 58 | t.selector = selector; 59 | return t; 60 | } 61 | 62 | - (void)commit { 63 | if (!_target || !_selector) return; 64 | YYTransactionSetup(); 65 | [transactionSet addObject:self]; 66 | } 67 | 68 | - (NSUInteger)hash { 69 | long v1 = (long)((void *)_selector); 70 | long v2 = (long)_target; 71 | return v1 ^ v2; 72 | } 73 | 74 | - (BOOL)isEqual:(id)object { 75 | if (self == object) return YES; 76 | if (![object isMemberOfClass:self.class]) return NO; 77 | YYTransaction *other = object; 78 | return other.selector == _selector && other.target == _target; 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GRichLabel 2 | A rich Label that supports selection, copying. 3 | 4 | 5 | Features 6 | ============== 7 | - 1,基于coretext绘制文本.内部使用YYAsyncLayer提供异步绘制任务 8 | - 2,支持选择/复制文本. 9 | - 3,支持自定义表情排版 10 | - 4,可自定义文本选择弹出menuView [GMenuContoller](https://github.com/GIKICoder/GMenuController) 11 | - 5,对于文本token 采用多模匹配算法. 详见[GMatcherExpression](https://github.com/GIKICoder/GRichLabel/tree/master/GRichLabel/GMatching).文本匹配效率比系统字符串匹配效率提高百倍. 12 | - 6,功能以及代码还在更新完善中. 后期会支持更多功能.欢迎star. 13 | - 7,支持文本在滚动视图中做文本滚动选择 (微信聊天气泡文本选择) 14 | 15 | 16 | 17 | Usage 18 | ============== 19 | 20 | ### 1 21 | ```objc 22 |  GRichLabel *richLabel = [GRichLabel new]; 23 | richLabel.frame = CGRectMake(5, 0, self.view.frame.size.width-10, 500); 24 | richLabel.text = @"xxxxxx"; 25 | ``` 26 | ### 2 27 | ```objc 28 | { 29 | self.tokens = @[@"@冯科",@"@王金yu",@"@巩柯",@"#郭天池#"]; 30 | GRichLabel *richLabel = [GRichLabel new]; 31 | richLabel.frame = CGRectMake(5, 0, self.view.frame.size.width-10, 500); 32 | } 33 | 34 | - (void)setRichText 35 | { 36 | __weak typeof(self) ws = self; 37 | NSString* string =[self content1]; 38 | __block NSMutableArray *tokens = [NSMutableArray array]; 39 | [self.tokens enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 40 | if (!obj) *stop = NO; 41 | GAttributedToken * token = [GAttributedToken attributedTextToken:obj]; 42 | token.tokenClickBlock = ^(GAttributedToken *token) { 43 | [ws gotoVC:token.textToken]; 44 | }; 45 | [tokens addObject:token]; 46 | }]; 47 | GAttributedToken *regex = [GAttributedToken new]; 48 | regex.regexToken = Phone; 49 | regex.tokenClickBlock = ^(GAttributedToken *token) { 50 | 51 | }; 52 | GAttributedStringLayout *layout = [GAttributedStringLayout attributedLayout:string]; 53 | layout.tokenPatternConfigs = tokens.copy; 54 | layout.regexPatternConifgs = @[regex]; 55 | layout.textAlignment = kCTTextAlignmentJustified; 56 | layout.linespace = 1; 57 | layout.lineIndent = 2; 58 | layout.font = [UIFont systemFontOfSize:14]; 59 | 60 | NSMutableAttributedString * truncation = [[NSMutableAttributedString alloc] initWithString:@"...全文"]; 61 | 62 | layout.truncationToken = truncation; 63 | GDrawTextBuilder * builder = [GAttributedStringFactory createDrawTextBuilderWithLayout:layout boundSize:self.richLabel.frame.size]; 64 | self.richLabel.textBuilder = builder; 65 | 66 | } 67 | 68 | ``` 69 | 70 | Installation 71 | ============== 72 | 73 | - 手动添加 74 | 1. ` git clone https://github.com/GIKICoder/GRichLabel.git ` 75 | 2. 选择`GRichLabel`文件夹.拖入项目中. 76 | 3. `GRichLabel` 依赖 `GMenuController https://github.com/GIKICoder/GMenuController`,  `YYAsyncLayer https://github.com/ibireme/YYAsyncLayer` 这2个库. 可以使用pod添加. 也可下载手动添加到项目 77 | 78 | update Info 79 | ============== 80 | - 29/10/2017 代码重构.处理耦合逻辑.简化代码.重新整理NSAttributedString分类(参考[NSAttributedString+YYText](https://github.com/ibireme/YYText)).使GRichLabel具有更强大的文本处理能力. 81 | - 06/12/2017 增加kGAttributeTokenReplaceStringName. 支持自定义表情 链接等字符串copy 处理. 82 | - 19/04/2018 增加GMatherExpression 增加字符匹配模式. 83 | - 27/08/2018 增加文本选择模式,在滚动视图下可支持滚动文本选择。 84 | 85 | screenshot 86 | ============== 87 | 88 | [![Alt][screenshot1_thumb]]   [![Alt][screenshot2_thumb]]    [![Alt][screenshot3_thumb]] 89 | 90 | [screenshot1_thumb]: https://github.com/GIKICoder/GRichLabel/blob/master/screenshot/001.png 91 | [screenshot2_thumb]: https://github.com/GIKICoder/GRichLabel/blob/master/screenshot/002.png 92 | [screenshot3_thumb]: https://github.com/GIKICoder/GRichLabel/blob/master/screenshot/003.png 93 | -------------------------------------------------------------------------------- /screenshot/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/screenshot/001.png -------------------------------------------------------------------------------- /screenshot/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/screenshot/002.png -------------------------------------------------------------------------------- /screenshot/003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GIKICoder/GRichLabel/8c4edeb0599d8210b00fe48597890018f1229e23/screenshot/003.png --------------------------------------------------------------------------------