wangmeng 2 rokov pred
rodič
commit
bed5bc70b1
100 zmenil súbory, kde vykonal 16145 pridanie a 2562 odobranie
  1. 2 0
      Podfile
  2. 5 1
      Podfile.lock
  3. 1 0
      Pods/Headers/Private/YYText/NSAttributedString+YYText.h
  4. 1 0
      Pods/Headers/Private/YYText/NSParagraphStyle+YYText.h
  5. 1 0
      Pods/Headers/Private/YYText/UIPasteboard+YYText.h
  6. 1 0
      Pods/Headers/Private/YYText/UIView+YYText.h
  7. 1 0
      Pods/Headers/Private/YYText/YYLabel.h
  8. 1 0
      Pods/Headers/Private/YYText/YYText.h
  9. 1 0
      Pods/Headers/Private/YYText/YYTextArchiver.h
  10. 1 0
      Pods/Headers/Private/YYText/YYTextAsyncLayer.h
  11. 1 0
      Pods/Headers/Private/YYText/YYTextAttribute.h
  12. 1 0
      Pods/Headers/Private/YYText/YYTextContainerView.h
  13. 1 0
      Pods/Headers/Private/YYText/YYTextDebugOption.h
  14. 1 0
      Pods/Headers/Private/YYText/YYTextEffectWindow.h
  15. 1 0
      Pods/Headers/Private/YYText/YYTextInput.h
  16. 1 0
      Pods/Headers/Private/YYText/YYTextKeyboardManager.h
  17. 1 0
      Pods/Headers/Private/YYText/YYTextLayout.h
  18. 1 0
      Pods/Headers/Private/YYText/YYTextLine.h
  19. 1 0
      Pods/Headers/Private/YYText/YYTextMagnifier.h
  20. 1 0
      Pods/Headers/Private/YYText/YYTextParser.h
  21. 1 0
      Pods/Headers/Private/YYText/YYTextRubyAnnotation.h
  22. 1 0
      Pods/Headers/Private/YYText/YYTextRunDelegate.h
  23. 1 0
      Pods/Headers/Private/YYText/YYTextSelectionView.h
  24. 1 0
      Pods/Headers/Private/YYText/YYTextTransaction.h
  25. 1 0
      Pods/Headers/Private/YYText/YYTextUtilities.h
  26. 1 0
      Pods/Headers/Private/YYText/YYTextView.h
  27. 1 0
      Pods/Headers/Private/YYText/YYTextWeakProxy.h
  28. 1 0
      Pods/Headers/Public/YYText/NSAttributedString+YYText.h
  29. 1 0
      Pods/Headers/Public/YYText/NSParagraphStyle+YYText.h
  30. 1 0
      Pods/Headers/Public/YYText/UIPasteboard+YYText.h
  31. 1 0
      Pods/Headers/Public/YYText/UIView+YYText.h
  32. 1 0
      Pods/Headers/Public/YYText/YYLabel.h
  33. 1 0
      Pods/Headers/Public/YYText/YYText.h
  34. 1 0
      Pods/Headers/Public/YYText/YYTextArchiver.h
  35. 1 0
      Pods/Headers/Public/YYText/YYTextAsyncLayer.h
  36. 1 0
      Pods/Headers/Public/YYText/YYTextAttribute.h
  37. 1 0
      Pods/Headers/Public/YYText/YYTextContainerView.h
  38. 1 0
      Pods/Headers/Public/YYText/YYTextDebugOption.h
  39. 1 0
      Pods/Headers/Public/YYText/YYTextEffectWindow.h
  40. 1 0
      Pods/Headers/Public/YYText/YYTextInput.h
  41. 1 0
      Pods/Headers/Public/YYText/YYTextKeyboardManager.h
  42. 1 0
      Pods/Headers/Public/YYText/YYTextLayout.h
  43. 1 0
      Pods/Headers/Public/YYText/YYTextLine.h
  44. 1 0
      Pods/Headers/Public/YYText/YYTextMagnifier.h
  45. 1 0
      Pods/Headers/Public/YYText/YYTextParser.h
  46. 1 0
      Pods/Headers/Public/YYText/YYTextRubyAnnotation.h
  47. 1 0
      Pods/Headers/Public/YYText/YYTextRunDelegate.h
  48. 1 0
      Pods/Headers/Public/YYText/YYTextSelectionView.h
  49. 1 0
      Pods/Headers/Public/YYText/YYTextTransaction.h
  50. 1 0
      Pods/Headers/Public/YYText/YYTextUtilities.h
  51. 1 0
      Pods/Headers/Public/YYText/YYTextView.h
  52. 1 0
      Pods/Headers/Public/YYText/YYTextWeakProxy.h
  53. 5 1
      Pods/Manifest.lock
  54. 2887 2554
      Pods/Pods.xcodeproj/project.pbxproj
  55. 58 0
      Pods/Pods.xcodeproj/xcuserdata/wangmeng.xcuserdatad/xcschemes/YYText.xcscheme
  56. 7 0
      Pods/Pods.xcodeproj/xcuserdata/wangmeng.xcuserdatad/xcschemes/xcschememanagement.plist
  57. 26 0
      Pods/Target Support Files/Pods-WMBase/Pods-WMBase-acknowledgements.markdown
  58. 32 0
      Pods/Target Support Files/Pods-WMBase/Pods-WMBase-acknowledgements.plist
  59. 3 3
      Pods/Target Support Files/Pods-WMBase/Pods-WMBase.debug.xcconfig
  60. 3 3
      Pods/Target Support Files/Pods-WMBase/Pods-WMBase.release.xcconfig
  61. 5 0
      Pods/Target Support Files/YYText/YYText-dummy.m
  62. 12 0
      Pods/Target Support Files/YYText/YYText-prefix.pch
  63. 13 0
      Pods/Target Support Files/YYText/YYText.debug.xcconfig
  64. 13 0
      Pods/Target Support Files/YYText/YYText.release.xcconfig
  65. 22 0
      Pods/YYText/LICENSE
  66. 1095 0
      Pods/YYText/README.md
  67. 55 0
      Pods/YYText/YYText/Component/YYTextContainerView.h
  68. 144 0
      Pods/YYText/YYText/Component/YYTextContainerView.m
  69. 95 0
      Pods/YYText/YYText/Component/YYTextDebugOption.h
  70. 140 0
      Pods/YYText/YYText/Component/YYTextDebugOption.m
  71. 52 0
      Pods/YYText/YYText/Component/YYTextEffectWindow.h
  72. 429 0
      Pods/YYText/YYText/Component/YYTextEffectWindow.m
  73. 87 0
      Pods/YYText/YYText/Component/YYTextInput.h
  74. 152 0
      Pods/YYText/YYText/Component/YYTextInput.m
  75. 98 0
      Pods/YYText/YYText/Component/YYTextKeyboardManager.h
  76. 521 0
      Pods/YYText/YYText/Component/YYTextKeyboardManager.m
  77. 571 0
      Pods/YYText/YYText/Component/YYTextLayout.h
  78. 3407 0
      Pods/YYText/YYText/Component/YYTextLayout.m
  79. 84 0
      Pods/YYText/YYText/Component/YYTextLine.h
  80. 167 0
      Pods/YYText/YYText/Component/YYTextLine.m
  81. 52 0
      Pods/YYText/YYText/Component/YYTextMagnifier.h
  82. 355 0
      Pods/YYText/YYText/Component/YYTextMagnifier.m
  83. 78 0
      Pods/YYText/YYText/Component/YYTextSelectionView.h
  84. 329 0
      Pods/YYText/YYText/Component/YYTextSelectionView.m
  85. 33 0
      Pods/YYText/YYText/String/YYTextArchiver.h
  86. 252 0
      Pods/YYText/YYText/String/YYTextArchiver.m
  87. 347 0
      Pods/YYText/YYText/String/YYTextAttribute.h
  88. 524 0
      Pods/YYText/YYText/String/YYTextAttribute.m
  89. 91 0
      Pods/YYText/YYText/String/YYTextParser.h
  90. 417 0
      Pods/YYText/YYText/String/YYTextParser.m
  91. 78 0
      Pods/YYText/YYText/String/YYTextRubyAnnotation.h
  92. 83 0
      Pods/YYText/YYText/String/YYTextRubyAnnotation.m
  93. 68 0
      Pods/YYText/YYText/String/YYTextRunDelegate.h
  94. 71 0
      Pods/YYText/YYText/String/YYTextRunDelegate.m
  95. 1415 0
      Pods/YYText/YYText/Utility/NSAttributedString+YYText.h
  96. 1404 0
      Pods/YYText/YYText/Utility/NSAttributedString+YYText.m
  97. 37 0
      Pods/YYText/YYText/Utility/NSParagraphStyle+YYText.h
  98. 230 0
      Pods/YYText/YYText/Utility/NSParagraphStyle+YYText.m
  99. 41 0
      Pods/YYText/YYText/Utility/UIPasteboard+YYText.h
  100. 0 0
      Pods/YYText/YYText/Utility/UIPasteboard+YYText.m

+ 2 - 0
Podfile

@@ -11,12 +11,14 @@ target 'WMBase' do
   pod 'MJRefresh'
   pod 'MJExtension'
   pod 'YYCache'
+  pod 'YYText'
   pod 'MBProgressHUD'
   pod 'SDWebImage'
   pod 'YTKNetwork'
   pod 'QMUIKit'
   pod 'Toast'
   pod 'CTMediator'
+  pod 'BRPickerView'
 end
 
 

+ 5 - 1
Podfile.lock

@@ -322,6 +322,7 @@ PODS:
   - YTKNetwork (3.0.6):
     - AFNetworking/NSURLSession (~> 4.0)
   - YYCache (1.0.4)
+  - YYText (1.0.7)
 
 DEPENDENCIES:
   - AFNetworking
@@ -335,6 +336,7 @@ DEPENDENCIES:
   - Toast
   - YTKNetwork
   - YYCache
+  - YYText
 
 SPEC REPOS:
   https://github.com/CocoaPods/Specs.git:
@@ -349,6 +351,7 @@ SPEC REPOS:
     - Toast
     - YTKNetwork
     - YYCache
+    - YYText
 
 SPEC CHECKSUMS:
   AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
@@ -362,7 +365,8 @@ SPEC CHECKSUMS:
   Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
   YTKNetwork: c16be90b06be003de9e9cd0d3b187cc8eaf35c04
   YYCache: 8105b6638f5e849296c71f331ff83891a4942952
+  YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
 
-PODFILE CHECKSUM: 31928e55b25def4c54c489c2b4c6da03be8aad62
+PODFILE CHECKSUM: bdbcc3f9982294ade075df44bf90f021e897ccd5
 
 COCOAPODS: 1.12.1

+ 1 - 0
Pods/Headers/Private/YYText/NSAttributedString+YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/NSAttributedString+YYText.h

+ 1 - 0
Pods/Headers/Private/YYText/NSParagraphStyle+YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/NSParagraphStyle+YYText.h

+ 1 - 0
Pods/Headers/Private/YYText/UIPasteboard+YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/UIPasteboard+YYText.h

+ 1 - 0
Pods/Headers/Private/YYText/UIView+YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/UIView+YYText.h

+ 1 - 0
Pods/Headers/Private/YYText/YYLabel.h

@@ -0,0 +1 @@
+../../../YYText/YYText/YYLabel.h

+ 1 - 0
Pods/Headers/Private/YYText/YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/YYText.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextArchiver.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextArchiver.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextAsyncLayer.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/YYTextAsyncLayer.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextAttribute.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextAttribute.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextContainerView.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextContainerView.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextDebugOption.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextDebugOption.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextEffectWindow.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextEffectWindow.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextInput.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextInput.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextKeyboardManager.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextKeyboardManager.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextLayout.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextLayout.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextLine.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextLine.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextMagnifier.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextMagnifier.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextParser.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextParser.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextRubyAnnotation.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextRubyAnnotation.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextRunDelegate.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextRunDelegate.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextSelectionView.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextSelectionView.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextTransaction.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/YYTextTransaction.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextUtilities.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/YYTextUtilities.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextView.h

@@ -0,0 +1 @@
+../../../YYText/YYText/YYTextView.h

+ 1 - 0
Pods/Headers/Private/YYText/YYTextWeakProxy.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/YYTextWeakProxy.h

+ 1 - 0
Pods/Headers/Public/YYText/NSAttributedString+YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/NSAttributedString+YYText.h

+ 1 - 0
Pods/Headers/Public/YYText/NSParagraphStyle+YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/NSParagraphStyle+YYText.h

+ 1 - 0
Pods/Headers/Public/YYText/UIPasteboard+YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/UIPasteboard+YYText.h

+ 1 - 0
Pods/Headers/Public/YYText/UIView+YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/UIView+YYText.h

+ 1 - 0
Pods/Headers/Public/YYText/YYLabel.h

@@ -0,0 +1 @@
+../../../YYText/YYText/YYLabel.h

+ 1 - 0
Pods/Headers/Public/YYText/YYText.h

@@ -0,0 +1 @@
+../../../YYText/YYText/YYText.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextArchiver.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextArchiver.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextAsyncLayer.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/YYTextAsyncLayer.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextAttribute.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextAttribute.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextContainerView.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextContainerView.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextDebugOption.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextDebugOption.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextEffectWindow.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextEffectWindow.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextInput.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextInput.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextKeyboardManager.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextKeyboardManager.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextLayout.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextLayout.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextLine.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextLine.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextMagnifier.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextMagnifier.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextParser.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextParser.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextRubyAnnotation.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextRubyAnnotation.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextRunDelegate.h

@@ -0,0 +1 @@
+../../../YYText/YYText/String/YYTextRunDelegate.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextSelectionView.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Component/YYTextSelectionView.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextTransaction.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/YYTextTransaction.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextUtilities.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/YYTextUtilities.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextView.h

@@ -0,0 +1 @@
+../../../YYText/YYText/YYTextView.h

+ 1 - 0
Pods/Headers/Public/YYText/YYTextWeakProxy.h

@@ -0,0 +1 @@
+../../../YYText/YYText/Utility/YYTextWeakProxy.h

+ 5 - 1
Pods/Manifest.lock

@@ -322,6 +322,7 @@ PODS:
   - YTKNetwork (3.0.6):
     - AFNetworking/NSURLSession (~> 4.0)
   - YYCache (1.0.4)
+  - YYText (1.0.7)
 
 DEPENDENCIES:
   - AFNetworking
@@ -335,6 +336,7 @@ DEPENDENCIES:
   - Toast
   - YTKNetwork
   - YYCache
+  - YYText
 
 SPEC REPOS:
   https://github.com/CocoaPods/Specs.git:
@@ -349,6 +351,7 @@ SPEC REPOS:
     - Toast
     - YTKNetwork
     - YYCache
+    - YYText
 
 SPEC CHECKSUMS:
   AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
@@ -362,7 +365,8 @@ SPEC CHECKSUMS:
   Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
   YTKNetwork: c16be90b06be003de9e9cd0d3b187cc8eaf35c04
   YYCache: 8105b6638f5e849296c71f331ff83891a4942952
+  YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
 
-PODFILE CHECKSUM: 31928e55b25def4c54c489c2b4c6da03be8aad62
+PODFILE CHECKSUM: bdbcc3f9982294ade075df44bf90f021e897ccd5
 
 COCOAPODS: 1.12.1

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 2887 - 2554
Pods/Pods.xcodeproj/project.pbxproj


+ 58 - 0
Pods/Pods.xcodeproj/xcuserdata/wangmeng.xcuserdatad/xcschemes/YYText.xcscheme

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1240"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "6F5C17E80BD1B7F60A33F79692330A57"
+               BuildableName = "libYYText.a"
+               BlueprintName = "YYText"
+               ReferencedContainer = "container:Pods.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 7 - 0
Pods/Pods.xcodeproj/xcuserdata/wangmeng.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -95,6 +95,13 @@
 			<key>orderHint</key>
 			<integer>12</integer>
 		</dict>
+		<key>YYText.xcscheme</key>
+		<dict>
+			<key>isShown</key>
+			<false/>
+			<key>orderHint</key>
+			<integer>13</integer>
+		</dict>
 	</dict>
 	<key>SuppressBuildableAutocreation</key>
 	<dict/>

+ 26 - 0
Pods/Target Support Files/Pods-WMBase/Pods-WMBase-acknowledgements.markdown

@@ -234,4 +234,30 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 
 
+
+## YYText
+
+The MIT License (MIT)
+
+Copyright (c) 2015 ibireme <ibireme@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
 Generated by CocoaPods - https://cocoapods.org

+ 32 - 0
Pods/Target Support Files/Pods-WMBase/Pods-WMBase-acknowledgements.plist

@@ -312,6 +312,38 @@ SOFTWARE.
 			<key>Type</key>
 			<string>PSGroupSpecifier</string>
 		</dict>
+		<dict>
+			<key>FooterText</key>
+			<string>The MIT License (MIT)
+
+Copyright (c) 2015 ibireme &lt;ibireme@gmail.com&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+</string>
+			<key>License</key>
+			<string>MIT</string>
+			<key>Title</key>
+			<string>YYText</string>
+			<key>Type</key>
+			<string>PSGroupSpecifier</string>
+		</dict>
 		<dict>
 			<key>FooterText</key>
 			<string>Generated by CocoaPods - https://cocoapods.org</string>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3 - 3
Pods/Target Support Files/Pods-WMBase/Pods-WMBase.debug.xcconfig


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3 - 3
Pods/Target Support Files/Pods-WMBase/Pods-WMBase.release.xcconfig


+ 5 - 0
Pods/Target Support Files/YYText/YYText-dummy.m

@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+@interface PodsDummy_YYText : NSObject
+@end
+@implementation PodsDummy_YYText
+@end

+ 12 - 0
Pods/Target Support Files/YYText/YYText-prefix.pch

@@ -0,0 +1,12 @@
+#ifdef __OBJC__
+#import <UIKit/UIKit.h>
+#else
+#ifndef FOUNDATION_EXPORT
+#if defined(__cplusplus)
+#define FOUNDATION_EXPORT extern "C"
+#else
+#define FOUNDATION_EXPORT extern
+#endif
+#endif
+#endif
+

+ 13 - 0
Pods/Target Support Files/YYText/YYText.debug.xcconfig

@@ -0,0 +1,13 @@
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYText
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/YYText" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YYText"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYText
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 13 - 0
Pods/Target Support Files/YYText/YYText.release.xcconfig

@@ -0,0 +1,13 @@
+CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
+CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYText
+GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
+HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/YYText" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YYText"
+PODS_BUILD_DIR = ${BUILD_DIR}
+PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
+PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
+PODS_ROOT = ${SRCROOT}
+PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYText
+PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
+PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
+SKIP_INSTALL = YES
+USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

+ 22 - 0
Pods/YYText/LICENSE

@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 ibireme <ibireme@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1095 - 0
Pods/YYText/README.md


+ 55 - 0
Pods/YYText/YYText/Component/YYTextContainerView.h

@@ -0,0 +1,55 @@
+//
+//  YYTextContainerView.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/21.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+#if __has_include(<YYText/YYText.h>)
+#import <YYText/YYTextLayout.h>
+#else
+#import "YYTextLayout.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A simple view to diaplay `YYTextLayout`.
+
+ @discussion This view can become first responder. If this view is first responder,
+ all the action (such as UIMenu's action) would forward to the `hostView` property.
+ Typically, you should not use this class directly.
+ 
+ @warning All the methods in this class should be called on main thread.
+ */
+@interface YYTextContainerView : UIView
+
+/// First responder's aciton will forward to this view.
+@property (nullable, nonatomic, weak) UIView *hostView;
+
+/// Debug option for layout debug. Set this property will let the view redraw it's contents.
+@property (nullable, nonatomic, copy) YYTextDebugOption *debugOption;
+
+/// Text vertical alignment.
+@property (nonatomic) YYTextVerticalAlignment textVerticalAlignment;
+
+/// Text layout. Set this property will let the view redraw it's contents.
+@property (nullable, nonatomic, strong) YYTextLayout *layout;
+
+/// The contents fade animation duration when the layout's contents changed. Default is 0 (no animation).
+@property (nonatomic) NSTimeInterval contentsFadeDuration;
+
+/// Convenience method to set `layout` and `contentsFadeDuration`.
+/// @param layout  Same as `layout` property.
+/// @param fadeDuration  Same as `contentsFadeDuration` property.
+- (void)setLayout:(nullable YYTextLayout *)layout withFadeDuration:(NSTimeInterval)fadeDuration;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 144 - 0
Pods/YYText/YYText/Component/YYTextContainerView.m

@@ -0,0 +1,144 @@
+//
+//  YYTextContainerView.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/21.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextContainerView.h"
+
+@implementation YYTextContainerView {
+    BOOL _attachmentChanged;
+    NSMutableArray *_attachmentViews;
+    NSMutableArray *_attachmentLayers;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (!self) return nil;
+    self.backgroundColor = [UIColor clearColor];
+    _attachmentViews = [NSMutableArray array];
+    _attachmentLayers = [NSMutableArray array];
+    return self;
+}
+
+- (void)setDebugOption:(YYTextDebugOption *)debugOption {
+    BOOL needDraw = _debugOption.needDrawDebug;
+    _debugOption = debugOption.copy;
+    if (_debugOption.needDrawDebug != needDraw) {
+        [self setNeedsDisplay];
+    }
+}
+
+- (void)setTextVerticalAlignment:(YYTextVerticalAlignment)textVerticalAlignment {
+    if (_textVerticalAlignment == textVerticalAlignment) return;
+    _textVerticalAlignment = textVerticalAlignment;
+    [self setNeedsDisplay];
+}
+
+- (void)setContentsFadeDuration:(NSTimeInterval)contentsFadeDuration {
+    if (_contentsFadeDuration == contentsFadeDuration) return;
+    _contentsFadeDuration = contentsFadeDuration;
+    if (contentsFadeDuration <= 0) {
+        [self.layer removeAnimationForKey:@"contents"];
+    }
+}
+
+- (void)setLayout:(YYTextLayout *)layout {
+    if (_layout == layout) return;
+    _layout = layout;
+    _attachmentChanged = YES;
+    [self setNeedsDisplay];
+}
+
+- (void)setLayout:(YYTextLayout *)layout withFadeDuration:(NSTimeInterval)fadeDuration {
+    self.contentsFadeDuration = fadeDuration;
+    self.layout = layout;
+}
+
+- (void)drawRect:(CGRect)rect {
+    // fade content
+    [self.layer removeAnimationForKey:@"contents"];
+    if (_contentsFadeDuration > 0) {
+        CATransition *transition = [CATransition animation];
+        transition.duration = _contentsFadeDuration;
+        transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
+        transition.type = kCATransitionFade;
+        [self.layer addAnimation:transition forKey:@"contents"];
+    }
+    
+    // update attachment
+    if (_attachmentChanged) {
+        for (UIView *view in _attachmentViews) {
+            if (view.superview == self) [view removeFromSuperview];
+        }
+        for (CALayer *layer in _attachmentLayers) {
+            if (layer.superlayer == self.layer) [layer removeFromSuperlayer];
+        }
+        [_attachmentViews removeAllObjects];
+        [_attachmentLayers removeAllObjects];
+    }
+    
+    // draw layout
+    CGSize boundingSize = _layout.textBoundingSize;
+    CGPoint point = CGPointZero;
+    if (_textVerticalAlignment == YYTextVerticalAlignmentCenter) {
+        if (_layout.container.isVerticalForm) {
+            point.x = -(self.bounds.size.width - boundingSize.width) * 0.5;
+        } else {
+            point.y = (self.bounds.size.height - boundingSize.height) * 0.5;
+        }
+    } else if (_textVerticalAlignment == YYTextVerticalAlignmentBottom) {
+        if (_layout.container.isVerticalForm) {
+            point.x = -(self.bounds.size.width - boundingSize.width);
+        } else {
+            point.y = (self.bounds.size.height - boundingSize.height);
+        }
+    }
+    [_layout drawInContext:UIGraphicsGetCurrentContext() size:self.bounds.size point:point view:self layer:self.layer debug:_debugOption cancel:nil];
+    
+    // update attachment
+    if (_attachmentChanged) {
+        _attachmentChanged = NO;
+        for (YYTextAttachment *a in _layout.attachments) {
+            if ([a.content isKindOfClass:[UIView class]]) [_attachmentViews addObject:a.content];
+            if ([a.content isKindOfClass:[CALayer class]]) [_attachmentLayers addObject:a.content];
+        }
+    }
+}
+
+- (void)setFrame:(CGRect)frame {
+    CGSize oldSize = self.bounds.size;
+    [super setFrame:frame];
+    if (!CGSizeEqualToSize(oldSize, self.bounds.size)) {
+        [self setNeedsLayout];
+    }
+}
+
+- (void)setBounds:(CGRect)bounds {
+    CGSize oldSize = self.bounds.size;
+    [super setBounds:bounds];
+    if (!CGSizeEqualToSize(oldSize, self.bounds.size)) {
+        [self setNeedsLayout];
+    }
+}
+
+#pragma mark - UIResponder forward
+
+- (BOOL)canBecomeFirstResponder {
+    return YES;
+}
+
+- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
+    return [self.hostView canPerformAction:action withSender:sender];
+}
+
+- (id)forwardingTargetForSelector:(SEL)aSelector {
+    return self.hostView;
+}
+
+@end

+ 95 - 0
Pods/YYText/YYText/Component/YYTextDebugOption.h

@@ -0,0 +1,95 @@
+//
+//  YYTextDebugOption.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/8.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+@class YYTextDebugOption;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The YYTextDebugTarget protocol defines the method a debug target should implement.
+ A debug target can be add to the global container to receive the shared debug
+ option changed notification.
+ */
+@protocol YYTextDebugTarget <NSObject>
+
+@required
+/**
+ When the shared debug option changed, this method would be called on main thread.
+ It should return as quickly as possible. The option's property should not be changed
+ in this method.
+ 
+ @param option  The shared debug option.
+ */
+- (void)setDebugOption:(nullable YYTextDebugOption *)option;
+@end
+
+
+
+/**
+ The debug option for YYText.
+ */
+@interface YYTextDebugOption : NSObject <NSCopying>
+@property (nullable, nonatomic, strong) UIColor *baselineColor;      ///< baseline color
+@property (nullable, nonatomic, strong) UIColor *CTFrameBorderColor; ///< CTFrame path border color
+@property (nullable, nonatomic, strong) UIColor *CTFrameFillColor;   ///< CTFrame path fill color
+@property (nullable, nonatomic, strong) UIColor *CTLineBorderColor;  ///< CTLine bounds border color
+@property (nullable, nonatomic, strong) UIColor *CTLineFillColor;    ///< CTLine bounds fill color
+@property (nullable, nonatomic, strong) UIColor *CTLineNumberColor;  ///< CTLine line number color
+@property (nullable, nonatomic, strong) UIColor *CTRunBorderColor;   ///< CTRun bounds border color
+@property (nullable, nonatomic, strong) UIColor *CTRunFillColor;     ///< CTRun bounds fill color
+@property (nullable, nonatomic, strong) UIColor *CTRunNumberColor;   ///< CTRun number color
+@property (nullable, nonatomic, strong) UIColor *CGGlyphBorderColor; ///< CGGlyph bounds border color
+@property (nullable, nonatomic, strong) UIColor *CGGlyphFillColor;   ///< CGGlyph bounds fill color
+
+- (BOOL)needDrawDebug; ///< `YES`: at least one debug color is visible. `NO`: all debug color is invisible/nil.
+- (void)clear; ///< Set all debug color to nil.
+
+/**
+ Add a debug target.
+ 
+ @discussion When `setSharedDebugOption:` is called, all added debug target will 
+ receive `setDebugOption:` in main thread. It maintains an unsafe_unretained
+ reference to this target. The target must to removed before dealloc.
+ 
+ @param target A debug target.
+ */
++ (void)addDebugTarget:(id<YYTextDebugTarget>)target;
+
+/**
+ Remove a debug target which is added by `addDebugTarget:`.
+ 
+ @param target A debug target.
+ */
++ (void)removeDebugTarget:(id<YYTextDebugTarget>)target;
+
+/**
+ Returns the shared debug option.
+ 
+ @return The shared debug option, default is nil.
+ */
++ (nullable YYTextDebugOption *)sharedDebugOption;
+
+/**
+ Set a debug option as shared debug option.
+ This method must be called on main thread.
+ 
+ @discussion When call this method, the new option will set to all debug target
+ which is added by `addDebugTarget:`.
+ 
+ @param option  A new debug option (nil is valid).
+ */
++ (void)setSharedDebugOption:(nullable YYTextDebugOption *)option;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 140 - 0
Pods/YYText/YYText/Component/YYTextDebugOption.m

@@ -0,0 +1,140 @@
+//
+//  YYTextDebugOption.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/8.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextDebugOption.h"
+#import "YYTextWeakProxy.h"
+#import <libkern/OSAtomic.h>
+#import <pthread.h>
+
+static pthread_mutex_t _sharedDebugLock;
+static CFMutableSetRef _sharedDebugTargets = nil;
+static YYTextDebugOption *_sharedDebugOption = nil;
+
+static const void* _sharedDebugSetRetain(CFAllocatorRef allocator, const void *value) {
+    return value;
+}
+
+static void _sharedDebugSetRelease(CFAllocatorRef allocator, const void *value) {
+}
+
+void _sharedDebugSetFunction(const void *value, void *context) {
+    id<YYTextDebugTarget> target = (__bridge id<YYTextDebugTarget>)(value);
+    [target setDebugOption:_sharedDebugOption];
+}
+
+static void _initSharedDebug() {
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        pthread_mutex_init(&_sharedDebugLock, NULL);
+        CFSetCallBacks callbacks = kCFTypeSetCallBacks;
+        callbacks.retain = _sharedDebugSetRetain;
+        callbacks.release = _sharedDebugSetRelease;
+        _sharedDebugTargets = CFSetCreateMutable(CFAllocatorGetDefault(), 0, &callbacks);
+    });
+}
+
+static void _setSharedDebugOption(YYTextDebugOption *option) {
+    _initSharedDebug();
+    pthread_mutex_lock(&_sharedDebugLock);
+    _sharedDebugOption = option.copy;
+    CFSetApplyFunction(_sharedDebugTargets, _sharedDebugSetFunction, NULL);
+    pthread_mutex_unlock(&_sharedDebugLock);
+}
+
+static YYTextDebugOption *_getSharedDebugOption() {
+    _initSharedDebug();
+    pthread_mutex_lock(&_sharedDebugLock);
+    YYTextDebugOption *op = _sharedDebugOption;
+    pthread_mutex_unlock(&_sharedDebugLock);
+    return op;
+}
+
+static void _addDebugTarget(id<YYTextDebugTarget> target) {
+    _initSharedDebug();
+    pthread_mutex_lock(&_sharedDebugLock);
+    CFSetAddValue(_sharedDebugTargets, (__bridge const void *)(target));
+    pthread_mutex_unlock(&_sharedDebugLock);
+}
+
+static void _removeDebugTarget(id<YYTextDebugTarget> target) {
+    _initSharedDebug();
+    pthread_mutex_lock(&_sharedDebugLock);
+    CFSetRemoveValue(_sharedDebugTargets, (__bridge const void *)(target));
+    pthread_mutex_unlock(&_sharedDebugLock);
+}
+
+
+@implementation YYTextDebugOption
+
+- (id)copyWithZone:(NSZone *)zone {
+    YYTextDebugOption *op = [self.class new];
+    op.baselineColor = self.baselineColor;
+    op.CTFrameBorderColor = self.CTFrameBorderColor;
+    op.CTFrameFillColor = self.CTFrameFillColor;
+    op.CTLineBorderColor = self.CTLineBorderColor;
+    op.CTLineFillColor = self.CTLineFillColor;
+    op.CTLineNumberColor = self.CTLineNumberColor;
+    op.CTRunBorderColor = self.CTRunBorderColor;
+    op.CTRunFillColor = self.CTRunFillColor;
+    op.CTRunNumberColor = self.CTRunNumberColor;
+    op.CGGlyphBorderColor = self.CGGlyphBorderColor;
+    op.CGGlyphFillColor = self.CGGlyphFillColor;
+    return op;
+}
+
+- (BOOL)needDrawDebug {
+    if (self.baselineColor ||
+        self.CTFrameBorderColor ||
+        self.CTFrameFillColor ||
+        self.CTLineBorderColor ||
+        self.CTLineFillColor ||
+        self.CTLineNumberColor ||
+        self.CTRunBorderColor ||
+        self.CTRunFillColor ||
+        self.CTRunNumberColor ||
+        self.CGGlyphBorderColor ||
+        self.CGGlyphFillColor) return YES;
+    return NO;
+}
+
+- (void)clear {
+    self.baselineColor = nil;
+    self.CTFrameBorderColor = nil;
+    self.CTFrameFillColor = nil;
+    self.CTLineBorderColor = nil;
+    self.CTLineFillColor = nil;
+    self.CTLineNumberColor = nil;
+    self.CTRunBorderColor = nil;
+    self.CTRunFillColor = nil;
+    self.CTRunNumberColor = nil;
+    self.CGGlyphBorderColor = nil;
+    self.CGGlyphFillColor = nil;
+}
+
++ (void)addDebugTarget:(id<YYTextDebugTarget>)target {
+    if (target) _addDebugTarget(target);
+}
+
++ (void)removeDebugTarget:(id<YYTextDebugTarget>)target {
+    if (target) _removeDebugTarget(target);
+}
+
++ (YYTextDebugOption *)sharedDebugOption {
+    return _getSharedDebugOption();
+}
+
++ (void)setSharedDebugOption:(YYTextDebugOption *)option {
+    NSAssert([NSThread isMainThread], @"This method must be called on the main thread");
+    _setSharedDebugOption(option);
+}
+
+@end
+

+ 52 - 0
Pods/YYText/YYText/Component/YYTextEffectWindow.h

@@ -0,0 +1,52 @@
+//
+//  YYTextEffectWindow.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+#if __has_include(<YYText/YYText.h>)
+#import <YYText/YYTextMagnifier.h>
+#import <YYtext/YYTextSelectionView.h>
+#else
+#import "YYTextMagnifier.h"
+#import "YYTextSelectionView.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A window to display magnifier and extra contents for text view.
+ 
+ @discussion Use `sharedWindow` to get the instance, don't create your own instance.
+ Typically, you should not use this class directly.
+ */
+@interface YYTextEffectWindow : UIWindow
+
+/// Returns the shared instance (returns nil in App Extension).
++ (nullable instancetype)sharedWindow;
+
+/// Show the magnifier in this window with a 'popup' animation. @param mag A magnifier.
+- (void)showMagnifier:(YYTextMagnifier *)mag;
+/// Update the magnifier content and position. @param mag A magnifier.
+- (void)moveMagnifier:(YYTextMagnifier *)mag;
+/// Remove the magnifier from this window with a 'shrink' animation. @param mag A magnifier.
+- (void)hideMagnifier:(YYTextMagnifier *)mag;
+
+
+/// Show the selection dot in this window if the dot is clipped by the selection view.
+/// @param selection A selection view.
+- (void)showSelectionDot:(YYTextSelectionView *)selection;
+/// Remove the selection dot from this window.
+/// @param selection A selection view.
+- (void)hideSelectionDot:(YYTextSelectionView *)selection;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 429 - 0
Pods/YYText/YYText/Component/YYTextEffectWindow.m

@@ -0,0 +1,429 @@
+//
+//  YYTextEffectWindow.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextEffectWindow.h"
+#import "YYTextKeyboardManager.h"
+#import "YYTextUtilities.h"
+#import "UIView+YYText.h"
+
+
+@implementation YYTextEffectWindow
+
++ (instancetype)sharedWindow {
+    static YYTextEffectWindow *one = nil;
+    if (one == nil) {
+        // iOS 9 compatible
+        NSString *mode = [NSRunLoop currentRunLoop].currentMode;
+        if (mode.length == 27 &&
+            [mode hasPrefix:@"UI"] &&
+            [mode hasSuffix:@"InitializationRunLoopMode"]) {
+            return nil;
+        }
+    }
+    
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        if (!YYTextIsAppExtension()) {
+            one = [self new];
+            one.frame = (CGRect){.size = YYTextScreenSize()};
+            one.userInteractionEnabled = NO;
+            one.windowLevel = UIWindowLevelStatusBar + 1;
+            one.hidden = NO;
+            
+            // for iOS 9:
+            one.opaque = NO;
+            one.backgroundColor = [UIColor clearColor];
+            one.layer.backgroundColor = [UIColor clearColor].CGColor;
+        }
+    });
+    return one;
+}
+
+// stop self from becoming the KeyWindow
+- (void)becomeKeyWindow {
+    [[YYTextSharedApplication().delegate window] makeKeyWindow];
+}
+
+- (UIViewController *)rootViewController {
+    for (UIWindow *window in [YYTextSharedApplication() windows]) {
+        if (self == window) continue;
+        if (window.hidden) continue;
+        UIViewController *topViewController = window.rootViewController;
+        if (topViewController) return topViewController;
+    }
+    UIViewController *viewController = [super rootViewController];
+    if (!viewController) {
+        viewController = [UIViewController new];
+        [super setRootViewController:viewController];
+    }
+    return viewController;
+}
+
+// Bring self to front
+- (void)_updateWindowLevel {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return;
+    
+    UIWindow *top = app.windows.lastObject;
+    UIWindow *key = app.keyWindow;
+    if (key && key.windowLevel > top.windowLevel) top = key;
+    if (top == self) return;
+    self.windowLevel = top.windowLevel + 1;
+}
+
+- (YYTextDirection)_keyboardDirection {
+    CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
+    keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
+    if (CGRectIsNull(keyboardFrame) || CGRectIsEmpty(keyboardFrame)) return YYTextDirectionNone;
+    
+    if (CGRectGetMinY(keyboardFrame) == 0 &&
+        CGRectGetMinX(keyboardFrame) == 0 &&
+        CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame))
+        return YYTextDirectionTop;
+    
+    if (CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame) &&
+        CGRectGetMinY(keyboardFrame) == 0 &&
+        CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame))
+        return YYTextDirectionRight;
+    
+    if (CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame) &&
+        CGRectGetMinX(keyboardFrame) == 0 &&
+        CGRectGetMaxX(keyboardFrame) == CGRectGetWidth(self.frame))
+        return YYTextDirectionBottom;
+    
+    if (CGRectGetMinX(keyboardFrame) == 0 &&
+        CGRectGetMinY(keyboardFrame) == 0 &&
+        CGRectGetMaxY(keyboardFrame) == CGRectGetHeight(self.frame))
+        return YYTextDirectionLeft;
+    
+    return YYTextDirectionNone;
+}
+
+- (CGPoint)_correctedCaptureCenter:(CGPoint)center{
+    CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
+    keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
+    if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
+        YYTextDirection direction = [self _keyboardDirection];
+        switch (direction) {
+            case YYTextDirectionTop: {
+                if (center.y < CGRectGetMaxY(keyboardFrame)) center.y = CGRectGetMaxY(keyboardFrame);
+            } break;
+            case YYTextDirectionRight: {
+                if (center.x > CGRectGetMinX(keyboardFrame)) center.x = CGRectGetMinX(keyboardFrame);
+            } break;
+            case YYTextDirectionBottom: {
+                if (center.y > CGRectGetMinY(keyboardFrame)) center.y = CGRectGetMinY(keyboardFrame);
+            } break;
+            case YYTextDirectionLeft: {
+                if (center.x < CGRectGetMaxX(keyboardFrame)) center.x = CGRectGetMaxX(keyboardFrame);
+            } break;
+            default: break;
+        }
+    }
+    return center;
+}
+
+- (CGPoint)_correctedCenter:(CGPoint)center forMagnifier:(YYTextMagnifier *)mag rotation:(CGFloat)rotation {
+    CGFloat degree = YYTextRadiansToDegrees(rotation);
+    
+    degree /= 45.0;
+    if (degree < 0) degree += (int)(-degree/8.0 + 1) * 8;
+    if (degree > 8) degree -= (int)(degree/8.0) * 8;
+    
+    CGFloat caretExt = 10;
+    if (degree <= 1 || degree >= 7) { //top
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            if (center.y < caretExt)
+                center.y = caretExt;
+        } else if (mag.type == YYTextMagnifierTypeRanged) {
+            if (center.y < mag.bounds.size.height)
+                center.y = mag.bounds.size.height;
+        }
+    } else if (1 < degree && degree < 3) { // right
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            if (center.x > self.bounds.size.width - caretExt)
+                center.x = self.bounds.size.width - caretExt;
+        } else if (mag.type == YYTextMagnifierTypeRanged) {
+            if (center.x > self.bounds.size.width - mag.bounds.size.height)
+                center.x = self.bounds.size.width - mag.bounds.size.height;
+        }
+    } else if (3 <= degree && degree <= 5) { // bottom
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            if (center.y > self.bounds.size.height - caretExt)
+                center.y = self.bounds.size.height - caretExt;
+        } else if (mag.type == YYTextMagnifierTypeRanged) {
+            if (center.y > mag.bounds.size.height)
+                center.y = mag.bounds.size.height;
+        }
+    } else if (5 < degree && degree < 7) { // left
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            if (center.x < caretExt)
+                center.x = caretExt;
+        } else if (mag.type == YYTextMagnifierTypeRanged) {
+            if (center.x < mag.bounds.size.height)
+                center.x = mag.bounds.size.height;
+        }
+    }
+
+    
+    CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
+    keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
+    if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
+        YYTextDirection direction = [self _keyboardDirection];
+        switch (direction) {
+            case YYTextDirectionTop: {
+                if (mag.type == YYTextMagnifierTypeCaret) {
+                    if (center.y - mag.bounds.size.height / 2 < CGRectGetMaxY(keyboardFrame))
+                        center.y = CGRectGetMaxY(keyboardFrame) + mag.bounds.size.height / 2;
+                } else if (mag.type == YYTextMagnifierTypeRanged) {
+                    if (center.y < CGRectGetMaxY(keyboardFrame)) center.y = CGRectGetMaxY(keyboardFrame);
+                }
+            } break;
+            case YYTextDirectionRight: {
+                if (mag.type == YYTextMagnifierTypeCaret) {
+                    if (center.x + mag.bounds.size.height / 2 > CGRectGetMinX(keyboardFrame))
+                        center.x = CGRectGetMinX(keyboardFrame) - mag.bounds.size.width / 2;
+                } else if (mag.type == YYTextMagnifierTypeRanged) {
+                    if (center.x > CGRectGetMinX(keyboardFrame)) center.x = CGRectGetMinX(keyboardFrame);
+                }
+            } break;
+            case YYTextDirectionBottom: {
+                if (mag.type == YYTextMagnifierTypeCaret) {
+                    if (center.y + mag.bounds.size.height / 2 > CGRectGetMinY(keyboardFrame))
+                        center.y = CGRectGetMinY(keyboardFrame) - mag.bounds.size.height / 2;
+                } else if (mag.type == YYTextMagnifierTypeRanged) {
+                    if (center.y > CGRectGetMinY(keyboardFrame)) center.y = CGRectGetMinY(keyboardFrame);
+                }
+            } break;
+            case YYTextDirectionLeft: {
+                if (mag.type == YYTextMagnifierTypeCaret) {
+                    if (center.x - mag.bounds.size.height / 2 < CGRectGetMaxX(keyboardFrame))
+                        center.x = CGRectGetMaxX(keyboardFrame) + mag.bounds.size.width / 2;
+                } else if (mag.type == YYTextMagnifierTypeRanged) {
+                    if (center.x < CGRectGetMaxX(keyboardFrame)) center.x = CGRectGetMaxX(keyboardFrame);
+                }
+            } break;
+            default: break;
+        }
+    }
+    
+    return center;
+}
+
+/**
+ Capture screen snapshot and set it to magnifier.
+ @return Magnifier rotation radius.
+ */
+- (CGFloat)_updateMagnifier:(YYTextMagnifier *)mag {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return 0;
+    
+    UIView *hostView = mag.hostView;
+    UIWindow *hostWindow = [hostView isKindOfClass:[UIWindow class]] ? (id)hostView : hostView.window;
+    if (!hostView || !hostWindow) return 0;
+    CGPoint captureCenter = [self yy_convertPoint:mag.hostCaptureCenter fromViewOrWindow:hostView];
+    captureCenter = [self _correctedCaptureCenter:captureCenter];
+    CGRect captureRect = {.size = mag.snapshotSize};
+    captureRect.origin.x = captureCenter.x - captureRect.size.width / 2;
+    captureRect.origin.y = captureCenter.y - captureRect.size.height / 2;
+    
+    CGAffineTransform trans = YYTextCGAffineTransformGetFromViews(hostView, self);
+    CGFloat rotation = YYTextCGAffineTransformGetRotation(trans);
+    
+    if (mag.captureDisabled) {
+        if (!mag.snapshot || mag.snapshot.size.width > 1) {
+            static UIImage *placeholder;
+            static dispatch_once_t onceToken;
+            dispatch_once(&onceToken, ^{
+                CGRect rect = mag.bounds;
+                rect.origin = CGPointZero;
+                UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
+                CGContextRef context = UIGraphicsGetCurrentContext();
+                [[UIColor colorWithWhite:1 alpha:0.8] set];
+                CGContextFillRect(context, rect);
+                placeholder = UIGraphicsGetImageFromCurrentImageContext();
+                UIGraphicsEndImageContext();
+            });
+            mag.captureFadeAnimation = YES;
+            mag.snapshot = placeholder;
+            mag.captureFadeAnimation = NO;
+        }
+        return rotation;
+    }
+    
+    UIGraphicsBeginImageContextWithOptions(captureRect.size, NO, 0);
+    CGContextRef context = UIGraphicsGetCurrentContext();
+    if (!context) return rotation;
+    
+    CGPoint tp = CGPointMake(captureRect.size.width / 2, captureRect.size.height / 2);
+    tp = CGPointApplyAffineTransform(tp, CGAffineTransformMakeRotation(rotation));
+    CGContextRotateCTM(context, -rotation);
+    CGContextTranslateCTM(context, tp.x - captureCenter.x, tp.y - captureCenter.y);
+    
+    NSMutableArray *windows = app.windows.mutableCopy;
+    UIWindow *keyWindow = app.keyWindow;
+    if (![windows containsObject:keyWindow]) [windows addObject:keyWindow];
+    [windows sortUsingComparator:^NSComparisonResult(UIWindow *w1, UIWindow *w2) {
+        if (w1.windowLevel < w2.windowLevel) return NSOrderedAscending;
+        else if (w1.windowLevel > w2.windowLevel) return NSOrderedDescending;
+        return NSOrderedSame;
+    }];
+    UIScreen *mainScreen = [UIScreen mainScreen];
+    for (UIWindow *window in windows) {
+        if (window.hidden || window.alpha <= 0.01) continue;
+        if (window.screen != mainScreen) continue;
+        if ([window isKindOfClass:self.class]) break; //don't capture window above self
+        CGContextSaveGState(context);
+        CGContextConcatCTM(context, YYTextCGAffineTransformGetFromViews(window, self));
+        [window.layer renderInContext:context]; //render
+        //[window drawViewHierarchyInRect:window.bounds afterScreenUpdates:NO]; //slower when capture whole window
+        CGContextRestoreGState(context);
+    }
+    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
+    UIGraphicsEndImageContext();
+    
+    if (mag.snapshot.size.width == 1) {
+        mag.captureFadeAnimation = YES;
+    }
+    mag.snapshot = image;
+    mag.captureFadeAnimation = NO;
+    return rotation;
+}
+
+- (void)showMagnifier:(YYTextMagnifier *)mag {
+    if (!mag) return;
+    if (mag.superview != self) [self addSubview:mag];
+    [self _updateWindowLevel];
+    CGFloat rotation = [self _updateMagnifier:mag];
+    CGPoint center = [self yy_convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
+    CGAffineTransform trans = CGAffineTransformMakeRotation(rotation);
+    trans = CGAffineTransformScale(trans, 0.3, 0.3);
+    mag.transform = trans;
+    mag.center = center;
+    if (mag.type == YYTextMagnifierTypeRanged) {
+        mag.alpha = 0;
+    }
+    NSTimeInterval time = mag.type == YYTextMagnifierTypeCaret ? 0.08 : 0.1;
+    [UIView animateWithDuration:time delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState animations:^{
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
+            newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
+            newCenter.x += center.x;
+            newCenter.y += center.y;
+            mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
+        } else {
+            mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
+        }
+        mag.transform = CGAffineTransformMakeRotation(rotation);
+        mag.alpha = 1;
+    } completion:^(BOOL finished) {
+        
+    }];
+}
+
+- (void)moveMagnifier:(YYTextMagnifier *)mag {
+    if (!mag) return;
+    [self _updateWindowLevel];
+    CGFloat rotation = [self _updateMagnifier:mag];
+    CGPoint center = [self yy_convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
+    if (mag.type == YYTextMagnifierTypeCaret) {
+        CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
+        newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
+        newCenter.x += center.x;
+        newCenter.y += center.y;
+        mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
+    } else {
+        mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
+    }
+    mag.transform = CGAffineTransformMakeRotation(rotation);
+}
+
+- (void)hideMagnifier:(YYTextMagnifier *)mag {
+    if (!mag) return;
+    if (mag.superview != self) return;
+    CGFloat rotation = [self _updateMagnifier:mag];
+    CGPoint center = [self yy_convertPoint:mag.hostPopoverCenter fromViewOrWindow:mag.hostView];
+    NSTimeInterval time = mag.type == YYTextMagnifierTypeCaret ? 0.20 : 0.15;
+    [UIView animateWithDuration:time delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState animations:^{
+        
+        CGAffineTransform trans = CGAffineTransformMakeRotation(rotation);
+        trans = CGAffineTransformScale(trans, 0.01, 0.01);
+        mag.transform = trans;
+        
+        if (mag.type == YYTextMagnifierTypeCaret) {
+            CGPoint newCenter = CGPointMake(0, -mag.fitSize.height / 2);
+            newCenter = CGPointApplyAffineTransform(newCenter, CGAffineTransformMakeRotation(rotation));
+            newCenter.x += center.x;
+            newCenter.y += center.y;
+            mag.center = [self _correctedCenter:newCenter forMagnifier:mag rotation:rotation];
+        } else {
+            mag.center = [self _correctedCenter:center forMagnifier:mag rotation:rotation];
+            mag.alpha = 0;
+        }
+        
+    } completion:^(BOOL finished) {
+        if (finished) {
+            [mag removeFromSuperview];
+            mag.transform = CGAffineTransformIdentity;
+            mag.alpha = 1;
+        }
+    }];
+}
+
+- (void)_updateSelectionGrabberDot:(YYSelectionGrabberDot *)dot selection:(YYTextSelectionView *)selection{
+    dot.mirror.hidden = YES;
+    if (selection.hostView.clipsToBounds == YES && dot.yy_visibleAlpha > 0.1) {
+        CGRect dotRect = [dot yy_convertRect:dot.bounds toViewOrWindow:self];
+        BOOL dotInKeyboard = NO;
+        
+        CGRect keyboardFrame = [YYTextKeyboardManager defaultManager].keyboardFrame;
+        keyboardFrame = [[YYTextKeyboardManager defaultManager] convertRect:keyboardFrame toView:self];
+        if (!CGRectIsNull(keyboardFrame) && !CGRectIsEmpty(keyboardFrame)) {
+            CGRect inter = CGRectIntersection(dotRect, keyboardFrame);
+            if (!CGRectIsNull(inter) && (inter.size.width > 1 || inter.size.height > 1)) {
+                dotInKeyboard = YES;
+            }
+        }
+        if (!dotInKeyboard) {
+            CGRect hostRect = [selection.hostView convertRect:selection.hostView.bounds toView:self];
+            CGRect intersection = CGRectIntersection(dotRect, hostRect);
+            if (YYTextCGRectGetArea(intersection) < YYTextCGRectGetArea(dotRect)) {
+                CGFloat dist = YYTextCGPointGetDistanceToRect(YYTextCGRectGetCenter(dotRect), hostRect);
+                if (dist < CGRectGetWidth(dot.frame) * 0.55) {
+                    dot.mirror.hidden = NO;
+                }
+            }
+        }
+    }
+    CGPoint center = [dot yy_convertPoint:CGPointMake(CGRectGetWidth(dot.frame) / 2, CGRectGetHeight(dot.frame) / 2) toViewOrWindow:self];
+    if (isnan(center.x) || isnan(center.y) || isinf(center.x) || isinf(center.y)) {
+        dot.mirror.hidden = YES;
+    } else {
+        dot.mirror.center = center;
+    }
+}
+
+- (void)showSelectionDot:(YYTextSelectionView *)selection {
+    if (!selection) return;
+    [self _updateWindowLevel];
+    [self insertSubview:selection.startGrabber.dot.mirror atIndex:0];
+    [self insertSubview:selection.endGrabber.dot.mirror atIndex:0];
+    [self _updateSelectionGrabberDot:selection.startGrabber.dot selection:selection];
+    [self _updateSelectionGrabberDot:selection.endGrabber.dot selection:selection];
+}
+
+- (void)hideSelectionDot:(YYTextSelectionView *)selection {
+    if (!selection) return;
+    [selection.startGrabber.dot.mirror removeFromSuperview];
+    [selection.endGrabber.dot.mirror removeFromSuperview];
+}
+
+@end

+ 87 - 0
Pods/YYText/YYText/Component/YYTextInput.h

@@ -0,0 +1,87 @@
+//
+//  YYTextInput.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/17.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Text position affinity. For example, the offset appears after the last
+ character on a line is backward affinity, before the first character on
+ the following line is forward affinity.
+ */
+typedef NS_ENUM(NSInteger, YYTextAffinity) {
+    YYTextAffinityForward  = 0, ///< offset appears before the character
+    YYTextAffinityBackward = 1, ///< offset appears after the character
+};
+
+
+/**
+ A YYTextPosition object represents a position in a text container; in other words, 
+ it is an index into the backing string in a text-displaying view.
+ 
+ YYTextPosition has the same API as Apple's implementation in UITextView/UITextField,
+ so you can alse use it to interact with UITextView/UITextField.
+ */
+@interface YYTextPosition : UITextPosition <NSCopying>
+
+@property (nonatomic, readonly) NSInteger offset;
+@property (nonatomic, readonly) YYTextAffinity affinity;
+
++ (instancetype)positionWithOffset:(NSInteger)offset;
++ (instancetype)positionWithOffset:(NSInteger)offset affinity:(YYTextAffinity) affinity;
+
+- (NSComparisonResult)compare:(id)otherPosition;
+
+@end
+
+
+/**
+ A YYTextRange object represents a range of characters in a text container; in other words, 
+ it identifies a starting index and an ending index in string backing a text-displaying view.
+ 
+ YYTextRange has the same API as Apple's implementation in UITextView/UITextField,
+ so you can alse use it to interact with UITextView/UITextField.
+ */
+@interface YYTextRange : UITextRange <NSCopying>
+
+@property (nonatomic, readonly) YYTextPosition *start;
+@property (nonatomic, readonly) YYTextPosition *end;
+@property (nonatomic, readonly, getter=isEmpty) BOOL empty;
+
++ (instancetype)rangeWithRange:(NSRange)range;
++ (instancetype)rangeWithRange:(NSRange)range affinity:(YYTextAffinity) affinity;
++ (instancetype)rangeWithStart:(YYTextPosition *)start end:(YYTextPosition *)end;
++ (instancetype)defaultRange; ///< <{0,0} Forward>
+
+- (NSRange)asRange;
+
+@end
+
+
+/**
+ A YYTextSelectionRect object encapsulates information about a selected range of 
+ text in a text-displaying view.
+ 
+ YYTextSelectionRect has the same API as Apple's implementation in UITextView/UITextField,
+ so you can alse use it to interact with UITextView/UITextField.
+ */
+@interface YYTextSelectionRect : UITextSelectionRect <NSCopying>
+
+@property (nonatomic, readwrite) CGRect rect;
+@property (nonatomic, readwrite) UITextWritingDirection writingDirection;
+@property (nonatomic, readwrite) BOOL containsStart;
+@property (nonatomic, readwrite) BOOL containsEnd;
+@property (nonatomic, readwrite) BOOL isVertical;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 152 - 0
Pods/YYText/YYText/Component/YYTextInput.m

@@ -0,0 +1,152 @@
+//
+//  YYTextInput.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/17.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextInput.h"
+#import "YYTextUtilities.h"
+
+
+@implementation YYTextPosition
+
++ (instancetype)positionWithOffset:(NSInteger)offset {
+    return [self positionWithOffset:offset affinity:YYTextAffinityForward];
+}
+
++ (instancetype)positionWithOffset:(NSInteger)offset affinity:(YYTextAffinity)affinity {
+    YYTextPosition *p = [self new];
+    p->_offset = offset;
+    p->_affinity = affinity;
+    return p;
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+    return [self.class positionWithOffset:_offset affinity:_affinity];
+}
+
+- (NSString *)description {
+    return [NSString stringWithFormat:@"<%@: %p> (%@%@)", self.class, self, @(_offset), _affinity == YYTextAffinityForward ? @"F":@"B"];
+}
+
+- (NSUInteger)hash {
+    return _offset * 2 + (_affinity == YYTextAffinityForward ? 1 : 0);
+}
+
+- (BOOL)isEqual:(YYTextPosition *)object {
+    if (!object) return NO;
+    return _offset == object.offset && _affinity == object.affinity;
+}
+
+- (NSComparisonResult)compare:(YYTextPosition *)otherPosition {
+    if (!otherPosition) return NSOrderedAscending;
+    if (_offset < otherPosition.offset) return NSOrderedAscending;
+    if (_offset > otherPosition.offset) return NSOrderedDescending;
+    if (_affinity == YYTextAffinityBackward && otherPosition.affinity == YYTextAffinityForward) return NSOrderedAscending;
+    if (_affinity == YYTextAffinityForward && otherPosition.affinity == YYTextAffinityBackward) return NSOrderedDescending;
+    return NSOrderedSame;
+}
+
+@end
+
+
+
+@implementation YYTextRange {
+    YYTextPosition *_start;
+    YYTextPosition *_end;
+}
+
+- (instancetype)init {
+    self = [super init];
+    if (!self) return nil;
+    _start = [YYTextPosition positionWithOffset:0];
+    _end = [YYTextPosition positionWithOffset:0];
+    return self;
+}
+
+- (YYTextPosition *)start {
+    return _start;
+}
+
+- (YYTextPosition *)end {
+    return _end;
+}
+
+- (BOOL)isEmpty {
+    return _start.offset == _end.offset;
+}
+
+- (NSRange)asRange {
+    return NSMakeRange(_start.offset, _end.offset - _start.offset);
+}
+
++ (instancetype)rangeWithRange:(NSRange)range {
+    return [self rangeWithRange:range affinity:YYTextAffinityForward];
+}
+
++ (instancetype)rangeWithRange:(NSRange)range affinity:(YYTextAffinity)affinity {
+    YYTextPosition *start = [YYTextPosition positionWithOffset:range.location affinity:affinity];
+    YYTextPosition *end = [YYTextPosition positionWithOffset:range.location + range.length affinity:affinity];
+    return [self rangeWithStart:start end:end];
+}
+
++ (instancetype)rangeWithStart:(YYTextPosition *)start end:(YYTextPosition *)end {
+    if (!start || !end) return nil;
+    if ([start compare:end] == NSOrderedDescending) {
+        YYTEXT_SWAP(start, end);
+    }
+    YYTextRange *range = [YYTextRange new];
+    range->_start = start;
+    range->_end = end;
+    return range;
+}
+
++ (instancetype)defaultRange {
+    return [self new];
+}
+
+- (instancetype)copyWithZone:(NSZone *)zone {
+    return [self.class rangeWithStart:_start end:_end];
+}
+
+- (NSString *)description {
+    return [NSString stringWithFormat:@"<%@: %p> (%@, %@)%@", self.class, self, @(_start.offset), @(_end.offset - _start.offset), _end.affinity == YYTextAffinityForward ? @"F":@"B"];
+}
+
+- (NSUInteger)hash {
+    return (sizeof(NSUInteger) == 8 ? OSSwapInt64(_start.hash) : OSSwapInt32(_start.hash)) + _end.hash;
+}
+
+- (BOOL)isEqual:(YYTextRange *)object {
+    if (!object) return NO;
+    return [_start isEqual:object.start] && [_end isEqual:object.end];
+}
+
+@end
+
+
+
+@implementation YYTextSelectionRect
+
+@synthesize rect = _rect;
+@synthesize writingDirection = _writingDirection;
+@synthesize containsStart = _containsStart;
+@synthesize containsEnd = _containsEnd;
+@synthesize isVertical = _isVertical;
+
+- (id)copyWithZone:(NSZone *)zone {
+    YYTextSelectionRect *one = [self.class new];
+    one.rect = _rect;
+    one.writingDirection = _writingDirection;
+    one.containsStart = _containsStart;
+    one.containsEnd = _containsEnd;
+    one.isVertical = _isVertical;
+    return one;
+}
+
+@end

+ 98 - 0
Pods/YYText/YYText/Component/YYTextKeyboardManager.h

@@ -0,0 +1,98 @@
+//
+//  YYTextKeyboardManager.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/6/3.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ System keyboard transition information.
+ Use -[YYTextKeyboardManager convertRect:toView:] to convert frame to specified view.
+ */
+typedef struct {
+    BOOL fromVisible; ///< Keyboard visible before transition.
+    BOOL toVisible;   ///< Keyboard visible after transition.
+    CGRect fromFrame; ///< Keyboard frame before transition.
+    CGRect toFrame;   ///< Keyboard frame after transition.
+    NSTimeInterval animationDuration;       ///< Keyboard transition animation duration.
+    UIViewAnimationCurve animationCurve;    ///< Keyboard transition animation curve.
+    UIViewAnimationOptions animationOption; ///< Keybaord transition animation option.
+} YYTextKeyboardTransition;
+
+
+/**
+ The YYTextKeyboardObserver protocol defines the method you can use
+ to receive system keyboard change information.
+ */
+@protocol YYTextKeyboardObserver <NSObject>
+@optional
+- (void)keyboardChangedWithTransition:(YYTextKeyboardTransition)transition;
+@end
+
+
+/**
+ A YYTextKeyboardManager object lets you get the system keyboard information,
+ and track the keyboard visible/frame/transition.
+ 
+ @discussion You should access this class in main thread.
+ Compatible: iPhone/iPad with iOS6/7/8/9.
+ */
+@interface YYTextKeyboardManager : NSObject
+
+- (instancetype)init UNAVAILABLE_ATTRIBUTE;
++ (instancetype)new UNAVAILABLE_ATTRIBUTE;
+
+/// Get the default manager (returns nil in App Extension).
++ (nullable instancetype)defaultManager;
+
+/// Get the keyboard window. nil if there's no keyboard window.
+@property (nullable, nonatomic, readonly) UIWindow *keyboardWindow;
+
+/// Get the keyboard view. nil if there's no keyboard view.
+@property (nullable, nonatomic, readonly) UIView *keyboardView;
+
+/// Whether the keyboard is visible.
+@property (nonatomic, readonly, getter=isKeyboardVisible) BOOL keyboardVisible;
+
+/// Get the keyboard frame. CGRectNull if there's no keyboard view.
+/// Use convertRect:toView: to convert frame to specified view.
+@property (nonatomic, readonly) CGRect keyboardFrame;
+
+
+/**
+ Add an observer to manager to get keyboard change information.
+ This method makes a weak reference to the observer.
+ 
+ @param observer An observer. 
+ This method will do nothing if the observer is nil, or already added.
+ */
+- (void)addObserver:(id<YYTextKeyboardObserver>)observer;
+
+/**
+ Remove an observer from manager.
+ 
+ @param observer An observer.
+ This method will do nothing if the observer is nil, or not in manager.
+ */
+- (void)removeObserver:(id<YYTextKeyboardObserver>)observer;
+
+/**
+ Convert rect to specified view or window.
+ 
+ @param rect The frame rect.
+ @param view A specified view or window (pass nil to convert for main window).
+ @return The converted rect in specifeid view.
+ */
+- (CGRect)convertRect:(CGRect)rect toView:(nullable UIView *)view;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 521 - 0
Pods/YYText/YYText/Component/YYTextKeyboardManager.m

@@ -0,0 +1,521 @@
+//
+//  YYTextKeyboardManager.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/6/3.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextKeyboardManager.h"
+#import "YYTextUtilities.h"
+#import <objc/runtime.h>
+
+
+static int _YYTextKeyboardViewFrameObserverKey;
+
+/// Observer for view's frame/bounds/center/transform
+@interface _YYTextKeyboardViewFrameObserver : NSObject
+@property (nonatomic, copy) void (^notifyBlock)(UIView *keyboard);
+- (void)addToKeyboardView:(UIView *)keyboardView;
++ (instancetype)observerForView:(UIView *)keyboardView;
+@end
+
+
+@implementation _YYTextKeyboardViewFrameObserver {
+    __unsafe_unretained UIView *_keyboardView;
+}
+- (void)addToKeyboardView:(UIView *)keyboardView {
+    if (_keyboardView == keyboardView) return;
+    if (_keyboardView) {
+        [self removeFrameObserver];
+        objc_setAssociatedObject(_keyboardView, &_YYTextKeyboardViewFrameObserverKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+    }
+    _keyboardView = keyboardView;
+    if (keyboardView) {
+        [self addFrameObserver];
+    }
+    objc_setAssociatedObject(keyboardView, &_YYTextKeyboardViewFrameObserverKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (void)removeFrameObserver {
+    [_keyboardView removeObserver:self forKeyPath:@"frame"];
+    [_keyboardView removeObserver:self forKeyPath:@"center"];
+    [_keyboardView removeObserver:self forKeyPath:@"bounds"];
+    [_keyboardView removeObserver:self forKeyPath:@"transform"];
+    _keyboardView = nil;
+}
+
+- (void)addFrameObserver {
+    if (!_keyboardView) return;
+    [_keyboardView addObserver:self forKeyPath:@"frame" options:kNilOptions context:NULL];
+    [_keyboardView addObserver:self forKeyPath:@"center" options:kNilOptions context:NULL];
+    [_keyboardView addObserver:self forKeyPath:@"bounds" options:kNilOptions context:NULL];
+    [_keyboardView addObserver:self forKeyPath:@"transform" options:kNilOptions context:NULL];
+}
+
+- (void)dealloc {
+    [self removeFrameObserver];
+}
+
++ (instancetype)observerForView:(UIView *)keyboardView {
+    if (!keyboardView) return nil;
+    return objc_getAssociatedObject(keyboardView, &_YYTextKeyboardViewFrameObserverKey);
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+    
+    BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
+    if (isPrior) return;
+    
+    NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
+    if (changeKind != NSKeyValueChangeSetting) return;
+    
+    id newVal = [change objectForKey:NSKeyValueChangeNewKey];
+    if (newVal == [NSNull null]) newVal = nil;
+    
+    if (_notifyBlock) {
+        _notifyBlock(_keyboardView);
+    }
+}
+
+@end
+
+
+
+@implementation YYTextKeyboardManager {
+    NSHashTable *_observers;
+    
+    CGRect _fromFrame;
+    BOOL _fromVisible;
+    UIInterfaceOrientation _fromOrientation;
+    
+    CGRect _notificationFromFrame;
+    CGRect _notificationToFrame;
+    NSTimeInterval _notificationDuration;
+    UIViewAnimationCurve _notificationCurve;
+    BOOL _hasNotification;
+    
+    CGRect _observedToFrame;
+    BOOL _hasObservedChange;
+    
+    BOOL _lastIsNotification;
+}
+
+- (instancetype)init {
+    @throw [NSException exceptionWithName:@"YYTextKeyboardManager init error" reason:@"Use 'defaultManager' to get instance." userInfo:nil];
+    return [super init];
+}
+
+- (instancetype)_init {
+    self = [super init];
+    _observers = [[NSHashTable alloc] initWithOptions:NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality capacity:0];
+    [[NSNotificationCenter defaultCenter] addObserver:self
+                                             selector:@selector(_keyboardFrameWillChangeNotification:)
+                                                 name:UIKeyboardWillChangeFrameNotification
+                                               object:nil];
+    // for iPad (iOS 9)
+    if ([UIDevice currentDevice].systemVersion.floatValue >= 9) {
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                                 selector:@selector(_keyboardFrameDidChangeNotification:)
+                                                     name:UIKeyboardDidChangeFrameNotification
+                                                   object:nil];
+    }
+    return self;
+}
+
+- (void)_initFrameObserver {
+    UIView *keyboardView = self.keyboardView;
+    if (!keyboardView) return;
+    __weak typeof(self) _self = self;
+    _YYTextKeyboardViewFrameObserver *observer = [_YYTextKeyboardViewFrameObserver observerForView:keyboardView];
+    if (!observer) {
+        observer = [_YYTextKeyboardViewFrameObserver new];
+        observer.notifyBlock = ^(UIView *keyboard) {
+            [_self _keyboardFrameChanged:keyboard];
+        };
+        [observer addToKeyboardView:keyboardView];
+    }
+}
+
+- (void)dealloc {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
++ (instancetype)defaultManager {
+    static YYTextKeyboardManager *mgr = nil;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        if (!YYTextIsAppExtension()) {
+            mgr = [[self alloc] _init];
+        }
+    });
+    return mgr;
+}
+
++ (void)load {
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
+        [self defaultManager];
+    });
+}
+
+- (void)addObserver:(id<YYTextKeyboardObserver>)observer {
+    if (!observer) return;
+    [_observers addObject:observer];
+}
+
+- (void)removeObserver:(id<YYTextKeyboardObserver>)observer {
+    if (!observer) return;
+    [_observers removeObject:observer];
+}
+
+- (UIWindow *)keyboardWindow {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return nil;
+    
+    UIWindow *window = nil;
+    for (window in app.windows) {
+        if ([self _getKeyboardViewFromWindow:window]) return window;
+    }
+    window = app.keyWindow;
+    if ([self _getKeyboardViewFromWindow:window]) return window;
+    
+    NSMutableArray *kbWindows = nil;
+    for (window in app.windows) {
+        NSString *windowName = NSStringFromClass(window.class);
+        if ([self _systemVersion] < 9) {
+            // UITextEffectsWindow
+            if (windowName.length == 19 &&
+                [windowName hasPrefix:@"UI"] &&
+                [windowName hasSuffix:@"TextEffectsWindow"]) {
+                if (!kbWindows) kbWindows = [NSMutableArray new];
+                [kbWindows addObject:window];
+            }
+        } else {
+            // UIRemoteKeyboardWindow
+            if (windowName.length == 22 &&
+                [windowName hasPrefix:@"UI"] &&
+                [windowName hasSuffix:@"RemoteKeyboardWindow"]) {
+                if (!kbWindows) kbWindows = [NSMutableArray new];
+                [kbWindows addObject:window];
+            }
+        }
+    }
+    
+    if (kbWindows.count == 1) {
+        return kbWindows.firstObject;
+    }
+    return nil;
+}
+
+- (UIView *)keyboardView {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return nil;
+    
+    UIWindow *window = nil;
+    UIView *view = nil;
+    for (window in app.windows) {
+        view = [self _getKeyboardViewFromWindow:window];
+        if (view) return view;
+    }
+    window = app.keyWindow;
+    view = [self _getKeyboardViewFromWindow:window];
+    if (view) return view;
+    return nil;
+}
+
+- (BOOL)isKeyboardVisible {
+    UIWindow *window = self.keyboardWindow;
+    if (!window) return NO;
+    UIView *view = self.keyboardView;
+    if (!view) return NO;
+    CGRect rect = CGRectIntersection(window.bounds, view.frame);
+    if (CGRectIsNull(rect)) return NO;
+    if (CGRectIsInfinite(rect)) return NO;
+    return rect.size.width > 0 && rect.size.height > 0;
+}
+
+- (CGRect)keyboardFrame {
+    UIView *keyboard = [self keyboardView];
+    if (!keyboard) return CGRectNull;
+    
+    CGRect frame = CGRectNull;
+    UIWindow *window = keyboard.window;
+    if (window) {
+        frame = [window convertRect:keyboard.frame toWindow:nil];
+    } else {
+        frame = keyboard.frame;
+    }
+    return frame;
+}
+
+#pragma mark - private
+
+- (double)_systemVersion {
+    static double v;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        v = [UIDevice currentDevice].systemVersion.doubleValue;
+    });
+    return v;
+}
+
+- (UIView *)_getKeyboardViewFromWindow:(UIWindow *)window {
+    /*
+     iOS 6/7:
+     UITextEffectsWindow
+        UIPeripheralHostView << keyboard
+     
+     iOS 8:
+     UITextEffectsWindow
+        UIInputSetContainerView
+            UIInputSetHostView << keyboard
+     
+     iOS 9:
+     UIRemoteKeyboardWindow
+        UIInputSetContainerView
+            UIInputSetHostView << keyboard
+     */
+    if (!window) return nil;
+    
+    // Get the window
+    NSString *windowName = NSStringFromClass(window.class);
+    if ([self _systemVersion] < 9) {
+        // UITextEffectsWindow
+        if (windowName.length != 19) return nil;
+        if (![windowName hasPrefix:@"UI"]) return nil;
+        if (![windowName hasSuffix:@"TextEffectsWindow"]) return nil;
+    } else {
+        // UIRemoteKeyboardWindow
+        if (windowName.length != 22) return nil;
+        if (![windowName hasPrefix:@"UI"]) return nil;
+        if (![windowName hasSuffix:@"RemoteKeyboardWindow"]) return nil;
+    }
+    
+    // Get the view
+    if ([self _systemVersion] < 8) {
+        // UIPeripheralHostView
+        for (UIView *view in window.subviews) {
+            NSString *viewName = NSStringFromClass(view.class);
+            if (viewName.length != 20) continue;
+            if (![viewName hasPrefix:@"UI"]) continue;
+            if (![viewName hasSuffix:@"PeripheralHostView"]) continue;
+            return view;
+        }
+    } else {
+        // UIInputSetContainerView
+        for (UIView *view in window.subviews) {
+            NSString *viewName = NSStringFromClass(view.class);
+            if (viewName.length != 23) continue;
+            if (![viewName hasPrefix:@"UI"]) continue;
+            if (![viewName hasSuffix:@"InputSetContainerView"]) continue;
+            // UIInputSetHostView
+            for (UIView *subView in view.subviews) {
+                NSString *subViewName = NSStringFromClass(subView.class);
+                if (subViewName.length != 18) continue;
+                if (![subViewName hasPrefix:@"UI"]) continue;
+                if (![subViewName hasSuffix:@"InputSetHostView"]) continue;
+                return subView;
+            }
+        }
+    }
+    
+    return nil;
+}
+
+- (void)_keyboardFrameWillChangeNotification:(NSNotification *)notif {
+    if (![notif.name isEqualToString:UIKeyboardWillChangeFrameNotification]) return;
+    NSDictionary *info = notif.userInfo;
+    if (!info) return;
+    
+    [self _initFrameObserver];
+    
+    NSValue *beforeValue = info[UIKeyboardFrameBeginUserInfoKey];
+    NSValue *afterValue = info[UIKeyboardFrameEndUserInfoKey];
+    NSNumber *curveNumber = info[UIKeyboardAnimationCurveUserInfoKey];
+    NSNumber *durationNumber = info[UIKeyboardAnimationDurationUserInfoKey];
+    
+    CGRect before = beforeValue.CGRectValue;
+    CGRect after = afterValue.CGRectValue;
+    UIViewAnimationCurve curve = curveNumber.integerValue;
+    NSTimeInterval duration = durationNumber.doubleValue;
+    
+    // ignore zero end frame
+    if (after.size.width <= 0 && after.size.height <= 0) return;
+    
+    _notificationFromFrame = before;
+    _notificationToFrame = after;
+    _notificationCurve = curve;
+    _notificationDuration = duration;
+    _hasNotification = YES;
+    _lastIsNotification = YES;
+    
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil];
+    if (duration == 0) {
+        [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]];
+    } else {
+        [self _notifyAllObservers];
+    }
+}
+
+- (void)_keyboardFrameDidChangeNotification:(NSNotification *)notif {
+    if (![notif.name isEqualToString:UIKeyboardDidChangeFrameNotification]) return;
+    NSDictionary *info = notif.userInfo;
+    if (!info) return;
+    
+    [self _initFrameObserver];
+    
+    NSValue *afterValue = info[UIKeyboardFrameEndUserInfoKey];
+    CGRect after = afterValue.CGRectValue;
+    
+    // ignore zero end frame
+    if (after.size.width <= 0 && after.size.height <= 0) return;
+    
+    _notificationToFrame = after;
+    _notificationCurve = UIViewAnimationCurveEaseInOut;
+    _notificationDuration = 0;
+    _hasNotification = YES;
+    _lastIsNotification = YES;
+    
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil];
+    [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]];
+}
+
+- (void)_keyboardFrameChanged:(UIView *)keyboard {
+    if (keyboard != self.keyboardView) return;
+    
+    UIWindow *window = keyboard.window;
+    if (window) {
+        _observedToFrame = [window convertRect:keyboard.frame toWindow:nil];
+    } else {
+        _observedToFrame = keyboard.frame;
+    }
+    _hasObservedChange = YES;
+    _lastIsNotification = NO;
+    
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_notifyAllObservers) object:nil];
+    [self performSelector:@selector(_notifyAllObservers) withObject:nil afterDelay:0 inModes:@[NSRunLoopCommonModes]];
+}
+
+- (void)_notifyAllObservers {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return;
+    
+    UIView *keyboard = self.keyboardView;
+    UIWindow *window = keyboard.window;
+    if (!window) {
+        window = app.keyWindow;
+    }
+    if (!window) {
+        window = app.windows.firstObject;
+    }
+    
+    YYTextKeyboardTransition trans = {0};
+    
+    // from
+    if (_fromFrame.size.width == 0 && _fromFrame.size.height == 0) { // first notify
+        _fromFrame.size.width = window.bounds.size.width;
+        _fromFrame.size.height = trans.toFrame.size.height;
+        _fromFrame.origin.x = trans.toFrame.origin.x;
+        _fromFrame.origin.y = window.bounds.size.height;
+    }
+    trans.fromFrame = _fromFrame;
+    trans.fromVisible = _fromVisible;
+    
+    // to
+    if (_lastIsNotification || (_hasObservedChange && CGRectEqualToRect(_observedToFrame, _notificationToFrame))) {
+        trans.toFrame = _notificationToFrame;
+        trans.animationDuration = _notificationDuration;
+        trans.animationCurve = _notificationCurve;
+        trans.animationOption = _notificationCurve << 16;
+        
+        // Fix iPad(iOS7) keyboard frame error after rorate device when the keyboard is not docked to bottom.
+        if (((int)[self _systemVersion]) == 7) {
+            UIInterfaceOrientation ori = app.statusBarOrientation;
+            if (_fromOrientation != UIInterfaceOrientationUnknown && _fromOrientation != ori) {
+                switch (ori) {
+                    case UIInterfaceOrientationPortrait: {
+                        if (CGRectGetMaxY(trans.toFrame) != window.frame.size.height) {
+                            trans.toFrame.origin.y -= trans.toFrame.size.height;
+                        }
+                    } break;
+                    case UIInterfaceOrientationPortraitUpsideDown: {
+                        if (CGRectGetMinY(trans.toFrame) != 0) {
+                            trans.toFrame.origin.y += trans.toFrame.size.height;
+                        }
+                    } break;
+                    case UIInterfaceOrientationLandscapeLeft: {
+                        if (CGRectGetMaxX(trans.toFrame) != window.frame.size.width) {
+                            trans.toFrame.origin.x -= trans.toFrame.size.width;
+                        }
+                    } break;
+                    case UIInterfaceOrientationLandscapeRight: {
+                        if (CGRectGetMinX(trans.toFrame) != 0) {
+                            trans.toFrame.origin.x += trans.toFrame.size.width;
+                        }
+                    } break;
+                    default: break;
+                }
+            }
+        }
+    } else {
+        trans.toFrame = _observedToFrame;
+    }
+    
+    if (window && trans.toFrame.size.width > 0 && trans.toFrame.size.height > 0) {
+        CGRect rect = CGRectIntersection(window.bounds, trans.toFrame);
+        if (!CGRectIsNull(rect) && !CGRectIsEmpty(rect)) {
+            trans.toVisible = YES;
+        }
+    }
+    
+    if (!CGRectEqualToRect(trans.toFrame, _fromFrame)) {
+        for (id<YYTextKeyboardObserver> observer in _observers.copy) {
+            if ([observer respondsToSelector:@selector(keyboardChangedWithTransition:)]) {
+                [observer keyboardChangedWithTransition:trans];
+            }
+        }
+    }
+    
+    _hasNotification = NO;
+    _hasObservedChange = NO;
+    _fromFrame = trans.toFrame;
+    _fromVisible = trans.toVisible;
+    _fromOrientation = app.statusBarOrientation;
+}
+
+- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view {
+    UIApplication *app = YYTextSharedApplication();
+    if (!app) return CGRectZero;
+    
+    if (CGRectIsNull(rect)) return rect;
+    if (CGRectIsInfinite(rect)) return rect;
+    
+    UIWindow *mainWindow = app.keyWindow;
+    if (!mainWindow) mainWindow = app.windows.firstObject;
+    if (!mainWindow) { // no window ?!
+        if (view) {
+            [view convertRect:rect fromView:nil];
+        } else {
+            return rect;
+        }
+    }
+    
+    rect = [mainWindow convertRect:rect fromWindow:nil];
+    if (!view) return [mainWindow convertRect:rect toWindow:nil];
+    if (view == mainWindow) return rect;
+    
+    UIWindow *toWindow = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
+    if (!mainWindow || !toWindow) return [mainWindow convertRect:rect toView:view];
+    if (mainWindow == toWindow) return [mainWindow convertRect:rect toView:view];
+    
+    // in different window
+    rect = [mainWindow convertRect:rect toView:mainWindow];
+    rect = [toWindow convertRect:rect fromWindow:mainWindow];
+    rect = [view convertRect:rect fromView:toWindow];
+    return rect;
+}
+
+@end

+ 571 - 0
Pods/YYText/YYText/Component/YYTextLayout.h

@@ -0,0 +1,571 @@
+//
+//  YYTextLayout.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/3/3.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+#import <CoreText/CoreText.h>
+
+#if __has_include(<YYText/YYText.h>)
+#import <YYText/YYTextDebugOption.h>
+#import <YYText/YYTextLine.h>
+#import <YYText/YYTextInput.h>
+#else
+#import "YYTextDebugOption.h"
+#import "YYTextLine.h"
+#import "YYTextInput.h"
+#endif
+
+@protocol YYTextLinePositionModifier;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The max text container size in layout.
+ */
+extern const CGSize YYTextContainerMaxSize;
+
+/**
+ The YYTextContainer class defines a region in which text is laid out.
+ YYTextLayout class uses one or more YYTextContainer objects to generate layouts.
+ 
+ A YYTextContainer defines rectangular regions (`size` and `insets`) or 
+ nonrectangular shapes (`path`), and you can define exclusion paths inside the 
+ text container's bounding rectangle so that text flows around the exclusion 
+ path as it is laid out.
+ 
+ All methods in this class is thread-safe.
+ 
+ Example:
+ 
+     ┌─────────────────────────────┐  <------- container
+     │                             │
+     │    asdfasdfasdfasdfasdfa   <------------ container insets
+     │    asdfasdfa   asdfasdfa    │
+     │    asdfas         asdasd    │
+     │    asdfa        <----------------------- container exclusion path
+     │    asdfas         adfasd    │
+     │    asdfasdfa   asdfasdfa    │
+     │    asdfasdfasdfasdfasdfa    │
+     │                             │
+     └─────────────────────────────┘
+ */
+@interface YYTextContainer : NSObject <NSCoding, NSCopying>
+
+/// Creates a container with the specified size. @param size The size.
++ (instancetype)containerWithSize:(CGSize)size;
+
+/// Creates a container with the specified size and insets. @param size The size. @param insets The text insets.
++ (instancetype)containerWithSize:(CGSize)size insets:(UIEdgeInsets)insets;
+
+/// Creates a container with the specified path. @param size The path.
++ (instancetype)containerWithPath:(nullable UIBezierPath *)path;
+
+/// The constrained size. (if the size is larger than YYTextContainerMaxSize, it will be clipped)
+@property CGSize size;
+
+/// The insets for constrained size. The inset value should not be negative. Default is UIEdgeInsetsZero.
+@property UIEdgeInsets insets;
+
+/// Custom constrained path. Set this property to ignore `size` and `insets`. Default is nil.
+@property (nullable, copy) UIBezierPath *path;
+
+/// An array of `UIBezierPath` for path exclusion. Default is nil.
+@property (nullable, copy) NSArray<UIBezierPath *> *exclusionPaths;
+
+/// Path line width. Default is 0;
+@property CGFloat pathLineWidth;
+
+/// YES:(PathFillEvenOdd) Text is filled in the area that would be painted if the path were given to CGContextEOFillPath.
+/// NO: (PathFillWindingNumber) Text is fill in the area that would be painted if the path were given to CGContextFillPath.
+/// Default is YES;
+@property (getter=isPathFillEvenOdd) BOOL pathFillEvenOdd;
+
+/// Whether the text is vertical form (may used for CJK text layout). Default is NO.
+@property (getter=isVerticalForm) BOOL verticalForm;
+
+/// Maximum number of rows, 0 means no limit. Default is 0.
+@property NSUInteger maximumNumberOfRows;
+
+/// The line truncation type, default is none.
+@property YYTextTruncationType truncationType;
+
+/// The truncation token. If nil, the layout will use "…" instead. Default is nil.
+@property (nullable, copy) NSAttributedString *truncationToken;
+
+/// This modifier is applied to the lines before the layout is completed,
+/// give you a chance to modify the line position. Default is nil.
+@property (nullable, copy) id<YYTextLinePositionModifier> linePositionModifier;
+@end
+
+
+/**
+ The YYTextLinePositionModifier protocol declares the required method to modify
+ the line position in text layout progress. See `YYTextLinePositionSimpleModifier` for example.
+ */
+@protocol YYTextLinePositionModifier <NSObject, NSCopying>
+@required
+/**
+ This method will called before layout is completed. The method should be thread-safe.
+ @param lines     An array of YYTextLine.
+ @param text      The full text.
+ @param container The layout container.
+ */
+- (void)modifyLines:(NSArray<YYTextLine *> *)lines fromText:(NSAttributedString *)text inContainer:(YYTextContainer *)container;
+@end
+
+
+/**
+ A simple implementation of `YYTextLinePositionModifier`. It can fix each line's position
+ to a specified value, lets each line of height be the same.
+ */
+@interface YYTextLinePositionSimpleModifier : NSObject <YYTextLinePositionModifier>
+@property (assign) CGFloat fixedLineHeight; ///< The fixed line height (distance between two baseline).
+@end
+
+
+
+/**
+ YYTextLayout class is a readonly class stores text layout result.
+ All the property in this class is readonly, and should not be changed.
+ The methods in this class is thread-safe (except some of the draw methods).
+ 
+ example: (layout with a circle exclusion path)
+ 
+     ┌──────────────────────────┐  <------ container
+     │ [--------Line0--------]  │  <- Row0
+     │ [--------Line1--------]  │  <- Row1
+     │ [-Line2-]     [-Line3-]  │  <- Row2
+     │ [-Line4]       [Line5-]  │  <- Row3
+     │ [-Line6-]     [-Line7-]  │  <- Row4
+     │ [--------Line8--------]  │  <- Row5
+     │ [--------Line9--------]  │  <- Row6
+     └──────────────────────────┘
+ */
+@interface YYTextLayout : NSObject <NSCoding>
+
+
+#pragma mark - Generate text layout
+///=============================================================================
+/// @name Generate text layout
+///=============================================================================
+
+/**
+ Generate a layout with the given container size and text.
+
+ @param size The text container's size
+ @param text The text (if nil, returns nil).
+ @return A new layout, or nil when an error occurs.
+*/
++ (nullable YYTextLayout *)layoutWithContainerSize:(CGSize)size text:(NSAttributedString *)text;
+
+/**
+ Generate a layout with the given container and text.
+ 
+ @param container The text container (if nil, returns nil).
+ @param text      The text (if nil, returns nil).
+ @return A new layout, or nil when an error occurs.
+ */
++ (nullable YYTextLayout *)layoutWithContainer:(YYTextContainer *)container text:(NSAttributedString *)text;
+
+/**
+ Generate a layout with the given container and text.
+ 
+ @param container The text container (if nil, returns nil).
+ @param text      The text (if nil, returns nil).
+ @param range     The text range (if out of range, returns nil). If the
+    length of the range is 0, it means the length is no limit.
+ @return A new layout, or nil when an error occurs.
+ */
++ (nullable YYTextLayout *)layoutWithContainer:(YYTextContainer *)container text:(NSAttributedString *)text range:(NSRange)range;
+
+/**
+ Generate layouts with the given containers and text.
+ 
+ @param containers An array of YYTextContainer object (if nil, returns nil).
+ @param text       The text (if nil, returns nil).
+ @return An array of YYTextLayout object (the count is same as containers),
+    or nil when an error occurs.
+ */
++ (nullable NSArray<YYTextLayout *> *)layoutWithContainers:(NSArray<YYTextContainer *> *)containers
+                                                      text:(NSAttributedString *)text;
+
+/**
+ Generate layouts with the given containers and text.
+ 
+ @param containers An array of YYTextContainer object (if nil, returns nil).
+ @param text       The text (if nil, returns nil).
+ @param range      The text range (if out of range, returns nil). If the
+    length of the range is 0, it means the length is no limit.
+ @return An array of YYTextLayout object (the count is same as containers),
+    or nil when an error occurs.
+ */
++ (nullable NSArray<YYTextLayout *> *)layoutWithContainers:(NSArray<YYTextContainer *> *)containers
+                                                      text:(NSAttributedString *)text
+                                                     range:(NSRange)range;
+
+- (instancetype)init UNAVAILABLE_ATTRIBUTE;
++ (instancetype)new UNAVAILABLE_ATTRIBUTE;
+
+
+#pragma mark - Text layout attributes
+///=============================================================================
+/// @name Text layout attributes
+///=============================================================================
+
+///< The text container
+@property (nonatomic, strong, readonly) YYTextContainer *container;
+///< The full text
+@property (nonatomic, strong, readonly) NSAttributedString *text;
+///< The text range in full text
+@property (nonatomic, readonly) NSRange range;
+///< CTFrameSetter
+@property (nonatomic, readonly) CTFramesetterRef frameSetter;
+///< CTFrame
+@property (nonatomic, readonly) CTFrameRef frame;
+///< Array of `YYTextLine`, no truncated
+@property (nonatomic, strong, readonly) NSArray<YYTextLine *> *lines;
+///< YYTextLine with truncated token, or nil
+@property (nullable, nonatomic, strong, readonly) YYTextLine *truncatedLine;
+///< Array of `YYTextAttachment`
+@property (nullable, nonatomic, strong, readonly) NSArray<YYTextAttachment *> *attachments;
+///< Array of NSRange(wrapped by NSValue) in text
+@property (nullable, nonatomic, strong, readonly) NSArray<NSValue *> *attachmentRanges;
+///< Array of CGRect(wrapped by NSValue) in container
+@property (nullable, nonatomic, strong, readonly) NSArray<NSValue *> *attachmentRects;
+///< Set of Attachment (UIImage/UIView/CALayer)
+@property (nullable, nonatomic, strong, readonly) NSSet *attachmentContentsSet;
+///< Number of rows
+@property (nonatomic, readonly) NSUInteger rowCount;
+///< Visible text range
+@property (nonatomic, readonly) NSRange visibleRange;
+///< Bounding rect (glyphs)
+@property (nonatomic, readonly) CGRect textBoundingRect;
+///< Bounding size (glyphs and insets, ceil to pixel)
+@property (nonatomic, readonly) CGSize textBoundingSize;
+///< Has highlight attribute
+@property (nonatomic, readonly) BOOL containsHighlight;
+///< Has block border attribute
+@property (nonatomic, readonly) BOOL needDrawBlockBorder;
+///< Has background border attribute
+@property (nonatomic, readonly) BOOL needDrawBackgroundBorder;
+///< Has shadow attribute
+@property (nonatomic, readonly) BOOL needDrawShadow;
+///< Has underline attribute
+@property (nonatomic, readonly) BOOL needDrawUnderline;
+///< Has visible text
+@property (nonatomic, readonly) BOOL needDrawText;
+///< Has attachment attribute
+@property (nonatomic, readonly) BOOL needDrawAttachment;
+///< Has inner shadow attribute
+@property (nonatomic, readonly) BOOL needDrawInnerShadow;
+///< Has strickthrough attribute
+@property (nonatomic, readonly) BOOL needDrawStrikethrough;
+///< Has border attribute
+@property (nonatomic, readonly) BOOL needDrawBorder;
+
+
+#pragma mark - Query information from text layout
+///=============================================================================
+/// @name Query information from text layout
+///=============================================================================
+
+/**
+ The first line index for row.
+ 
+ @param row  A row index.
+ @return The line index, or NSNotFound if not found.
+ */
+- (NSUInteger)lineIndexForRow:(NSUInteger)row;
+
+/**
+ The number of lines for row.
+ 
+ @param row  A row index.
+ @return The number of lines, or NSNotFound when an error occurs.
+ */
+- (NSUInteger)lineCountForRow:(NSUInteger)row;
+
+/**
+ The row index for line.
+ 
+ @param line A row index.
+ 
+ @return The row index, or NSNotFound if not found.
+ */
+- (NSUInteger)rowIndexForLine:(NSUInteger)line;
+
+/**
+ The line index for a specified point.
+ 
+ @discussion It returns NSNotFound if there's no text at the point.
+ 
+ @param point  A point in the container.
+ @return The line index, or NSNotFound if not found.
+ */
+- (NSUInteger)lineIndexForPoint:(CGPoint)point;
+
+/**
+ The line index closest to a specified point.
+ 
+ @param point  A point in the container.
+ @return The line index, or NSNotFound if no line exist in layout.
+ */
+- (NSUInteger)closestLineIndexForPoint:(CGPoint)point;
+
+/**
+ The offset in container for a text position in a specified line.
+ 
+ @discussion The offset is the text position's baseline point.x.
+ If the container is vertical form, the offset is the baseline point.y;
+ 
+ @param position   The text position in string.
+ @param lineIndex  The line index.
+ @return The offset in container, or CGFLOAT_MAX if not found.
+ */
+- (CGFloat)offsetForTextPosition:(NSUInteger)position lineIndex:(NSUInteger)lineIndex;
+
+/**
+ The text position for a point in a specified line.
+ 
+ @discussion This method just call CTLineGetStringIndexForPosition() and does 
+ NOT consider the emoji, line break character, binding text...
+ 
+ @param point      A point in the container.
+ @param lineIndex  The line index.
+ @return The text position, or NSNotFound if not found.
+ */
+- (NSUInteger)textPositionForPoint:(CGPoint)point lineIndex:(NSUInteger)lineIndex;
+
+/**
+ The closest text position to a specified point.
+ 
+ @discussion This method takes into account the restrict of emoji, line break 
+ character, binding text and text affinity.
+ 
+ @param point  A point in the container.
+ @return A text position, or nil if not found.
+ */
+- (nullable YYTextPosition *)closestPositionToPoint:(CGPoint)point;
+
+/**
+ Returns the new position when moving selection grabber in text view.
+ 
+ @discussion There are two grabber in the text selection period, user can only 
+ move one grabber at the same time.
+ 
+ @param point          A point in the container.
+ @param oldPosition    The old text position for the moving grabber.
+ @param otherPosition  The other position in text selection view.
+ 
+ @return A text position, or nil if not found.
+ */
+- (nullable YYTextPosition *)positionForPoint:(CGPoint)point
+                                  oldPosition:(YYTextPosition *)oldPosition
+                                otherPosition:(YYTextPosition *)otherPosition;
+
+/**
+ Returns the character or range of characters that is at a given point in the container.
+ If there is no text at the point, returns nil.
+ 
+ @discussion This method takes into account the restrict of emoji, line break
+ character, binding text and text affinity.
+ 
+ @param point  A point in the container.
+ @return An object representing a range that encloses a character (or characters) 
+ at point. Or nil if not found.
+ */
+- (nullable YYTextRange *)textRangeAtPoint:(CGPoint)point;
+
+/**
+ Returns the closest character or range of characters that is at a given point in 
+ the container.
+ 
+ @discussion This method takes into account the restrict of emoji, line break
+ character, binding text and text affinity.
+ 
+ @param point  A point in the container.
+ @return An object representing a range that encloses a character (or characters)
+ at point. Or nil if not found.
+ */
+- (nullable YYTextRange *)closestTextRangeAtPoint:(CGPoint)point;
+
+/**
+ If the position is inside an emoji, composed character sequences, line break '\\r\\n'
+ or custom binding range, then returns the range by extend the position. Otherwise,
+ returns a zero length range from the position.
+ 
+ @param position A text-position object that identifies a location in layout.
+ 
+ @return A text-range object that extend the position. Or nil if an error occurs
+ */
+- (nullable YYTextRange *)textRangeByExtendingPosition:(YYTextPosition *)position;
+
+/**
+ Returns a text range at a given offset in a specified direction from another 
+ text position to its farthest extent in a certain direction of layout.
+ 
+ @param position  A text-position object that identifies a location in layout.
+ @param direction A constant that indicates a direction of layout (right, left, up, down).
+ @param offset    A character offset from position.
+ 
+ @return A text-range object that represents the distance from position to the
+ farthest extent in direction. Or nil if an error occurs.
+ */
+- (nullable YYTextRange *)textRangeByExtendingPosition:(YYTextPosition *)position
+                                           inDirection:(UITextLayoutDirection)direction
+                                                offset:(NSInteger)offset;
+
+/**
+ Returns the line index for a given text position.
+ 
+ @discussion This method takes into account the text affinity.
+ 
+ @param position A text-position object that identifies a location in layout.
+ @return The line index, or NSNotFound if not found.
+ */
+- (NSUInteger)lineIndexForPosition:(YYTextPosition *)position;
+
+/**
+ Returns the baseline position for a given text position.
+ 
+ @param position An object that identifies a location in the layout.
+ @return The baseline position for text, or CGPointZero if not found.
+ */
+- (CGPoint)linePositionForPosition:(YYTextPosition *)position;
+
+/**
+ Returns a rectangle used to draw the caret at a given insertion point.
+ 
+ @param position An object that identifies a location in the layout.
+ @return A rectangle that defines the area for drawing the caret. The width is
+ always zero in normal container, the height is always zero in vertical form container.
+ If not found, it returns CGRectNull.
+ */
+- (CGRect)caretRectForPosition:(YYTextPosition *)position;
+
+/**
+ Returns the first rectangle that encloses a range of text in the layout.
+ 
+ @param range An object that represents a range of text in layout.
+ 
+ @return The first rectangle in a range of text. You might use this rectangle to 
+ draw a correction rectangle. The "first" in the name refers the rectangle 
+ enclosing the first line when the range encompasses multiple lines of text.
+ If not found, it returns CGRectNull.
+ */
+- (CGRect)firstRectForRange:(YYTextRange *)range;
+
+/**
+ Returns the rectangle union that encloses a range of text in the layout.
+ 
+ @param range An object that represents a range of text in layout.
+ 
+ @return A rectangle that defines the area than encloses the range.
+ If not found, it returns CGRectNull.
+ */
+- (CGRect)rectForRange:(YYTextRange *)range;
+
+/**
+ Returns an array of selection rects corresponding to the range of text.
+ The start and end rect can be used to show grabber.
+ 
+ @param range An object representing a range in text.
+ @return An array of `YYTextSelectionRect` objects that encompass the selection.
+ If not found, the array is empty.
+ */
+- (NSArray<YYTextSelectionRect *> *)selectionRectsForRange:(YYTextRange *)range;
+
+/**
+ Returns an array of selection rects corresponding to the range of text.
+ 
+ @param range An object representing a range in text.
+ @return An array of `YYTextSelectionRect` objects that encompass the selection.
+ If not found, the array is empty.
+ */
+- (NSArray<YYTextSelectionRect *> *)selectionRectsWithoutStartAndEndForRange:(YYTextRange *)range;
+
+/**
+ Returns the start and end selection rects corresponding to the range of text.
+ The start and end rect can be used to show grabber.
+ 
+ @param range An object representing a range in text.
+ @return An array of `YYTextSelectionRect` objects contains the start and end to
+ the selection. If not found, the array is empty.
+ */
+- (NSArray<YYTextSelectionRect *> *)selectionRectsWithOnlyStartAndEndForRange:(YYTextRange *)range;
+
+
+#pragma mark - Draw text layout
+///=============================================================================
+/// @name Draw text layout
+///=============================================================================
+
+/**
+ Draw the layout and show the attachments.
+ 
+ @discussion If the `view` parameter is not nil, then the attachment views will
+ add to this `view`, and if the `layer` parameter is not nil, then the attachment
+ layers will add to this `layer`. 
+ 
+ @warning This method should be called on main thread if `view` or `layer` parameter
+ is not nil and there's UIView or CALayer attachments in layout. 
+ Otherwise, it can be called on any thread.
+ 
+ @param context The draw context. Pass nil to avoid text and image drawing.
+ @param size    The context size.
+ @param point   The point at which to draw the layout.
+ @param view    The attachment views will add to this view.
+ @param layer   The attachment layers will add to this layer.
+ @param debug   The debug option. Pass nil to avoid debug drawing.
+ @param cancel  The cancel checker block. It will be called in drawing progress.
+                    If it returns YES, the further draw progress will be canceled.
+                    Pass nil to ignore this feature.
+ */
+- (void)drawInContext:(nullable CGContextRef)context
+                 size:(CGSize)size
+                point:(CGPoint)point
+                 view:(nullable UIView *)view
+                layer:(nullable CALayer *)layer
+                debug:(nullable YYTextDebugOption *)debug
+               cancel:(nullable BOOL (^)(void))cancel;
+
+/**
+ Draw the layout text and image (without view or layer attachments).
+ 
+ @discussion This method is thread safe and can be called on any thread.
+ 
+ @param context The draw context. Pass nil to avoid text and image drawing.
+ @param size    The context size.
+ @param debug   The debug option. Pass nil to avoid debug drawing.
+ */
+- (void)drawInContext:(nullable CGContextRef)context
+                 size:(CGSize)size
+                debug:(nullable YYTextDebugOption *)debug;
+
+/**
+ Show view and layer attachments.
+ 
+ @warning This method must be called on main thread.
+ 
+ @param view  The attachment views will add to this view.
+ @param layer The attachment layers will add to this layer.
+ */
+- (void)addAttachmentToView:(nullable UIView *)view layer:(nullable CALayer *)layer;
+
+/**
+ Remove attachment views and layers from their super container.
+ 
+ @warning This method must be called on main thread.
+ */
+- (void)removeAttachmentFromViewAndLayer;
+
+@end
+
+NS_ASSUME_NONNULL_END

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 3407 - 0
Pods/YYText/YYText/Component/YYTextLayout.m


+ 84 - 0
Pods/YYText/YYText/Component/YYTextLine.h

@@ -0,0 +1,84 @@
+//
+//  YYTextLine.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/3/10.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+#import <CoreText/CoreText.h>
+
+#if __has_include(<YYText/YYText.h>)
+#import <YYText/YYTextAttribute.h>
+#else
+#import "YYTextAttribute.h"
+#endif
+
+@class YYTextRunGlyphRange;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A text line object wrapped `CTLineRef`, see `YYTextLayout` for more.
+ */
+@interface YYTextLine : NSObject
+
++ (instancetype)lineWithCTLine:(CTLineRef)CTLine position:(CGPoint)position vertical:(BOOL)isVertical;
+
+@property (nonatomic) NSUInteger index;     ///< line index
+@property (nonatomic) NSUInteger row;       ///< line row
+@property (nullable, nonatomic, strong) NSArray<NSArray<YYTextRunGlyphRange *> *> *verticalRotateRange; ///< Run rotate range
+
+@property (nonatomic, readonly) CTLineRef CTLine;   ///< CoreText line
+@property (nonatomic, readonly) NSRange range;      ///< string range
+@property (nonatomic, readonly) BOOL vertical;      ///< vertical form
+
+@property (nonatomic, readonly) CGRect bounds;      ///< bounds (ascent + descent)
+@property (nonatomic, readonly) CGSize size;        ///< bounds.size
+@property (nonatomic, readonly) CGFloat width;      ///< bounds.size.width
+@property (nonatomic, readonly) CGFloat height;     ///< bounds.size.height
+@property (nonatomic, readonly) CGFloat top;        ///< bounds.origin.y
+@property (nonatomic, readonly) CGFloat bottom;     ///< bounds.origin.y + bounds.size.height
+@property (nonatomic, readonly) CGFloat left;       ///< bounds.origin.x
+@property (nonatomic, readonly) CGFloat right;      ///< bounds.origin.x + bounds.size.width
+
+@property (nonatomic)   CGPoint position;   ///< baseline position
+@property (nonatomic, readonly) CGFloat ascent;     ///< line ascent
+@property (nonatomic, readonly) CGFloat descent;    ///< line descent
+@property (nonatomic, readonly) CGFloat leading;    ///< line leading
+@property (nonatomic, readonly) CGFloat lineWidth;  ///< line width
+@property (nonatomic, readonly) CGFloat trailingWhitespaceWidth;
+
+@property (nullable, nonatomic, readonly) NSArray<YYTextAttachment *> *attachments; ///< YYTextAttachment
+@property (nullable, nonatomic, readonly) NSArray<NSValue *> *attachmentRanges;     ///< NSRange(NSValue)
+@property (nullable, nonatomic, readonly) NSArray<NSValue *> *attachmentRects;      ///< CGRect(NSValue)
+
+@end
+
+
+typedef NS_ENUM(NSUInteger, YYTextRunGlyphDrawMode) {
+    /// No rotate.
+    YYTextRunGlyphDrawModeHorizontal = 0,
+    
+    /// Rotate vertical for single glyph.
+    YYTextRunGlyphDrawModeVerticalRotate = 1,
+    
+    /// Rotate vertical for single glyph, and move the glyph to a better position,
+    /// such as fullwidth punctuation.
+    YYTextRunGlyphDrawModeVerticalRotateMove = 2,
+};
+
+/**
+ A range in CTRun, used for vertical form.
+ */
+@interface YYTextRunGlyphRange : NSObject
+@property (nonatomic) NSRange glyphRangeInRun;
+@property (nonatomic) YYTextRunGlyphDrawMode drawMode;
++ (instancetype)rangeWithRange:(NSRange)range drawMode:(YYTextRunGlyphDrawMode)mode;
+@end
+
+NS_ASSUME_NONNULL_END

+ 167 - 0
Pods/YYText/YYText/Component/YYTextLine.m

@@ -0,0 +1,167 @@
+//
+//  YYYTextLine.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/3/3.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextLine.h"
+#import "YYTextUtilities.h"
+
+
+@implementation YYTextLine {
+    CGFloat _firstGlyphPos; // first glyph position for baseline, typically 0.
+}
+
++ (instancetype)lineWithCTLine:(CTLineRef)CTLine position:(CGPoint)position vertical:(BOOL)isVertical {
+    if (!CTLine) return nil;
+    YYTextLine *line = [self new];
+    line->_position = position;
+    line->_vertical = isVertical;
+    [line setCTLine:CTLine];
+    return line;
+}
+
+- (void)dealloc {
+    if (_CTLine) CFRelease(_CTLine);
+}
+
+- (void)setCTLine:(_Nonnull CTLineRef)CTLine {
+    if (_CTLine != CTLine) {
+        if (CTLine) CFRetain(CTLine);
+        if (_CTLine) CFRelease(_CTLine);
+        _CTLine = CTLine;
+        if (_CTLine) {
+            _lineWidth = CTLineGetTypographicBounds(_CTLine, &_ascent, &_descent, &_leading);
+            CFRange range = CTLineGetStringRange(_CTLine);
+            _range = NSMakeRange(range.location, range.length);
+            if (CTLineGetGlyphCount(_CTLine) > 0) {
+                CFArrayRef runs = CTLineGetGlyphRuns(_CTLine);
+                CTRunRef run = CFArrayGetValueAtIndex(runs, 0);
+                CGPoint pos;
+                CTRunGetPositions(run, CFRangeMake(0, 1), &pos);
+                _firstGlyphPos = pos.x;
+            } else {
+                _firstGlyphPos = 0;
+            }
+            _trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(_CTLine);
+        } else {
+            _lineWidth = _ascent = _descent = _leading = _firstGlyphPos = _trailingWhitespaceWidth = 0;
+            _range = NSMakeRange(0, 0);
+        }
+        [self reloadBounds];
+    }
+}
+
+- (void)setPosition:(CGPoint)position {
+    _position = position;
+    [self reloadBounds];
+}
+
+- (void)reloadBounds {
+    if (_vertical) {
+        _bounds = CGRectMake(_position.x - _descent, _position.y, _ascent + _descent, _lineWidth);
+        _bounds.origin.y += _firstGlyphPos;
+    } else {
+        _bounds = CGRectMake(_position.x, _position.y - _ascent, _lineWidth, _ascent + _descent);
+        _bounds.origin.x += _firstGlyphPos;
+    }
+    
+    _attachments = nil;
+    _attachmentRanges = nil;
+    _attachmentRects = nil;
+    if (!_CTLine) return;
+    CFArrayRef runs = CTLineGetGlyphRuns(_CTLine);
+    NSUInteger runCount = CFArrayGetCount(runs);
+    if (runCount == 0) return;
+    
+    NSMutableArray *attachments = [NSMutableArray new];
+    NSMutableArray *attachmentRanges = [NSMutableArray new];
+    NSMutableArray *attachmentRects = [NSMutableArray new];
+    for (NSUInteger r = 0; r < runCount; r++) {
+        CTRunRef run = CFArrayGetValueAtIndex(runs, r);
+        CFIndex glyphCount = CTRunGetGlyphCount(run);
+        if (glyphCount == 0) continue;
+        NSDictionary *attrs = (id)CTRunGetAttributes(run);
+        YYTextAttachment *attachment = attrs[YYTextAttachmentAttributeName];
+        if (attachment) {
+            CGPoint runPosition = CGPointZero;
+            CTRunGetPositions(run, CFRangeMake(0, 1), &runPosition);
+            
+            CGFloat ascent, descent, leading, runWidth;
+            CGRect runTypoBounds;
+            runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, &leading);
+            
+            if (_vertical) {
+                YYTEXT_SWAP(runPosition.x, runPosition.y);
+                runPosition.y = _position.y + runPosition.y;
+                runTypoBounds = CGRectMake(_position.x + runPosition.x - descent, runPosition.y , ascent + descent, runWidth);
+            } else {
+                runPosition.x += _position.x;
+                runPosition.y = _position.y - runPosition.y;
+                runTypoBounds = CGRectMake(runPosition.x, runPosition.y - ascent, runWidth, ascent + descent);
+            }
+            
+            NSRange runRange = YYTextNSRangeFromCFRange(CTRunGetStringRange(run));
+            [attachments addObject:attachment];
+            [attachmentRanges addObject:[NSValue valueWithRange:runRange]];
+            [attachmentRects addObject:[NSValue valueWithCGRect:runTypoBounds]];
+        }
+    }
+    _attachments = attachments.count ? attachments : nil;
+    _attachmentRanges = attachmentRanges.count ? attachmentRanges : nil;
+    _attachmentRects = attachmentRects.count ? attachmentRects : nil;
+}
+
+- (CGSize)size {
+    return _bounds.size;
+}
+
+- (CGFloat)width {
+    return CGRectGetWidth(_bounds);
+}
+
+- (CGFloat)height {
+    return CGRectGetHeight(_bounds);
+}
+
+- (CGFloat)top {
+    return CGRectGetMinY(_bounds);
+}
+
+- (CGFloat)bottom {
+    return CGRectGetMaxY(_bounds);
+}
+
+- (CGFloat)left {
+    return CGRectGetMinX(_bounds);
+}
+
+- (CGFloat)right {
+    return CGRectGetMaxX(_bounds);
+}
+
+- (NSString *)description {
+    NSMutableString *desc = @"".mutableCopy;
+    NSRange range = self.range;
+    [desc appendFormat:@"<YYTextLine: %p> row:%zd range:%tu,%tu",self, self.row, range.location, range.length];
+    [desc appendFormat:@" position:%@",NSStringFromCGPoint(self.position)];
+    [desc appendFormat:@" bounds:%@",NSStringFromCGRect(self.bounds)];
+    return desc;
+}
+
+@end
+
+
+@implementation YYTextRunGlyphRange
++ (instancetype)rangeWithRange:(NSRange)range drawMode:(YYTextRunGlyphDrawMode)mode {
+    YYTextRunGlyphRange *one = [self new];
+    one.glyphRangeInRun = range;
+    one.drawMode = mode;
+    return one;
+}
+@end

+ 52 - 0
Pods/YYText/YYText/Component/YYTextMagnifier.h

@@ -0,0 +1,52 @@
+//
+//  YYTextMagnifier.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+#if __has_include(<YYText/YYText.h>)
+#import <YYText/YYTextAttribute.h>
+#else
+#import "YYTextAttribute.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+/// Magnifier type
+typedef NS_ENUM(NSInteger, YYTextMagnifierType) {
+    YYTextMagnifierTypeCaret,  ///< Circular magnifier
+    YYTextMagnifierTypeRanged, ///< Round rectangle magnifier
+};
+
+/**
+ A magnifier view which can be displayed in `YYTextEffectWindow`.
+ 
+ @discussion Use `magnifierWithType:` to create instance.
+ Typically, you should not use this class directly.
+ */
+@interface YYTextMagnifier : UIView
+
+/// Create a mangifier with the specified type. @param type The magnifier type.
++ (id)magnifierWithType:(YYTextMagnifierType)type;
+
+@property (nonatomic, readonly) YYTextMagnifierType type;  ///< Type of magnifier
+@property (nonatomic, readonly) CGSize fitSize;            ///< The 'best' size for magnifier view.
+@property (nonatomic, readonly) CGSize snapshotSize;       ///< The 'best' snapshot image size for magnifier.
+@property (nullable, nonatomic, strong) UIImage *snapshot; ///< The image in magnifier (readwrite).
+
+@property (nullable, nonatomic, weak) UIView *hostView;   ///< The coordinate based view.
+@property (nonatomic) CGPoint hostCaptureCenter;          ///< The snapshot capture center in `hostView`.
+@property (nonatomic) CGPoint hostPopoverCenter;          ///< The popover center in `hostView`.
+@property (nonatomic) BOOL hostVerticalForm;              ///< The host view is vertical form.
+@property (nonatomic) BOOL captureDisabled;               ///< A hint for `YYTextEffectWindow` to disable capture.
+@property (nonatomic) BOOL captureFadeAnimation;          ///< Show fade animation when the snapshot image changed.
+@end
+
+NS_ASSUME_NONNULL_END

+ 355 - 0
Pods/YYText/YYText/Component/YYTextMagnifier.m

@@ -0,0 +1,355 @@
+//
+//  YYTextMagnifier.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextMagnifier.h"
+#import "YYTextUtilities.h"
+
+#define kCaptureDisableFadeTime 0.1
+
+
+@interface _YYTextMagnifierCaret : YYTextMagnifier {
+    UIImageView *_contentView;
+    UIImageView *_coverView;
+}
+@end
+
+@implementation _YYTextMagnifierCaret
+
+#define kMultiple 1.2
+#define kDiameter 113.0
+#define kPadding 7.0
+#define kSize CGSizeMake(kDiameter + kPadding * 2, kDiameter + kPadding * 2)
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    _contentView = [UIImageView new];
+    _contentView.frame = CGRectMake(kPadding, kPadding, kDiameter, kDiameter);
+    _contentView.layer.cornerRadius = kDiameter / 2;
+    _contentView.clipsToBounds = YES;
+    [self addSubview:_contentView];
+    
+    _coverView = [UIImageView new];
+    _coverView.frame = (CGRect){.origin = CGPointZero, .size = kSize};
+    _coverView.image = [self.class coverImage];
+    [self addSubview:_coverView];
+    return self;
+}
+
+- (instancetype)init {
+    self = [self initWithFrame:CGRectZero];
+    self.frame = (CGRect){.size = [self sizeThatFits:CGSizeZero]};
+    return self;
+}
+
+- (YYTextMagnifierType)type {
+    return YYTextMagnifierTypeCaret;
+}
+
+- (CGSize)sizeThatFits:(CGSize)size {
+    return kSize;
+}
+
+- (void)setSnapshot:(UIImage *)snapshot {
+    if (self.captureFadeAnimation) {
+        [_contentView.layer removeAnimationForKey:@"contents"];
+        CABasicAnimation *animation = [CABasicAnimation animation];
+        animation.duration = kCaptureDisableFadeTime;
+        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
+        [_contentView.layer addAnimation:animation forKey:@"contents"];
+    }
+    _contentView.image = snapshot;
+}
+
+- (UIImage *)snapshot {
+    return _contentView.image;
+}
+
+- (CGSize)snapshotSize {
+    CGFloat length = floor(kDiameter / 1.2);
+    return CGSizeMake(length, length);
+}
+
+- (CGSize)fitSize {
+    return [self sizeThatFits:CGSizeZero];
+}
+
++ (UIImage *)coverImage {
+    static UIImage *image;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        CGSize size = kSize;
+        CGRect rect = (CGRect) {.size = size, .origin = CGPointZero};
+        rect = CGRectInset(rect, kPadding, kPadding);
+        
+        UIGraphicsBeginImageContextWithOptions(size, NO, 0);
+        CGContextRef context = UIGraphicsGetCurrentContext();
+        
+        CGPathRef boxPath = CGPathCreateWithRect(CGRectMake(0, 0, size.width, size.height), NULL);
+        CGPathRef fillPath = CGPathCreateWithEllipseInRect(rect, NULL);
+        CGPathRef strokePath = CGPathCreateWithEllipseInRect(YYTextCGRectPixelHalf(rect), NULL);
+        
+        // inner shadow
+        CGContextSaveGState(context); {
+            CGFloat blurRadius = 25;
+            CGSize offset = CGSizeMake(0, 15);
+            CGColorRef shadowColor = [UIColor colorWithWhite:0 alpha:0.16].CGColor;
+            CGColorRef opaqueShadowColor = CGColorCreateCopyWithAlpha(shadowColor, 1.0);
+            CGContextAddPath(context, fillPath);
+            CGContextClip(context);
+            CGContextSetAlpha(context, CGColorGetAlpha(shadowColor));
+            CGContextBeginTransparencyLayer(context, NULL); {
+                CGContextSetShadowWithColor(context, offset, blurRadius, opaqueShadowColor);
+                CGContextSetBlendMode(context, kCGBlendModeSourceOut);
+                CGContextSetFillColorWithColor(context, opaqueShadowColor);
+                CGContextAddPath(context, fillPath);
+                CGContextFillPath(context);
+            } CGContextEndTransparencyLayer(context);
+            CGColorRelease(opaqueShadowColor);
+        } CGContextRestoreGState(context);
+        
+        // outer shadow
+        CGContextSaveGState(context); {
+            CGContextAddPath(context, boxPath);
+            CGContextAddPath(context, fillPath);
+            CGContextEOClip(context);
+            CGColorRef shadowColor = [UIColor colorWithWhite:0 alpha:0.32].CGColor;
+            CGContextSetShadowWithColor(context, CGSizeMake(0, 1.5), 3, shadowColor);
+            CGContextBeginTransparencyLayer(context, NULL); {
+                CGContextAddPath(context, fillPath);
+                [[UIColor colorWithWhite:0.7 alpha:1.000] setFill];
+                CGContextFillPath(context);
+            } CGContextEndTransparencyLayer(context);
+        } CGContextRestoreGState(context);
+        
+        // stroke
+        CGContextSaveGState(context); {
+            CGContextAddPath(context, strokePath);
+            [[UIColor colorWithWhite:0.6 alpha:1] setStroke];
+            CGContextSetLineWidth(context, YYTextCGFloatFromPixel(1));
+            CGContextStrokePath(context);
+        } CGContextRestoreGState(context);
+        
+        CFRelease(boxPath);
+        CFRelease(fillPath);
+        CFRelease(strokePath);
+        
+        image = UIGraphicsGetImageFromCurrentImageContext();
+        UIGraphicsEndImageContext();
+        
+    });
+    return image;
+}
+
+
+#undef kMultiple
+#undef kDiameter
+#undef kPadding
+#undef kSize
+
+@end
+
+
+
+@interface _YYTextMagnifierRanged : YYTextMagnifier {
+    UIImageView *_contentView;
+    UIImageView *_coverView;
+}
+@end
+
+
+@implementation _YYTextMagnifierRanged
+#define kMultiple 1.2
+#define kSize CGSizeMake(141, 60)
+#define kPadding YYTextCGFloatPixelHalf(6.0)
+#define kRadius 6.0
+#define kHeight 32.0
+#define kArrow 14.0
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    _contentView = [UIImageView new];
+    _contentView.frame = CGRectMake(kPadding, kPadding, kSize.width - 2 * kPadding, kHeight);
+    _contentView.layer.cornerRadius = kRadius;
+    _contentView.clipsToBounds = YES;
+    [self addSubview:_contentView];
+    
+    _coverView = [UIImageView new];
+    _coverView.frame = (CGRect){.origin = CGPointZero, .size = kSize};
+    _coverView.image = [self.class coverImage];
+    [self addSubview:_coverView];
+    
+    self.layer.anchorPoint = CGPointMake(0.5, 1.2);
+    return self;
+}
+
+- (instancetype)init {
+    self = [self initWithFrame:CGRectZero];
+    self.frame = (CGRect){.size = [self sizeThatFits:CGSizeZero]};
+    return self;
+}
+
+- (YYTextMagnifierType)type {
+    return YYTextMagnifierTypeRanged;
+}
+
+- (CGSize)sizeThatFits:(CGSize)size {
+    return kSize;
+}
+
+- (void)setSnapshot:(UIImage *)snapshot {
+    if (self.captureFadeAnimation) {
+        [_contentView.layer removeAnimationForKey:@"contents"];
+        CABasicAnimation *animation = [CABasicAnimation animation];
+        animation.duration = kCaptureDisableFadeTime;
+        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
+        [_contentView.layer addAnimation:animation forKey:@"contents"];
+    }
+    _contentView.image = snapshot;
+}
+
+- (UIImage *)snapshot {
+    return _contentView.image;
+}
+
+- (CGSize)snapshotSize {
+    CGSize size;
+    size.width = floor((kSize.width - 2 * kPadding) / kMultiple);
+    size.height = floor(kHeight / kMultiple);
+    return size;
+}
+
+- (CGSize)fitSize {
+    return [self sizeThatFits:CGSizeZero];
+}
+
++ (UIImage *)coverImage {
+    static UIImage *image;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        CGSize size = kSize;
+        CGRect rect = (CGRect) {.size = size, .origin = CGPointZero};
+        
+        UIGraphicsBeginImageContextWithOptions(size, NO, 0);
+        CGContextRef context = UIGraphicsGetCurrentContext();
+        
+        CGPathRef boxPath = CGPathCreateWithRect(rect, NULL);
+        
+        CGMutablePathRef path = CGPathCreateMutable();
+        CGPathMoveToPoint(path, NULL, kPadding + kRadius, kPadding);
+        CGPathAddLineToPoint(path, NULL, size.width - kPadding - kRadius, kPadding);
+        CGPathAddQuadCurveToPoint(path, NULL, size.width - kPadding, kPadding, size.width - kPadding, kPadding + kRadius);
+        CGPathAddLineToPoint(path, NULL, size.width - kPadding, kHeight);
+        CGPathAddCurveToPoint(path, NULL, size.width - kPadding, kPadding + kHeight, size.width - kPadding - kRadius, kPadding + kHeight, size.width - kPadding - kRadius, kPadding + kHeight);
+        CGPathAddLineToPoint(path, NULL, size.width / 2 + kArrow, kPadding + kHeight);
+        CGPathAddLineToPoint(path, NULL, size.width / 2, kPadding + kHeight + kArrow);
+        CGPathAddLineToPoint(path, NULL, size.width / 2 - kArrow, kPadding + kHeight);
+        CGPathAddLineToPoint(path, NULL, kPadding + kRadius, kPadding + kHeight);
+        CGPathAddQuadCurveToPoint(path, NULL, kPadding, kPadding + kHeight, kPadding, kHeight);
+        CGPathAddLineToPoint(path, NULL, kPadding, kPadding + kRadius);
+        CGPathAddQuadCurveToPoint(path, NULL, kPadding, kPadding, kPadding + kRadius, kPadding);
+        CGPathCloseSubpath(path);
+        
+        CGMutablePathRef arrowPath = CGPathCreateMutable();
+        CGPathMoveToPoint(arrowPath, NULL, size.width / 2 - kArrow, YYTextCGFloatPixelFloor(kPadding) + kHeight);
+        CGPathAddLineToPoint(arrowPath, NULL, size.width / 2 + kArrow, YYTextCGFloatPixelFloor(kPadding) + kHeight);
+        CGPathAddLineToPoint(arrowPath, NULL, size.width / 2, kPadding + kHeight + kArrow);
+        CGPathCloseSubpath(arrowPath);
+        
+        // inner shadow
+        CGContextSaveGState(context); {
+            CGFloat blurRadius = 25;
+            CGSize offset = CGSizeMake(0, 15);
+            CGColorRef shadowColor = [UIColor colorWithWhite:0 alpha:0.16].CGColor;
+            CGColorRef opaqueShadowColor = CGColorCreateCopyWithAlpha(shadowColor, 1.0);
+            CGContextAddPath(context, path);
+            CGContextClip(context);
+            CGContextSetAlpha(context, CGColorGetAlpha(shadowColor));
+            CGContextBeginTransparencyLayer(context, NULL); {
+                CGContextSetShadowWithColor(context, offset, blurRadius, opaqueShadowColor);
+                CGContextSetBlendMode(context, kCGBlendModeSourceOut);
+                CGContextSetFillColorWithColor(context, opaqueShadowColor);
+                CGContextAddPath(context, path);
+                CGContextFillPath(context);
+            } CGContextEndTransparencyLayer(context);
+            CGColorRelease(opaqueShadowColor);
+        } CGContextRestoreGState(context);
+        
+        // outer shadow
+        CGContextSaveGState(context); {
+            CGContextAddPath(context, boxPath);
+            CGContextAddPath(context, path);
+            CGContextEOClip(context);
+            CGColorRef shadowColor = [UIColor colorWithWhite:0 alpha:0.32].CGColor;
+            CGContextSetShadowWithColor(context, CGSizeMake(0, 1.5), 3, shadowColor);
+            CGContextBeginTransparencyLayer(context, NULL); {
+                CGContextAddPath(context, path);
+                [[UIColor colorWithWhite:0.7 alpha:1.000] setFill];
+                CGContextFillPath(context);
+            } CGContextEndTransparencyLayer(context);
+        } CGContextRestoreGState(context);
+        
+        // arrow
+        CGContextSaveGState(context); {
+            CGContextAddPath(context, arrowPath);
+            [[UIColor colorWithWhite:1 alpha:0.95] set];
+            CGContextFillPath(context);
+        } CGContextRestoreGState(context);
+        
+        // stroke
+        CGContextSaveGState(context); {
+            CGContextAddPath(context, path);
+            [[UIColor colorWithWhite:0.6 alpha:1] setStroke];
+            CGContextSetLineWidth(context, YYTextCGFloatFromPixel(1));
+            CGContextStrokePath(context);
+        } CGContextRestoreGState(context);
+        
+        CFRelease(boxPath);
+        CFRelease(path);
+        CFRelease(arrowPath);
+        
+        image = UIGraphicsGetImageFromCurrentImageContext();
+        UIGraphicsEndImageContext();
+        
+    });
+    return image;
+}
+
+#undef kMultiple
+#undef kSize
+#undef kPadding
+#undef kRadius
+#undef kHeight
+#undef kArrow
+
+@end
+
+
+@implementation YYTextMagnifier
+
++ (id)magnifierWithType:(YYTextMagnifierType)type {
+    switch (type) {
+        case YYTextMagnifierTypeCaret :return [_YYTextMagnifierCaret new];
+        case YYTextMagnifierTypeRanged :return [_YYTextMagnifierRanged new];
+    }
+    return nil;
+}
+
+- (id)initWithFrame:(CGRect)frame {
+    // class cluster
+    if ([self isMemberOfClass:[YYTextMagnifier class]]) {
+        @throw [NSException exceptionWithName:NSStringFromClass([self class]) reason:@"Attempting to instantiate an abstract class. Use a concrete subclass instead." userInfo:nil];
+        return nil;
+    }
+    self = [super initWithFrame:frame];
+    return self;
+}
+
+@end

+ 78 - 0
Pods/YYText/YYText/Component/YYTextSelectionView.h

@@ -0,0 +1,78 @@
+//
+//  YYTextSelectionView.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+#if __has_include(<YYText/YYText.h>)
+#import <YYText/YYTextAttribute.h>
+#import <YYText/YYTextInput.h>
+#else
+#import "YYTextAttribute.h"
+#import "YYTextInput.h"
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A single dot view. The frame should be foursquare.
+ Change the background color for display.
+ 
+ @discussion Typically, you should not use this class directly.
+ */
+@interface YYSelectionGrabberDot : UIView
+/// Dont't access this property. It was used by `YYTextEffectWindow`.
+@property (nonatomic, strong) UIView *mirror;
+@end
+
+
+/**
+ A grabber (stick with a dot).
+ 
+ @discussion Typically, you should not use this class directly.
+ */
+@interface YYSelectionGrabber : UIView
+
+@property (nonatomic, readonly) YYSelectionGrabberDot *dot; ///< the dot view
+@property (nonatomic) YYTextDirection dotDirection;         ///< don't support composite direction
+@property (nullable, nonatomic, strong) UIColor *color;     ///< tint color, default is nil
+
+@end
+
+
+/**
+ The selection view for text edit and select.
+ 
+ @discussion Typically, you should not use this class directly.
+ */
+@interface YYTextSelectionView : UIView
+
+@property (nullable, nonatomic, weak) UIView *hostView; ///< the holder view
+@property (nullable, nonatomic, strong) UIColor *color; ///< the tint color
+@property (nonatomic, getter = isCaretBlinks) BOOL caretBlinks; ///< whether the caret is blinks
+@property (nonatomic, getter = isCaretVisible) BOOL caretVisible; ///< whether the caret is visible
+@property (nonatomic, getter = isVerticalForm) BOOL verticalForm; ///< weather the text view is vertical form
+
+@property (nonatomic) CGRect caretRect; ///< caret rect (width==0 or height==0)
+@property (nullable, nonatomic, copy) NSArray<YYTextSelectionRect *> *selectionRects; ///< default is nil
+
+@property (nonatomic, readonly) UIView *caretView;
+@property (nonatomic, readonly) YYSelectionGrabber *startGrabber;
+@property (nonatomic, readonly) YYSelectionGrabber *endGrabber;
+
+- (BOOL)isGrabberContainsPoint:(CGPoint)point;
+- (BOOL)isStartGrabberContainsPoint:(CGPoint)point;
+- (BOOL)isEndGrabberContainsPoint:(CGPoint)point;
+- (BOOL)isCaretContainsPoint:(CGPoint)point;
+- (BOOL)isSelectionRectsContainsPoint:(CGPoint)point;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 329 - 0
Pods/YYText/YYText/Component/YYTextSelectionView.m

@@ -0,0 +1,329 @@
+//
+//  YYTextSelectionView.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/2/25.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextSelectionView.h"
+#import "YYTextUtilities.h"
+#import "YYTextWeakProxy.h"
+
+#define kMarkAlpha 0.2
+#define kLineWidth 2.0
+#define kBlinkDuration 0.5
+#define kBlinkFadeDuration 0.2
+#define kBlinkFirstDelay 0.1
+#define kTouchTestExtend 14.0
+#define kTouchDotExtend 7.0
+
+
+@implementation YYSelectionGrabberDot
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (!self) return nil;
+    self.userInteractionEnabled = NO;
+    self.mirror = [UIView new];
+    return self;
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    CGFloat length = MIN(self.bounds.size.width, self.bounds.size.height);
+    self.layer.cornerRadius = length * 0.5;
+    self.mirror.bounds = self.bounds;
+    self.mirror.layer.cornerRadius = self.layer.cornerRadius;
+}
+
+- (void)setBackgroundColor:(UIColor *)backgroundColor {
+    [super setBackgroundColor:backgroundColor];
+    _mirror.backgroundColor = backgroundColor;    
+}
+
+@end
+
+
+
+@implementation YYSelectionGrabber
+
+- (instancetype) initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (!self) return nil;
+    _dot = [[YYSelectionGrabberDot alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
+    return self;
+}
+
+- (void)setDotDirection:(YYTextDirection)dotDirection {
+    _dotDirection = dotDirection;
+    [self addSubview:_dot];
+    CGRect frame = _dot.frame;
+    CGFloat ofs = 0.5;
+    if (dotDirection == YYTextDirectionTop) {
+        frame.origin.y = -frame.size.height + ofs;
+        frame.origin.x = (self.bounds.size.width - frame.size.width) / 2;
+    } else if (dotDirection == YYTextDirectionRight) {
+        frame.origin.x = self.bounds.size.width - ofs;
+        frame.origin.y = (self.bounds.size.height - frame.size.height) / 2;
+    } else if (dotDirection == YYTextDirectionBottom) {
+        frame.origin.y = self.bounds.size.height - ofs;
+        frame.origin.x = (self.bounds.size.width - frame.size.width) / 2;
+    } else if (dotDirection == YYTextDirectionLeft) {
+        frame.origin.x = -frame.size.width + ofs;
+        frame.origin.y = (self.bounds.size.height - frame.size.height) / 2;
+    } else {
+        [_dot removeFromSuperview];
+    }
+    _dot.frame = frame;
+}
+
+- (void)setColor:(UIColor *)color {
+    self.backgroundColor = color;
+    _dot.backgroundColor = color;
+    _color = color;
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    [self setDotDirection:_dotDirection];
+}
+
+- (CGRect)touchRect {
+    CGRect rect = CGRectInset(self.frame, -kTouchTestExtend, -kTouchTestExtend);
+    UIEdgeInsets insets = {0};
+    if (_dotDirection == YYTextDirectionTop) {
+        insets.top = -kTouchDotExtend;
+    } else if (_dotDirection == YYTextDirectionRight) {
+        insets.right = -kTouchDotExtend;
+    } else if (_dotDirection == YYTextDirectionBottom) {
+        insets.bottom = -kTouchDotExtend;
+    } else if (_dotDirection == YYTextDirectionLeft) {
+        insets.left = -kTouchDotExtend;
+    }
+    rect = UIEdgeInsetsInsetRect(rect, insets);
+    return rect;
+}
+
+@end
+
+
+
+@interface YYTextSelectionView ()
+@property (nonatomic, strong) NSTimer *caretTimer;
+@property (nonatomic, strong) UIView *caretView;
+@property (nonatomic, strong) YYSelectionGrabber *startGrabber;
+@property (nonatomic, strong) YYSelectionGrabber *endGrabber;
+@property (nonatomic, strong) NSMutableArray *markViews;
+@end
+
+@implementation YYTextSelectionView
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    self = [super initWithFrame:frame];
+    if (!self) return nil;
+    
+    self.userInteractionEnabled = NO;
+    self.clipsToBounds = NO;
+    _markViews = [NSMutableArray array];
+    _caretView = [UIView new];
+    _caretView.hidden = YES;
+    _startGrabber = [YYSelectionGrabber new];
+    _startGrabber.dotDirection = YYTextDirectionTop;
+    _startGrabber.hidden = YES;
+    _endGrabber = [YYSelectionGrabber new];
+    _endGrabber.dotDirection = YYTextDirectionBottom;
+    _endGrabber.hidden = YES;
+    
+    [self addSubview:_startGrabber];
+    [self addSubview:_endGrabber];
+    [self addSubview:_caretView];
+    
+    return self;
+}
+
+- (void)dealloc {
+    [_caretTimer invalidate];
+}
+
+- (void)setColor:(UIColor *)color {
+    _color = color;
+    self.caretView.backgroundColor = color;
+    self.startGrabber.color = color;
+    self.endGrabber.color = color;
+    [self.markViews enumerateObjectsUsingBlock: ^(UIView *v, NSUInteger idx, BOOL *stop) {
+        v.backgroundColor = color;
+    }];
+}
+
+- (void)setCaretBlinks:(BOOL)caretBlinks {
+    if (_caretBlinks != caretBlinks) {
+        _caretView.alpha = 1;
+        [self.class cancelPreviousPerformRequestsWithTarget:self selector:@selector(_startBlinks) object:nil];
+        if (caretBlinks) {
+            [self performSelector:@selector(_startBlinks) withObject:nil afterDelay:kBlinkFirstDelay];
+        } else {
+            [_caretTimer invalidate];
+            _caretTimer = nil;
+        }
+        _caretBlinks = caretBlinks;
+    }
+}
+
+- (void)_startBlinks {
+    [_caretTimer invalidate];
+    if (_caretVisible) {
+        _caretTimer = [NSTimer timerWithTimeInterval:kBlinkDuration target:[YYTextWeakProxy proxyWithTarget:self] selector:@selector(_doBlink) userInfo:nil repeats:YES];
+        [[NSRunLoop currentRunLoop] addTimer:_caretTimer forMode:NSDefaultRunLoopMode];
+    } else {
+        _caretView.alpha = 1;
+    }
+}
+
+- (void)_doBlink {
+    [UIView animateWithDuration:kBlinkFadeDuration delay:0 options:UIViewAnimationOptionCurveEaseInOut animations: ^{
+        if (_caretView.alpha == 1) _caretView.alpha = 0;
+        else _caretView.alpha = 1;
+    } completion:NULL];
+}
+
+- (void)setCaretVisible:(BOOL)caretVisible {
+    _caretVisible = caretVisible;
+    self.caretView.hidden = !caretVisible;
+    _caretView.alpha = 1;
+    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_startBlinks) object:nil];
+    if (_caretBlinks) {
+        [self performSelector:@selector(_startBlinks) withObject:nil afterDelay:kBlinkFirstDelay];
+    }
+}
+
+- (void)setVerticalForm:(BOOL)verticalForm {
+    if (_verticalForm != verticalForm) {
+        _verticalForm = verticalForm;
+        [self setCaretRect:_caretRect];
+        self.startGrabber.dotDirection = verticalForm ? YYTextDirectionRight : YYTextDirectionTop;
+        self.endGrabber.dotDirection = verticalForm ? YYTextDirectionLeft : YYTextDirectionBottom;
+    }
+}
+
+- (CGRect)_standardCaretRect:(CGRect)caretRect {
+    caretRect = CGRectStandardize(caretRect);
+    if (_verticalForm) {
+        if (caretRect.size.height == 0) {
+            caretRect.size.height = kLineWidth;
+            caretRect.origin.y -= kLineWidth * 0.5;
+        }
+        if (caretRect.origin.y < 0) {
+            caretRect.origin.y = 0;
+        } else if (caretRect.origin.y + caretRect.size.height > self.bounds.size.height) {
+            caretRect.origin.y = self.bounds.size.height - caretRect.size.height;
+        }
+    } else {
+        if (caretRect.size.width == 0) {
+            caretRect.size.width = kLineWidth;
+            caretRect.origin.x -= kLineWidth * 0.5;
+        }
+        if (caretRect.origin.x < 0) {
+            caretRect.origin.x = 0;
+        } else if (caretRect.origin.x + caretRect.size.width > self.bounds.size.width) {
+            caretRect.origin.x = self.bounds.size.width - caretRect.size.width;
+        }
+    }
+    caretRect = YYTextCGRectPixelRound(caretRect);
+    if (isnan(caretRect.origin.x) || isinf(caretRect.origin.x)) caretRect.origin.x = 0;
+    if (isnan(caretRect.origin.y) || isinf(caretRect.origin.y)) caretRect.origin.y = 0;
+    if (isnan(caretRect.size.width) || isinf(caretRect.size.width)) caretRect.size.width = 0;
+    if (isnan(caretRect.size.height) || isinf(caretRect.size.height)) caretRect.size.height = 0;
+    return caretRect;
+}
+
+- (void)setCaretRect:(CGRect)caretRect {
+    _caretRect = caretRect;
+    self.caretView.frame = [self _standardCaretRect:caretRect];
+    CGFloat minWidth = MIN(self.caretView.bounds.size.width, self.caretView.bounds.size.height);
+    self.caretView.layer.cornerRadius = minWidth / 2;
+}
+
+- (void)setSelectionRects:(NSArray *)selectionRects {
+    _selectionRects = selectionRects.copy;
+    [self.markViews enumerateObjectsUsingBlock: ^(UIView *v, NSUInteger idx, BOOL *stop) {
+        [v removeFromSuperview];
+    }];
+    [self.markViews removeAllObjects];
+    self.startGrabber.hidden = YES;
+    self.endGrabber.hidden = YES;
+    
+    [selectionRects enumerateObjectsUsingBlock: ^(YYTextSelectionRect *r, NSUInteger idx, BOOL *stop) {
+        CGRect rect = r.rect;
+        rect = CGRectStandardize(rect);
+        rect = YYTextCGRectPixelRound(rect);
+        if (r.containsStart || r.containsEnd) {
+            rect = [self _standardCaretRect:rect];
+            if (r.containsStart) {
+                self.startGrabber.hidden = NO;
+                self.startGrabber.frame = rect;
+            }
+            if (r.containsEnd) {
+                self.endGrabber.hidden = NO;
+                self.endGrabber.frame = rect;
+            }
+        } else {
+            if (rect.size.width > 0 && rect.size.height > 0) {
+                UIView *mark = [[UIView alloc] initWithFrame:rect];
+                mark.backgroundColor = _color;
+                mark.alpha = kMarkAlpha;
+                [self insertSubview:mark atIndex:0];
+                [self.markViews addObject:mark];
+            }
+        }
+    }];
+}
+
+- (BOOL)isGrabberContainsPoint:(CGPoint)point {
+    return [self isStartGrabberContainsPoint:point] || [self isEndGrabberContainsPoint:point];
+}
+
+- (BOOL)isStartGrabberContainsPoint:(CGPoint)point {
+    if (_startGrabber.hidden) return NO;
+    CGRect startRect = [_startGrabber touchRect];
+    CGRect endRect = [_endGrabber touchRect];
+    if (CGRectIntersectsRect(startRect, endRect)) {
+        CGFloat distStart = YYTextCGPointGetDistanceToPoint(point, YYTextCGRectGetCenter(startRect));
+        CGFloat distEnd = YYTextCGPointGetDistanceToPoint(point, YYTextCGRectGetCenter(endRect));
+        if (distEnd <= distStart) return NO;
+    }
+    return CGRectContainsPoint(startRect, point);
+}
+
+- (BOOL)isEndGrabberContainsPoint:(CGPoint)point {
+    if (_endGrabber.hidden) return NO;
+    CGRect startRect = [_startGrabber touchRect];
+    CGRect endRect = [_endGrabber touchRect];
+    if (CGRectIntersectsRect(startRect, endRect)) {
+        CGFloat distStart = YYTextCGPointGetDistanceToPoint(point, YYTextCGRectGetCenter(startRect));
+        CGFloat distEnd = YYTextCGPointGetDistanceToPoint(point, YYTextCGRectGetCenter(endRect));
+        if (distEnd > distStart) return NO;
+    }
+    return CGRectContainsPoint(endRect, point);
+}
+
+- (BOOL)isCaretContainsPoint:(CGPoint)point {
+    if (_caretVisible) {
+        CGRect rect = CGRectInset(_caretRect, -kTouchTestExtend, -kTouchTestExtend);
+        return CGRectContainsPoint(rect, point);
+    }
+    return NO;
+}
+
+- (BOOL)isSelectionRectsContainsPoint:(CGPoint)point {
+    if (_selectionRects.count == 0) return NO;
+    for (YYTextSelectionRect *rect in _selectionRects) {
+        if (CGRectContainsPoint(rect.rect, point)) return YES;
+    }
+    return NO;
+}
+
+@end

+ 33 - 0
Pods/YYText/YYText/String/YYTextArchiver.h

@@ -0,0 +1,33 @@
+//
+//  YYTextArchiver.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/3/16.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ A subclass of `NSKeyedArchiver` which implement `NSKeyedArchiverDelegate` protocol.
+ 
+ The archiver can encode the object which contains
+ CGColor/CGImage/CTRunDelegateRef/.. (such as NSAttributedString).
+ */
+@interface YYTextArchiver : NSKeyedArchiver <NSKeyedArchiverDelegate>
+@end
+
+/**
+ A subclass of `NSKeyedUnarchiver` which implement `NSKeyedUnarchiverDelegate` 
+ protocol. The unarchiver can decode the data which is encoded by 
+ `YYTextArchiver` or `NSKeyedArchiver`.
+ */
+@interface YYTextUnarchiver : NSKeyedUnarchiver <NSKeyedUnarchiverDelegate>
+@end
+
+NS_ASSUME_NONNULL_END

+ 252 - 0
Pods/YYText/YYText/String/YYTextArchiver.m

@@ -0,0 +1,252 @@
+//
+//  YYTextArchiver.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/3/16.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextArchiver.h"
+#import "YYTextRunDelegate.h"
+#import "YYTextRubyAnnotation.h"
+
+/**
+ When call CTRunDelegateGetTypeID() on some devices (runs iOS6), I got the error:
+ "dyld: lazy symbol binding failed: Symbol not found: _CTRunDelegateGetTypeID"
+ 
+ Here's a workaround for this issue.
+ */
+static CFTypeID CTRunDelegateTypeID() {
+    static CFTypeID typeID;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        /*
+        if ((long)CTRunDelegateGetTypeID + 1 > 1) { //avoid compiler optimization
+            typeID = CTRunDelegateGetTypeID();
+        }
+         */
+        YYTextRunDelegate *delegate = [YYTextRunDelegate new];
+        CTRunDelegateRef ref = delegate.CTRunDelegate;
+        typeID = CFGetTypeID(ref);
+        CFRelease(ref);
+    });
+    return typeID;
+}
+
+static CFTypeID CTRubyAnnotationTypeID() {
+    static CFTypeID typeID;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        if ((long)CTRubyAnnotationGetTypeID + 1 > 1) { //avoid compiler optimization
+            typeID = CTRunDelegateGetTypeID();
+        } else {
+            typeID = kCFNotFound;
+        }
+    });
+    return typeID;
+}
+
+/**
+ A wrapper for CGColorRef. Used for Archive/Unarchive/Copy.
+ */
+@interface _YYCGColor : NSObject <NSCopying, NSCoding>
+@property (nonatomic, assign) CGColorRef CGColor;
++ (instancetype)colorWithCGColor:(CGColorRef)CGColor;
+@end
+
+@implementation _YYCGColor
+
++ (instancetype)colorWithCGColor:(CGColorRef)CGColor {
+    _YYCGColor *color = [self new];
+    color.CGColor = CGColor;
+    return color;
+}
+
+- (void)setCGColor:(CGColorRef)CGColor {
+    if (_CGColor != CGColor) {
+        if (CGColor) CGColor = (CGColorRef)CFRetain(CGColor);
+        if (_CGColor) CFRelease(_CGColor);
+        _CGColor = CGColor;
+    }
+}
+
+- (void)dealloc {
+    if (_CGColor) CFRelease(_CGColor);
+    _CGColor = NULL;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    _YYCGColor *color = [self.class new];
+    color.CGColor = self.CGColor;
+    return color;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    UIColor *color = [UIColor colorWithCGColor:_CGColor];
+    [aCoder encodeObject:color forKey:@"color"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [self init];
+    UIColor *color = [aDecoder decodeObjectForKey:@"color"];
+    self.CGColor = color.CGColor;
+    return self;
+}
+
+@end
+
+/**
+ A wrapper for CGImageRef. Used for Archive/Unarchive/Copy.
+ */
+@interface _YYCGImage : NSObject <NSCoding, NSCopying>
+@property (nonatomic, assign) CGImageRef CGImage;
++ (instancetype)imageWithCGImage:(CGImageRef)CGImage;
+@end
+
+@implementation _YYCGImage
+
++ (instancetype)imageWithCGImage:(CGImageRef)CGImage {
+    _YYCGImage *image = [self new];
+    image.CGImage = CGImage;
+    return image;
+}
+
+- (void)setCGImage:(CGImageRef)CGImage {
+    if (_CGImage != CGImage) {
+        if (CGImage) CGImage = (CGImageRef)CFRetain(CGImage);
+        if (_CGImage) CFRelease(_CGImage);
+        _CGImage = CGImage;
+    }
+}
+
+- (void)dealloc {
+    if (_CGImage) CFRelease(_CGImage);
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    _YYCGImage *image = [self.class new];
+    image.CGImage = self.CGImage;
+    return image;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    UIImage *image = [UIImage imageWithCGImage:_CGImage];
+    [aCoder encodeObject:image forKey:@"image"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [self init];
+    UIImage *image = [aDecoder decodeObjectForKey:@"image"];
+    self.CGImage = image.CGImage;
+    return self;
+}
+
+@end
+
+
+@implementation YYTextArchiver
+
++ (NSData *)archivedDataWithRootObject:(id)rootObject {
+    if (!rootObject) return nil;
+    NSMutableData *data = [NSMutableData data];
+    YYTextArchiver *archiver = [[[self class] alloc] initForWritingWithMutableData:data];
+    [archiver encodeRootObject:rootObject];
+    [archiver finishEncoding];
+    return data;
+}
+
++ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path {
+    NSData *data = [self archivedDataWithRootObject:rootObject];
+    if (!data) return NO;
+    return [data writeToFile:path atomically:YES];
+}
+
+- (instancetype)init {
+    self = [super init];
+    self.delegate = self;
+    return self;
+}
+
+- (instancetype)initForWritingWithMutableData:(NSMutableData *)data {
+    self = [super initForWritingWithMutableData:data];
+    self.delegate = self;
+    return self;
+}
+
+- (id)archiver:(NSKeyedArchiver *)archiver willEncodeObject:(id)object {
+    CFTypeID typeID = CFGetTypeID((CFTypeRef)object);
+    if (typeID == CTRunDelegateTypeID()) {
+        CTRunDelegateRef runDelegate = (__bridge CFTypeRef)(object);
+        id ref = CTRunDelegateGetRefCon(runDelegate);
+        if (ref) return ref;
+    } else if (typeID == CTRubyAnnotationTypeID()) {
+        CTRubyAnnotationRef ctRuby = (__bridge CFTypeRef)(object);
+        YYTextRubyAnnotation *ruby = [YYTextRubyAnnotation rubyWithCTRubyRef:ctRuby];
+        if (ruby) return ruby;
+    } else if (typeID == CGColorGetTypeID()) {
+        return [_YYCGColor colorWithCGColor:(CGColorRef)object];
+    } else if (typeID == CGImageGetTypeID()) {
+        return [_YYCGImage imageWithCGImage:(CGImageRef)object];
+    }
+    return object;
+}
+
+@end
+
+
+@implementation YYTextUnarchiver
+
++ (id)unarchiveObjectWithData:(NSData *)data {
+    if (data.length == 0) return nil;
+    YYTextUnarchiver *unarchiver = [[self alloc] initForReadingWithData:data];
+    return [unarchiver decodeObject];
+}
+
++ (id)unarchiveObjectWithFile:(NSString *)path {
+    NSData *data = [NSData dataWithContentsOfFile:path];
+    return [self unarchiveObjectWithData:data];
+}
+
+- (instancetype)init {
+    self = [super init];
+    self.delegate = self;
+    return self;
+}
+
+- (instancetype)initForReadingWithData:(NSData *)data {
+    self = [super initForReadingWithData:data];
+    self.delegate = self;
+    return self;
+}
+
+- (id)unarchiver:(NSKeyedUnarchiver *)unarchiver didDecodeObject:(id) NS_RELEASES_ARGUMENT object NS_RETURNS_RETAINED {
+    if ([object class] == [YYTextRunDelegate class]) {
+        YYTextRunDelegate *runDelegate = object;
+        CTRunDelegateRef ct = runDelegate.CTRunDelegate;
+        id ctObj = (__bridge id)ct;
+        if (ct) CFRelease(ct);
+        return ctObj;
+    } else if ([object class] == [YYTextRubyAnnotation class]) {
+        YYTextRubyAnnotation *ruby = object;
+        if ([UIDevice currentDevice].systemVersion.floatValue >= 8) {
+            CTRubyAnnotationRef ct = ruby.CTRubyAnnotation;
+            id ctObj = (__bridge id)(ct);
+            if (ct) CFRelease(ct);
+            return ctObj;
+        } else {
+            return object;
+        }
+    } else if ([object class] == [_YYCGColor class]) {
+        _YYCGColor *color = object;
+        return (id)color.CGColor;
+    } else if ([object class] == [_YYCGImage class]) {
+        _YYCGImage *image = object;
+        return (id)image.CGImage;
+    }
+    return object;
+}
+
+@end

+ 347 - 0
Pods/YYText/YYText/String/YYTextAttribute.h

@@ -0,0 +1,347 @@
+//
+//  YYTextAttribute.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 14/10/26.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+#pragma mark - Enum Define
+
+/// The attribute type
+typedef NS_OPTIONS(NSInteger, YYTextAttributeType) {
+    YYTextAttributeTypeNone     = 0,
+    YYTextAttributeTypeUIKit    = 1 << 0, ///< UIKit attributes, such as UILabel/UITextField/drawInRect.
+    YYTextAttributeTypeCoreText = 1 << 1, ///< CoreText attributes, used by CoreText.
+    YYTextAttributeTypeYYText   = 1 << 2, ///< YYText attributes, used by YYText.
+};
+
+/// Get the attribute type from an attribute name.
+extern YYTextAttributeType YYTextAttributeGetType(NSString *attributeName);
+
+/**
+ Line style in YYText (similar to NSUnderlineStyle).
+ */
+typedef NS_OPTIONS (NSInteger, YYTextLineStyle) {
+    // basic style (bitmask:0xFF)
+    YYTextLineStyleNone       = 0x00, ///< (        ) Do not draw a line (Default).
+    YYTextLineStyleSingle     = 0x01, ///< (──────) Draw a single line.
+    YYTextLineStyleThick      = 0x02, ///< (━━━━━━━) Draw a thick line.
+    YYTextLineStyleDouble     = 0x09, ///< (══════) Draw a double line.
+    
+    // style pattern (bitmask:0xF00)
+    YYTextLineStylePatternSolid      = 0x000, ///< (────────) Draw a solid line (Default).
+    YYTextLineStylePatternDot        = 0x100, ///< (‑ ‑ ‑ ‑ ‑ ‑) Draw a line of dots.
+    YYTextLineStylePatternDash       = 0x200, ///< (— — — —) Draw a line of dashes.
+    YYTextLineStylePatternDashDot    = 0x300, ///< (— ‑ — ‑ — ‑) Draw a line of alternating dashes and dots.
+    YYTextLineStylePatternDashDotDot = 0x400, ///< (— ‑ ‑ — ‑ ‑) Draw a line of alternating dashes and two dots.
+    YYTextLineStylePatternCircleDot  = 0x900, ///< (••••••••••••) Draw a line of small circle dots.
+};
+
+/**
+ Text vertical alignment.
+ */
+typedef NS_ENUM(NSInteger, YYTextVerticalAlignment) {
+    YYTextVerticalAlignmentTop =    0, ///< Top alignment.
+    YYTextVerticalAlignmentCenter = 1, ///< Center alignment.
+    YYTextVerticalAlignmentBottom = 2, ///< Bottom alignment.
+};
+
+/**
+ The direction define in YYText.
+ */
+typedef NS_OPTIONS(NSUInteger, YYTextDirection) {
+    YYTextDirectionNone   = 0,
+    YYTextDirectionTop    = 1 << 0,
+    YYTextDirectionRight  = 1 << 1,
+    YYTextDirectionBottom = 1 << 2,
+    YYTextDirectionLeft   = 1 << 3,
+};
+
+/**
+ The trunction type, tells the truncation engine which type of truncation is being requested.
+ */
+typedef NS_ENUM (NSUInteger, YYTextTruncationType) {
+    /// No truncate.
+    YYTextTruncationTypeNone   = 0,
+    
+    /// Truncate at the beginning of the line, leaving the end portion visible.
+    YYTextTruncationTypeStart  = 1,
+    
+    /// Truncate at the end of the line, leaving the start portion visible.
+    YYTextTruncationTypeEnd    = 2,
+    
+    /// Truncate in the middle of the line, leaving both the start and the end portions visible.
+    YYTextTruncationTypeMiddle = 3,
+};
+
+
+
+#pragma mark - Attribute Name Defined in YYText
+
+/// The value of this attribute is a `YYTextBackedString` object.
+/// Use this attribute to store the original plain text if it is replaced by something else (such as attachment).
+UIKIT_EXTERN NSString *const YYTextBackedStringAttributeName;
+
+/// The value of this attribute is a `YYTextBinding` object.
+/// Use this attribute to bind a range of text together, as if it was a single charactor.
+UIKIT_EXTERN NSString *const YYTextBindingAttributeName;
+
+/// The value of this attribute is a `YYTextShadow` object.
+/// Use this attribute to add shadow to a range of text.
+/// Shadow will be drawn below text glyphs. Use YYTextShadow.subShadow to add multi-shadow.
+UIKIT_EXTERN NSString *const YYTextShadowAttributeName;
+
+/// The value of this attribute is a `YYTextShadow` object.
+/// Use this attribute to add inner shadow to a range of text.
+/// Inner shadow will be drawn above text glyphs. Use YYTextShadow.subShadow to add multi-shadow.
+UIKIT_EXTERN NSString *const YYTextInnerShadowAttributeName;
+
+/// The value of this attribute is a `YYTextDecoration` object.
+/// Use this attribute to add underline to a range of text.
+/// The underline will be drawn below text glyphs.
+UIKIT_EXTERN NSString *const YYTextUnderlineAttributeName;
+
+/// The value of this attribute is a `YYTextDecoration` object.
+/// Use this attribute to add strikethrough (delete line) to a range of text.
+/// The strikethrough will be drawn above text glyphs.
+UIKIT_EXTERN NSString *const YYTextStrikethroughAttributeName;
+
+/// The value of this attribute is a `YYTextBorder` object.
+/// Use this attribute to add cover border or cover color to a range of text.
+/// The border will be drawn above the text glyphs.
+UIKIT_EXTERN NSString *const YYTextBorderAttributeName;
+
+/// The value of this attribute is a `YYTextBorder` object.
+/// Use this attribute to add background border or background color to a range of text.
+/// The border will be drawn below the text glyphs.
+UIKIT_EXTERN NSString *const YYTextBackgroundBorderAttributeName;
+
+/// The value of this attribute is a `YYTextBorder` object.
+/// Use this attribute to add a code block border to one or more line of text.
+/// The border will be drawn below the text glyphs.
+UIKIT_EXTERN NSString *const YYTextBlockBorderAttributeName;
+
+/// The value of this attribute is a `YYTextAttachment` object.
+/// Use this attribute to add attachment to text.
+/// It should be used in conjunction with a CTRunDelegate.
+UIKIT_EXTERN NSString *const YYTextAttachmentAttributeName;
+
+/// The value of this attribute is a `YYTextHighlight` object.
+/// Use this attribute to add a touchable highlight state to a range of text.
+UIKIT_EXTERN NSString *const YYTextHighlightAttributeName;
+
+/// The value of this attribute is a `NSValue` object stores CGAffineTransform.
+/// Use this attribute to add transform to each glyph in a range of text.
+UIKIT_EXTERN NSString *const YYTextGlyphTransformAttributeName;
+
+
+
+#pragma mark - String Token Define
+
+UIKIT_EXTERN NSString *const YYTextAttachmentToken; ///< Object replacement character (U+FFFC), used for text attachment.
+UIKIT_EXTERN NSString *const YYTextTruncationToken; ///< Horizontal ellipsis (U+2026), used for text truncation  "…".
+
+
+
+#pragma mark - Attribute Value Define
+
+/**
+ The tap/long press action callback defined in YYText.
+ 
+ @param containerView The text container view (such as YYLabel/YYTextView).
+ @param text          The whole text.
+ @param range         The text range in `text` (if no range, the range.location is NSNotFound).
+ @param rect          The text frame in `containerView` (if no data, the rect is CGRectNull).
+ */
+typedef void(^YYTextAction)(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect);
+
+
+/**
+ YYTextBackedString objects are used by the NSAttributedString class cluster
+ as the values for text backed string attributes (stored in the attributed 
+ string under the key named YYTextBackedStringAttributeName).
+ 
+ It may used for copy/paste plain text from attributed string.
+ Example: If :) is replace by a custom emoji (such as😊), the backed string can be set to @":)".
+ */
+@interface YYTextBackedString : NSObject <NSCoding, NSCopying>
++ (instancetype)stringWithString:(nullable NSString *)string;
+@property (nullable, nonatomic, copy) NSString *string; ///< backed string
+@end
+
+
+/**
+ YYTextBinding objects are used by the NSAttributedString class cluster
+ as the values for shadow attributes (stored in the attributed string under
+ the key named YYTextBindingAttributeName).
+ 
+ Add this to a range of text will make the specified characters 'binding together'.
+ YYTextView will treat the range of text as a single character during text 
+ selection and edit.
+ */
+@interface YYTextBinding : NSObject <NSCoding, NSCopying>
++ (instancetype)bindingWithDeleteConfirm:(BOOL)deleteConfirm;
+@property (nonatomic) BOOL deleteConfirm; ///< confirm the range when delete in YYTextView
+@end
+
+
+/**
+ YYTextShadow objects are used by the NSAttributedString class cluster
+ as the values for shadow attributes (stored in the attributed string under
+ the key named YYTextShadowAttributeName or YYTextInnerShadowAttributeName).
+ 
+ It's similar to `NSShadow`, but offers more options.
+ */
+@interface YYTextShadow : NSObject <NSCoding, NSCopying>
++ (instancetype)shadowWithColor:(nullable UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius;
+
+@property (nullable, nonatomic, strong) UIColor *color; ///< shadow color
+@property (nonatomic) CGSize offset;                    ///< shadow offset
+@property (nonatomic) CGFloat radius;                   ///< shadow blur radius
+@property (nonatomic) CGBlendMode blendMode;            ///< shadow blend mode
+@property (nullable, nonatomic, strong) YYTextShadow *subShadow;  ///< a sub shadow which will be added above the parent shadow
+
++ (instancetype)shadowWithNSShadow:(NSShadow *)nsShadow; ///< convert NSShadow to YYTextShadow
+- (NSShadow *)nsShadow; ///< convert YYTextShadow to NSShadow
+@end
+
+
+/**
+ YYTextDecorationLine objects are used by the NSAttributedString class cluster
+ as the values for decoration line attributes (stored in the attributed string under
+ the key named YYTextUnderlineAttributeName or YYTextStrikethroughAttributeName).
+ 
+ When it's used as underline, the line is drawn below text glyphs;
+ when it's used as strikethrough, the line is drawn above text glyphs.
+ */
+@interface YYTextDecoration : NSObject <NSCoding, NSCopying>
++ (instancetype)decorationWithStyle:(YYTextLineStyle)style;
++ (instancetype)decorationWithStyle:(YYTextLineStyle)style width:(nullable NSNumber *)width color:(nullable UIColor *)color;
+@property (nonatomic) YYTextLineStyle style;                   ///< line style
+@property (nullable, nonatomic, strong) NSNumber *width;       ///< line width (nil means automatic width)
+@property (nullable, nonatomic, strong) UIColor *color;        ///< line color (nil means automatic color)
+@property (nullable, nonatomic, strong) YYTextShadow *shadow;  ///< line shadow
+@end
+
+
+/**
+ YYTextBorder objects are used by the NSAttributedString class cluster
+ as the values for border attributes (stored in the attributed string under
+ the key named YYTextBorderAttributeName or YYTextBackgroundBorderAttributeName).
+ 
+ It can be used to draw a border around a range of text, or draw a background
+ to a range of text.
+ 
+ Example:
+    ╭──────╮
+    │ Text │
+    ╰──────╯
+ */
+@interface YYTextBorder : NSObject <NSCoding, NSCopying>
++ (instancetype)borderWithLineStyle:(YYTextLineStyle)lineStyle lineWidth:(CGFloat)width strokeColor:(nullable UIColor *)color;
++ (instancetype)borderWithFillColor:(nullable UIColor *)color cornerRadius:(CGFloat)cornerRadius;
+@property (nonatomic) YYTextLineStyle lineStyle;              ///< border line style
+@property (nonatomic) CGFloat strokeWidth;                    ///< border line width
+@property (nullable, nonatomic, strong) UIColor *strokeColor; ///< border line color
+@property (nonatomic) CGLineJoin lineJoin;                    ///< border line join
+@property (nonatomic) UIEdgeInsets insets;                    ///< border insets for text bounds
+@property (nonatomic) CGFloat cornerRadius;                   ///< border corder radius
+@property (nullable, nonatomic, strong) YYTextShadow *shadow; ///< border shadow
+@property (nullable, nonatomic, strong) UIColor *fillColor;   ///< inner fill color
+@end
+
+
+/**
+ YYTextAttachment objects are used by the NSAttributedString class cluster 
+ as the values for attachment attributes (stored in the attributed string under 
+ the key named YYTextAttachmentAttributeName).
+ 
+ When display an attributed string which contains `YYTextAttachment` object,
+ the content will be placed in text metric. If the content is `UIImage`, 
+ then it will be drawn to CGContext; if the content is `UIView` or `CALayer`, 
+ then it will be added to the text container's view or layer.
+ */
+@interface YYTextAttachment : NSObject<NSCoding, NSCopying>
++ (instancetype)attachmentWithContent:(nullable id)content;
+@property (nullable, nonatomic, strong) id content;             ///< Supported type: UIImage, UIView, CALayer
+@property (nonatomic) UIViewContentMode contentMode;            ///< Content display mode.
+@property (nonatomic) UIEdgeInsets contentInsets;               ///< The insets when drawing content.
+@property (nullable, nonatomic, strong) NSDictionary *userInfo; ///< The user information dictionary.
+@end
+
+
+/**
+ YYTextHighlight objects are used by the NSAttributedString class cluster
+ as the values for touchable highlight attributes (stored in the attributed string
+ under the key named YYTextHighlightAttributeName).
+ 
+ When display an attributed string in `YYLabel` or `YYTextView`, the range of 
+ highlight text can be toucheds down by users. If a range of text is turned into 
+ highlighted state, the `attributes` in `YYTextHighlight` will be used to modify 
+ (set or remove) the original attributes in the range for display.
+ */
+@interface YYTextHighlight : NSObject <NSCoding, NSCopying>
+
+/**
+ Attributes that you can apply to text in an attributed string when highlight.
+ Key:   Same as CoreText/YYText Attribute Name.
+ Value: Modify attribute value when highlight (NSNull for remove attribute).
+ */
+@property (nullable, nonatomic, copy) NSDictionary<NSString *, id> *attributes;
+
+/**
+ Creates a highlight object with specified attributes.
+ 
+ @param attributes The attributes which will replace original attributes when highlight,
+        If the value is NSNull, it will removed when highlight.
+ */
++ (instancetype)highlightWithAttributes:(nullable NSDictionary<NSString *, id> *)attributes;
+
+/**
+ Convenience methods to create a default highlight with the specifeid background color.
+ 
+ @param color The background border color.
+ */
++ (instancetype)highlightWithBackgroundColor:(nullable UIColor *)color;
+
+// Convenience methods below to set the `attributes`.
+- (void)setFont:(nullable UIFont *)font;
+- (void)setColor:(nullable UIColor *)color;
+- (void)setStrokeWidth:(nullable NSNumber *)width;
+- (void)setStrokeColor:(nullable UIColor *)color;
+- (void)setShadow:(nullable YYTextShadow *)shadow;
+- (void)setInnerShadow:(nullable YYTextShadow *)shadow;
+- (void)setUnderline:(nullable YYTextDecoration *)underline;
+- (void)setStrikethrough:(nullable YYTextDecoration *)strikethrough;
+- (void)setBackgroundBorder:(nullable YYTextBorder *)border;
+- (void)setBorder:(nullable YYTextBorder *)border;
+- (void)setAttachment:(nullable YYTextAttachment *)attachment;
+
+/**
+ The user information dictionary, default is nil.
+ */
+@property (nullable, nonatomic, copy) NSDictionary *userInfo;
+
+/**
+ Tap action when user tap the highlight, default is nil.
+ If the value is nil, YYTextView or YYLabel will ask it's delegate to handle the tap action.
+ */
+@property (nullable, nonatomic, copy) YYTextAction tapAction;
+
+/**
+ Long press action when user long press the highlight, default is nil.
+ If the value is nil, YYTextView or YYLabel will ask it's delegate to handle the long press action.
+ */
+@property (nullable, nonatomic, copy) YYTextAction longPressAction;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 524 - 0
Pods/YYText/YYText/String/YYTextAttribute.m

@@ -0,0 +1,524 @@
+//
+//  YYTextAttribute.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 14/10/26.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextAttribute.h"
+#import <UIKit/UIKit.h>
+#import <CoreText/CoreText.h>
+#import "NSAttributedString+YYText.h"
+#import "YYTextArchiver.h"
+
+
+static double _YYDeviceSystemVersion() {
+    static double version;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        version = [UIDevice currentDevice].systemVersion.doubleValue;
+    });
+    return version;
+}
+
+
+NSString *const YYTextBackedStringAttributeName = @"YYTextBackedString";
+NSString *const YYTextBindingAttributeName = @"YYTextBinding";
+NSString *const YYTextShadowAttributeName = @"YYTextShadow";
+NSString *const YYTextInnerShadowAttributeName = @"YYTextInnerShadow";
+NSString *const YYTextUnderlineAttributeName = @"YYTextUnderline";
+NSString *const YYTextStrikethroughAttributeName = @"YYTextStrikethrough";
+NSString *const YYTextBorderAttributeName = @"YYTextBorder";
+NSString *const YYTextBackgroundBorderAttributeName = @"YYTextBackgroundBorder";
+NSString *const YYTextBlockBorderAttributeName = @"YYTextBlockBorder";
+NSString *const YYTextAttachmentAttributeName = @"YYTextAttachment";
+NSString *const YYTextHighlightAttributeName = @"YYTextHighlight";
+NSString *const YYTextGlyphTransformAttributeName = @"YYTextGlyphTransform";
+
+NSString *const YYTextAttachmentToken = @"\uFFFC";
+NSString *const YYTextTruncationToken = @"\u2026";
+
+
+YYTextAttributeType YYTextAttributeGetType(NSString *name){
+    if (name.length == 0) return YYTextAttributeTypeNone;
+    
+    static NSMutableDictionary *dic;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        dic = [NSMutableDictionary new];
+        NSNumber *All = @(YYTextAttributeTypeUIKit | YYTextAttributeTypeCoreText | YYTextAttributeTypeYYText);
+        NSNumber *CoreText_YYText = @(YYTextAttributeTypeCoreText | YYTextAttributeTypeYYText);
+        NSNumber *UIKit_YYText = @(YYTextAttributeTypeUIKit | YYTextAttributeTypeYYText);
+        NSNumber *UIKit_CoreText = @(YYTextAttributeTypeUIKit | YYTextAttributeTypeCoreText);
+        NSNumber *UIKit = @(YYTextAttributeTypeUIKit);
+        NSNumber *CoreText = @(YYTextAttributeTypeCoreText);
+        NSNumber *YYText = @(YYTextAttributeTypeYYText);
+        
+        dic[NSFontAttributeName] = All;
+        dic[NSKernAttributeName] = All;
+        dic[NSForegroundColorAttributeName] = UIKit;
+        dic[(id)kCTForegroundColorAttributeName] = CoreText;
+        dic[(id)kCTForegroundColorFromContextAttributeName] = CoreText;
+        dic[NSBackgroundColorAttributeName] = UIKit;
+        dic[NSStrokeWidthAttributeName] = All;
+        dic[NSStrokeColorAttributeName] = UIKit;
+        dic[(id)kCTStrokeColorAttributeName] = CoreText_YYText;
+        dic[NSShadowAttributeName] = UIKit_YYText;
+        dic[NSStrikethroughStyleAttributeName] = UIKit;
+        dic[NSUnderlineStyleAttributeName] = UIKit_CoreText;
+        dic[(id)kCTUnderlineColorAttributeName] = CoreText;
+        dic[NSLigatureAttributeName] = All;
+        dic[(id)kCTSuperscriptAttributeName] = UIKit; //it's a CoreText attrubite, but only supported by UIKit...
+        dic[NSVerticalGlyphFormAttributeName] = All;
+        dic[(id)kCTGlyphInfoAttributeName] = CoreText_YYText;
+        dic[(id)kCTCharacterShapeAttributeName] = CoreText_YYText;
+        dic[(id)kCTRunDelegateAttributeName] = CoreText_YYText;
+        dic[(id)kCTBaselineClassAttributeName] = CoreText_YYText;
+        dic[(id)kCTBaselineInfoAttributeName] = CoreText_YYText;
+        dic[(id)kCTBaselineReferenceInfoAttributeName] = CoreText_YYText;
+        dic[(id)kCTWritingDirectionAttributeName] = CoreText_YYText;
+        dic[NSParagraphStyleAttributeName] = All;
+        
+        if (_YYDeviceSystemVersion() >= 7) {
+            dic[NSStrikethroughColorAttributeName] = UIKit;
+            dic[NSUnderlineColorAttributeName] = UIKit;
+            dic[NSTextEffectAttributeName] = UIKit;
+            dic[NSObliquenessAttributeName] = UIKit;
+            dic[NSExpansionAttributeName] = UIKit;
+            dic[(id)kCTLanguageAttributeName] = CoreText_YYText;
+            dic[NSBaselineOffsetAttributeName] = UIKit;
+            dic[NSWritingDirectionAttributeName] = All;
+            dic[NSAttachmentAttributeName] = UIKit;
+            dic[NSLinkAttributeName] = UIKit;
+        }
+        if (_YYDeviceSystemVersion() >= 8) {
+            dic[(id)kCTRubyAnnotationAttributeName] = CoreText;
+        }
+        
+        dic[YYTextBackedStringAttributeName] = YYText;
+        dic[YYTextBindingAttributeName] = YYText;
+        dic[YYTextShadowAttributeName] = YYText;
+        dic[YYTextInnerShadowAttributeName] = YYText;
+        dic[YYTextUnderlineAttributeName] = YYText;
+        dic[YYTextStrikethroughAttributeName] = YYText;
+        dic[YYTextBorderAttributeName] = YYText;
+        dic[YYTextBackgroundBorderAttributeName] = YYText;
+        dic[YYTextBlockBorderAttributeName] = YYText;
+        dic[YYTextAttachmentAttributeName] = YYText;
+        dic[YYTextHighlightAttributeName] = YYText;
+        dic[YYTextGlyphTransformAttributeName] = YYText;
+    });
+    NSNumber *num = dic[name];
+    if (num) return num.integerValue;
+    return YYTextAttributeTypeNone;
+}
+
+
+@implementation YYTextBackedString
+
++ (instancetype)stringWithString:(NSString *)string {
+    YYTextBackedString *one = [self new];
+    one.string = string;
+    return one;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [aCoder encodeObject:self.string forKey:@"string"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [super init];
+    _string = [aDecoder decodeObjectForKey:@"string"];
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    typeof(self) one = [self.class new];
+    one.string = self.string;
+    return one;
+}
+
+@end
+
+
+@implementation YYTextBinding
+
++ (instancetype)bindingWithDeleteConfirm:(BOOL)deleteConfirm {
+    YYTextBinding *one = [self new];
+    one.deleteConfirm = deleteConfirm;
+    return one;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [aCoder encodeObject:@(self.deleteConfirm) forKey:@"deleteConfirm"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [super init];
+    _deleteConfirm = ((NSNumber *)[aDecoder decodeObjectForKey:@"deleteConfirm"]).boolValue;
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    typeof(self) one = [self.class new];
+    one.deleteConfirm = self.deleteConfirm;
+    return one;
+}
+
+@end
+
+
+@implementation YYTextShadow
+
++ (instancetype)shadowWithColor:(UIColor *)color offset:(CGSize)offset radius:(CGFloat)radius {
+    YYTextShadow *one = [self new];
+    one.color = color;
+    one.offset = offset;
+    one.radius = radius;
+    return one;
+}
+
++ (instancetype)shadowWithNSShadow:(NSShadow *)nsShadow {
+    if (!nsShadow) return nil;
+    YYTextShadow *shadow = [self new];
+    shadow.offset = nsShadow.shadowOffset;
+    shadow.radius = nsShadow.shadowBlurRadius;
+    id color = nsShadow.shadowColor;
+    if (color) {
+        if (CGColorGetTypeID() == CFGetTypeID((__bridge CFTypeRef)(color))) {
+            color = [UIColor colorWithCGColor:(__bridge CGColorRef)(color)];
+        }
+        if ([color isKindOfClass:[UIColor class]]) {
+            shadow.color = color;
+        }
+    }
+    return shadow;
+}
+
+- (NSShadow *)nsShadow {
+    NSShadow *shadow = [NSShadow new];
+    shadow.shadowOffset = self.offset;
+    shadow.shadowBlurRadius = self.radius;
+    shadow.shadowColor = self.color;
+    return shadow;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [aCoder encodeObject:self.color forKey:@"color"];
+    [aCoder encodeObject:@(self.radius) forKey:@"radius"];
+    [aCoder encodeObject:[NSValue valueWithCGSize:self.offset] forKey:@"offset"];
+    [aCoder encodeObject:self.subShadow forKey:@"subShadow"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [super init];
+    _color = [aDecoder decodeObjectForKey:@"color"];
+    _radius = ((NSNumber *)[aDecoder decodeObjectForKey:@"radius"]).floatValue;
+    _offset = ((NSValue *)[aDecoder decodeObjectForKey:@"offset"]).CGSizeValue;
+    _subShadow = [aDecoder decodeObjectForKey:@"subShadow"];
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    typeof(self) one = [self.class new];
+    one.color = self.color;
+    one.radius = self.radius;
+    one.offset = self.offset;
+    one.subShadow = self.subShadow.copy;
+    return one;
+}
+
+@end
+
+
+@implementation YYTextDecoration
+
+- (instancetype)init {
+    self = [super init];
+    _style = YYTextLineStyleSingle;
+    return self;
+}
+
++ (instancetype)decorationWithStyle:(YYTextLineStyle)style {
+    YYTextDecoration *one = [self new];
+    one.style = style;
+    return one;
+}
++ (instancetype)decorationWithStyle:(YYTextLineStyle)style width:(NSNumber *)width color:(UIColor *)color {
+    YYTextDecoration *one = [self new];
+    one.style = style;
+    one.width = width;
+    one.color = color;
+    return one;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [aCoder encodeObject:@(self.style) forKey:@"style"];
+    [aCoder encodeObject:self.width forKey:@"width"];
+    [aCoder encodeObject:self.color forKey:@"color"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [super init];
+    self.style = ((NSNumber *)[aDecoder decodeObjectForKey:@"style"]).unsignedIntegerValue;
+    self.width = [aDecoder decodeObjectForKey:@"width"];
+    self.color = [aDecoder decodeObjectForKey:@"color"];
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    typeof(self) one = [self.class new];
+    one.style = self.style;
+    one.width = self.width;
+    one.color = self.color;
+    return one;
+}
+
+@end
+
+
+@implementation YYTextBorder
+
++ (instancetype)borderWithLineStyle:(YYTextLineStyle)lineStyle lineWidth:(CGFloat)width strokeColor:(UIColor *)color {
+    YYTextBorder *one = [self new];
+    one.lineStyle = lineStyle;
+    one.strokeWidth = width;
+    one.strokeColor = color;
+    return one;
+}
+
++ (instancetype)borderWithFillColor:(UIColor *)color cornerRadius:(CGFloat)cornerRadius {
+    YYTextBorder *one = [self new];
+    one.fillColor = color;
+    one.cornerRadius = cornerRadius;
+    one.insets = UIEdgeInsetsMake(-2, 0, 0, -2);
+    return one;
+}
+
+- (instancetype)init {
+    self = [super init];
+    self.lineStyle = YYTextLineStyleSingle;
+    return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [aCoder encodeObject:@(self.lineStyle) forKey:@"lineStyle"];
+    [aCoder encodeObject:@(self.strokeWidth) forKey:@"strokeWidth"];
+    [aCoder encodeObject:self.strokeColor forKey:@"strokeColor"];
+    [aCoder encodeObject:@(self.lineJoin) forKey:@"lineJoin"];
+    [aCoder encodeObject:[NSValue valueWithUIEdgeInsets:self.insets] forKey:@"insets"];
+    [aCoder encodeObject:@(self.cornerRadius) forKey:@"cornerRadius"];
+    [aCoder encodeObject:self.shadow forKey:@"shadow"];
+    [aCoder encodeObject:self.fillColor forKey:@"fillColor"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [super init];
+    _lineStyle = ((NSNumber *)[aDecoder decodeObjectForKey:@"lineStyle"]).unsignedIntegerValue;
+    _strokeWidth = ((NSNumber *)[aDecoder decodeObjectForKey:@"strokeWidth"]).doubleValue;
+    _strokeColor = [aDecoder decodeObjectForKey:@"strokeColor"];
+    _lineJoin = (CGLineJoin)((NSNumber *)[aDecoder decodeObjectForKey:@"join"]).unsignedIntegerValue;
+    _insets = ((NSValue *)[aDecoder decodeObjectForKey:@"insets"]).UIEdgeInsetsValue;
+    _cornerRadius = ((NSNumber *)[aDecoder decodeObjectForKey:@"cornerRadius"]).doubleValue;
+    _shadow = [aDecoder decodeObjectForKey:@"shadow"];
+    _fillColor = [aDecoder decodeObjectForKey:@"fillColor"];
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    typeof(self) one = [self.class new];
+    one.lineStyle = self.lineStyle;
+    one.strokeWidth = self.strokeWidth;
+    one.strokeColor = self.strokeColor;
+    one.lineJoin = self.lineJoin;
+    one.insets = self.insets;
+    one.cornerRadius = self.cornerRadius;
+    one.shadow = self.shadow.copy;
+    one.fillColor = self.fillColor;
+    return one;
+}
+
+@end
+
+
+@implementation YYTextAttachment
+
++ (instancetype)attachmentWithContent:(id)content {
+    YYTextAttachment *one = [self new];
+    one.content = content;
+    return one;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [aCoder encodeObject:self.content forKey:@"content"];
+    [aCoder encodeObject:[NSValue valueWithUIEdgeInsets:self.contentInsets] forKey:@"contentInsets"];
+    [aCoder encodeObject:self.userInfo forKey:@"userInfo"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [super init];
+    _content = [aDecoder decodeObjectForKey:@"content"];
+    _contentInsets = ((NSValue *)[aDecoder decodeObjectForKey:@"contentInsets"]).UIEdgeInsetsValue;
+    _userInfo = [aDecoder decodeObjectForKey:@"userInfo"];
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    typeof(self) one = [self.class new];
+    if ([self.content respondsToSelector:@selector(copy)]) {
+        one.content = [self.content copy];
+    } else {
+        one.content = self.content;
+    }
+    one.contentInsets = self.contentInsets;
+    one.userInfo = self.userInfo.copy;
+    return one;
+}
+
+@end
+
+
+@implementation YYTextHighlight
+
++ (instancetype)highlightWithAttributes:(NSDictionary *)attributes {
+    YYTextHighlight *one = [self new];
+    one.attributes = attributes;
+    return one;
+}
+
++ (instancetype)highlightWithBackgroundColor:(UIColor *)color {
+    YYTextBorder *highlightBorder = [YYTextBorder new];
+    highlightBorder.insets = UIEdgeInsetsMake(-2, -1, -2, -1);
+    highlightBorder.cornerRadius = 3;
+    highlightBorder.fillColor = color;
+    
+    YYTextHighlight *one = [self new];
+    [one setBackgroundBorder:highlightBorder];
+    return one;
+}
+
+- (void)setAttributes:(NSDictionary *)attributes {
+    _attributes = attributes.mutableCopy;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    NSData *data = nil;
+    @try {
+        data = [YYTextArchiver archivedDataWithRootObject:self.attributes];
+    }
+    @catch (NSException *exception) {
+        NSLog(@"%@",exception);
+    }
+    [aCoder encodeObject:data forKey:@"attributes"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [super init];
+    NSData *data = [aDecoder decodeObjectForKey:@"attributes"];
+    @try {
+        _attributes = [YYTextUnarchiver unarchiveObjectWithData:data];
+    }
+    @catch (NSException *exception) {
+        NSLog(@"%@",exception);
+    }
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    typeof(self) one = [self.class new];
+    one.attributes = self.attributes.mutableCopy;
+    return one;
+}
+
+- (void)_makeMutableAttributes {
+    if (!_attributes) {
+        _attributes = [NSMutableDictionary new];
+    } else if (![_attributes isKindOfClass:[NSMutableDictionary class]]) {
+        _attributes = _attributes.mutableCopy;
+    }
+}
+
+- (void)setFont:(UIFont *)font {
+    [self _makeMutableAttributes];
+    if (font == (id)[NSNull null] || font == nil) {
+        ((NSMutableDictionary *)_attributes)[(id)kCTFontAttributeName] = [NSNull null];
+    } else {
+        CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL);
+        if (ctFont) {
+            ((NSMutableDictionary *)_attributes)[(id)kCTFontAttributeName] = (__bridge id)(ctFont);
+            CFRelease(ctFont);
+        }
+    }
+}
+
+- (void)setColor:(UIColor *)color {
+    [self _makeMutableAttributes];
+    if (color == (id)[NSNull null] || color == nil) {
+        ((NSMutableDictionary *)_attributes)[(id)kCTForegroundColorAttributeName] = [NSNull null];
+        ((NSMutableDictionary *)_attributes)[NSForegroundColorAttributeName] = [NSNull null];
+    } else {
+        ((NSMutableDictionary *)_attributes)[(id)kCTForegroundColorAttributeName] = (__bridge id)(color.CGColor);
+        ((NSMutableDictionary *)_attributes)[NSForegroundColorAttributeName] = color;
+    }
+}
+
+- (void)setStrokeWidth:(NSNumber *)width {
+    [self _makeMutableAttributes];
+    if (width == (id)[NSNull null] || width == nil) {
+        ((NSMutableDictionary *)_attributes)[(id)kCTStrokeWidthAttributeName] = [NSNull null];
+    } else {
+        ((NSMutableDictionary *)_attributes)[(id)kCTStrokeWidthAttributeName] = width;
+    }
+}
+
+- (void)setStrokeColor:(UIColor *)color {
+    [self _makeMutableAttributes];
+    if (color == (id)[NSNull null] || color == nil) {
+        ((NSMutableDictionary *)_attributes)[(id)kCTStrokeColorAttributeName] = [NSNull null];
+        ((NSMutableDictionary *)_attributes)[NSStrokeColorAttributeName] = [NSNull null];
+    } else {
+        ((NSMutableDictionary *)_attributes)[(id)kCTStrokeColorAttributeName] = (__bridge id)(color.CGColor);
+        ((NSMutableDictionary *)_attributes)[NSStrokeColorAttributeName] = color;
+    }
+}
+
+- (void)setTextAttribute:(NSString *)attribute value:(id)value {
+    [self _makeMutableAttributes];
+    if (value == nil) value = [NSNull null];
+    ((NSMutableDictionary *)_attributes)[attribute] = value;
+}
+
+- (void)setShadow:(YYTextShadow *)shadow {
+    [self setTextAttribute:YYTextShadowAttributeName value:shadow];
+}
+
+- (void)setInnerShadow:(YYTextShadow *)shadow {
+    [self setTextAttribute:YYTextInnerShadowAttributeName value:shadow];
+}
+
+- (void)setUnderline:(YYTextDecoration *)underline {
+    [self setTextAttribute:YYTextUnderlineAttributeName value:underline];
+}
+
+- (void)setStrikethrough:(YYTextDecoration *)strikethrough {
+    [self setTextAttribute:YYTextStrikethroughAttributeName value:strikethrough];
+}
+
+- (void)setBackgroundBorder:(YYTextBorder *)border {
+    [self setTextAttribute:YYTextBackgroundBorderAttributeName value:border];
+}
+
+- (void)setBorder:(YYTextBorder *)border {
+    [self setTextAttribute:YYTextBorderAttributeName value:border];
+}
+
+- (void)setAttachment:(YYTextAttachment *)attachment {
+    [self setTextAttribute:YYTextAttachmentAttributeName value:attachment];
+}
+
+@end
+

+ 91 - 0
Pods/YYText/YYText/String/YYTextParser.h

@@ -0,0 +1,91 @@
+//
+//  YYTextParser.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/3/6.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ The YYTextParser protocol declares the required method for YYTextView and YYLabel
+ to modify the text during editing.
+ 
+ You can implement this protocol to add code highlighting or emoticon replacement for
+ YYTextView and YYLabel. See `YYTextSimpleMarkdownParser` and `YYTextSimpleEmoticonParser` for example.
+ */
+@protocol YYTextParser <NSObject>
+@required
+/**
+ When text is changed in YYTextView or YYLabel, this method will be called.
+ 
+ @param text  The original attributed string. This method may parse the text and
+ change the text attributes or content.
+ 
+ @param selectedRange  Current selected range in `text`.
+ This method should correct the range if the text content is changed. If there's 
+ no selected range (such as YYLabel), this value is NULL.
+ 
+ @return If the 'text' is modified in this method, returns `YES`, otherwise returns `NO`.
+ */
+- (BOOL)parseText:(nullable NSMutableAttributedString *)text selectedRange:(nullable NSRangePointer)selectedRange;
+@end
+
+
+
+/**
+ A simple markdown parser.
+ 
+ It'a very simple markdown parser, you can use this parser to highlight some 
+ small piece of markdown text.
+ 
+ This markdown parser use regular expression to parse text, slow and weak.
+ If you want to write a better parser, try these projests:
+ https://github.com/NimbusKit/markdown
+ https://github.com/dreamwieber/AttributedMarkdown
+ https://github.com/indragiek/CocoaMarkdown
+ 
+ Or you can use lex/yacc to generate your custom parser.
+ */
+@interface YYTextSimpleMarkdownParser : NSObject <YYTextParser>
+@property (nonatomic) CGFloat fontSize;         ///< default is 14
+@property (nonatomic) CGFloat headerFontSize;   ///< default is 20
+
+@property (nullable, nonatomic, strong) UIColor *textColor;
+@property (nullable, nonatomic, strong) UIColor *controlTextColor;
+@property (nullable, nonatomic, strong) UIColor *headerTextColor;
+@property (nullable, nonatomic, strong) UIColor *inlineTextColor;
+@property (nullable, nonatomic, strong) UIColor *codeTextColor;
+@property (nullable, nonatomic, strong) UIColor *linkTextColor;
+
+- (void)setColorWithBrightTheme; ///< reset the color properties to pre-defined value.
+- (void)setColorWithDarkTheme;   ///< reset the color properties to pre-defined value.
+@end
+
+
+
+/**
+ A simple emoticon parser.
+ 
+ Use this parser to map some specified piece of string to image emoticon.
+ Example: "Hello :smile:"  ->  "Hello 😀"
+ 
+ It can also be used to extend the "unicode emoticon".
+ */
+@interface YYTextSimpleEmoticonParser : NSObject <YYTextParser>
+
+/**
+ The custom emoticon mapper.
+ The key is a specified plain string, such as @":smile:".
+ The value is a UIImage which will replace the specified plain string in text.
+ */
+@property (nullable, copy) NSDictionary<NSString *, __kindof UIImage *> *emoticonMapper;
+@end
+
+NS_ASSUME_NONNULL_END

+ 417 - 0
Pods/YYText/YYText/String/YYTextParser.m

@@ -0,0 +1,417 @@
+//
+//  YYTextParser.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/3/6.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextParser.h"
+#import "YYTextUtilities.h"
+#import "YYTextAttribute.h"
+#import "NSAttributedString+YYText.h"
+#import "NSParagraphStyle+YYText.h"
+
+
+#pragma mark - Markdown Parser
+
+@implementation YYTextSimpleMarkdownParser {
+    UIFont *_font;
+    NSMutableArray *_headerFonts; ///< h1~h6
+    UIFont *_boldFont;
+    UIFont *_italicFont;
+    UIFont *_boldItalicFont;
+    UIFont *_monospaceFont;
+    YYTextBorder *_border;
+    
+    NSRegularExpression *_regexEscape;          ///< escape
+    NSRegularExpression *_regexHeader;          ///< #header
+    NSRegularExpression *_regexH1;              ///< header\n====
+    NSRegularExpression *_regexH2;              ///< header\n----
+    NSRegularExpression *_regexBreakline;       ///< ******
+    NSRegularExpression *_regexEmphasis;        ///< *text*  _text_
+    NSRegularExpression *_regexStrong;          ///< **text**
+    NSRegularExpression *_regexStrongEmphasis;  ///< ***text*** ___text___
+    NSRegularExpression *_regexUnderline;       ///< __text__
+    NSRegularExpression *_regexStrikethrough;   ///< ~~text~~
+    NSRegularExpression *_regexInlineCode;      ///< `text`
+    NSRegularExpression *_regexLink;            ///< [name](link)
+    NSRegularExpression *_regexLinkRefer;       ///< [ref]:
+    NSRegularExpression *_regexList;            ///< 1.text 2.text 3.text
+    NSRegularExpression *_regexBlockQuote;      ///< > quote
+    NSRegularExpression *_regexCodeBlock;       ///< \tcode \tcode
+    NSRegularExpression *_regexNotEmptyLine;
+}
+
+- (void)initRegex {
+#define regexp(reg, option) [NSRegularExpression regularExpressionWithPattern : @reg options : option error : NULL]
+    _regexEscape = regexp("(\\\\\\\\|\\\\\\`|\\\\\\*|\\\\\\_|\\\\\\(|\\\\\\)|\\\\\\[|\\\\\\]|\\\\#|\\\\\\+|\\\\\\-|\\\\\\!)", 0);
+    _regexHeader = regexp("^((\\#{1,6}[^#].*)|(\\#{6}.+))$", NSRegularExpressionAnchorsMatchLines);
+    _regexH1 = regexp("^[^=\\n][^\\n]*\\n=+$", NSRegularExpressionAnchorsMatchLines);
+    _regexH2 = regexp("^[^-\\n][^\\n]*\\n-+$", NSRegularExpressionAnchorsMatchLines);
+    _regexBreakline = regexp("^[ \\t]*([*-])[ \\t]*((\\1)[ \\t]*){2,}[ \\t]*$", NSRegularExpressionAnchorsMatchLines);
+    _regexEmphasis = regexp("((?<!\\*)\\*(?=[^ \\t*])(.+?)(?<=[^ \\t*])\\*(?!\\*)|(?<!_)_(?=[^ \\t_])(.+?)(?<=[^ \\t_])_(?!_))", 0);
+    _regexStrong = regexp("(?<!\\*)\\*{2}(?=[^ \\t*])(.+?)(?<=[^ \\t*])\\*{2}(?!\\*)", 0);
+    _regexStrongEmphasis =  regexp("((?<!\\*)\\*{3}(?=[^ \\t*])(.+?)(?<=[^ \\t*])\\*{3}(?!\\*)|(?<!_)_{3}(?=[^ \\t_])(.+?)(?<=[^ \\t_])_{3}(?!_))", 0);
+    _regexUnderline = regexp("(?<!_)__(?=[^ \\t_])(.+?)(?<=[^ \\t_])\\__(?!_)", 0);
+    _regexStrikethrough = regexp("(?<!~)~~(?=[^ \\t~])(.+?)(?<=[^ \\t~])\\~~(?!~)", 0);
+    _regexInlineCode = regexp("(?<!`)(`{1,3})([^`\n]+?)\\1(?!`)", 0);
+    _regexLink = regexp("!?\\[([^\\[\\]]+)\\](\\(([^\\(\\)]+)\\)|\\[([^\\[\\]]+)\\])", 0);
+    _regexLinkRefer = regexp("^[ \\t]*\\[[^\\[\\]]\\]:", NSRegularExpressionAnchorsMatchLines);
+    _regexList = regexp("^[ \\t]*([*+-]|\\d+[.])[ \\t]+", NSRegularExpressionAnchorsMatchLines);
+    _regexBlockQuote = regexp("^[ \\t]*>[ \\t>]*", NSRegularExpressionAnchorsMatchLines);
+    _regexCodeBlock = regexp("(^\\s*$\\n)((( {4}|\\t).*(\\n|\\z))|(^\\s*$\\n))+", NSRegularExpressionAnchorsMatchLines);
+    _regexNotEmptyLine = regexp("^[ \\t]*[^ \\t]+[ \\t]*$", NSRegularExpressionAnchorsMatchLines);
+#undef regexp
+}
+
+- (instancetype)init {
+    self = [super init];
+    _fontSize = 14;
+    _headerFontSize = 20;
+    [self _updateFonts];
+    [self setColorWithBrightTheme];
+    [self initRegex];
+    return self;
+}
+
+- (void)setFontSize:(CGFloat)fontSize {
+    if (fontSize < 1) fontSize = 12;
+    _fontSize = fontSize;
+    [self _updateFonts];
+}
+
+- (void)setHeaderFontSize:(CGFloat)headerFontSize {
+    if (headerFontSize < 1) headerFontSize = 20;
+    _headerFontSize = headerFontSize;
+    [self _updateFonts];
+}
+
+- (void)_updateFonts {
+    _font = [UIFont systemFontOfSize:_fontSize];
+    _headerFonts = [NSMutableArray new];
+    for (int i = 0; i < 6; i++) {
+        CGFloat size = _headerFontSize - (_headerFontSize - _fontSize) / 5.0 * i;
+        [_headerFonts addObject:[UIFont systemFontOfSize:size]];
+    }
+    _boldFont = YYTextFontWithBold(_font);
+    _italicFont = YYTextFontWithItalic(_font);
+    _boldItalicFont = YYTextFontWithBoldItalic(_font);
+    _monospaceFont = [UIFont fontWithName:@"Menlo" size:_fontSize]; // Since iOS 7
+    if (!_monospaceFont) _monospaceFont = [UIFont fontWithName:@"Courier" size:_fontSize]; // Since iOS 3
+}
+
+- (void)setColorWithBrightTheme {
+    _textColor = [UIColor blackColor];
+    _controlTextColor = [UIColor colorWithWhite:0.749 alpha:1.000];
+    _headerTextColor = [UIColor colorWithRed:1.000 green:0.502 blue:0.000 alpha:1.000];
+    _inlineTextColor = [UIColor colorWithWhite:0.150 alpha:1.000];
+    _codeTextColor = [UIColor colorWithWhite:0.150 alpha:1.000];
+    _linkTextColor = [UIColor colorWithRed:0.000 green:0.478 blue:0.962 alpha:1.000];
+    
+    _border = [YYTextBorder new];
+    _border.lineStyle = YYTextLineStyleSingle;
+    _border.fillColor = [UIColor colorWithWhite:0.184 alpha:0.090];
+    _border.strokeColor = [UIColor colorWithWhite:0.546 alpha:0.650];
+    _border.insets = UIEdgeInsetsMake(-1, 0, -1, 0);
+    _border.cornerRadius = 2;
+    _border.strokeWidth = YYTextCGFloatFromPixel(1);
+}
+
+- (void)setColorWithDarkTheme {
+    _textColor = [UIColor whiteColor];
+    _controlTextColor = [UIColor colorWithWhite:0.604 alpha:1.000];
+    _headerTextColor = [UIColor colorWithRed:0.558 green:1.000 blue:0.502 alpha:1.000];
+    _inlineTextColor = [UIColor colorWithRed:1.000 green:0.862 blue:0.387 alpha:1.000];
+    _codeTextColor = [UIColor colorWithWhite:0.906 alpha:1.000];
+    _linkTextColor = [UIColor colorWithRed:0.000 green:0.646 blue:1.000 alpha:1.000];
+    
+    _border = [YYTextBorder new];
+    _border.lineStyle = YYTextLineStyleSingle;
+    _border.fillColor = [UIColor colorWithWhite:0.820 alpha:0.130];
+    _border.strokeColor = [UIColor colorWithWhite:1.000 alpha:0.280];
+    _border.insets = UIEdgeInsetsMake(-1, 0, -1, 0);
+    _border.cornerRadius = 2;
+    _border.strokeWidth = YYTextCGFloatFromPixel(1);
+}
+
+- (NSUInteger)lenghOfBeginWhiteInString:(NSString *)str withRange:(NSRange)range{
+    for (NSUInteger i = 0; i < range.length; i++) {
+        unichar c = [str characterAtIndex:i + range.location];
+        if (c != ' ' && c != '\t' && c != '\n') return i;
+    }
+    return str.length;
+}
+
+- (NSUInteger)lenghOfEndWhiteInString:(NSString *)str withRange:(NSRange)range{
+    for (NSInteger i = range.length - 1; i >= 0; i--) {
+        unichar c = [str characterAtIndex:i + range.location];
+        if (c != ' ' && c != '\t' && c != '\n') return range.length - i;
+    }
+    return str.length;
+}
+
+- (NSUInteger)lenghOfBeginChar:(unichar)c inString:(NSString *)str withRange:(NSRange)range{
+    for (NSUInteger i = 0; i < range.length; i++) {
+        if ([str characterAtIndex:i + range.location] != c) return i;
+    }
+    return str.length;
+}
+
+- (BOOL)parseText:(NSMutableAttributedString *)text selectedRange:(NSRangePointer)range {
+    if (text.length == 0) return NO;
+    [text yy_removeAttributesInRange:NSMakeRange(0, text.length)];
+    text.yy_font = _font;
+    text.yy_color = _textColor;
+    
+    NSMutableString *str = text.string.mutableCopy;
+    [_regexEscape replaceMatchesInString:str options:kNilOptions range:NSMakeRange(0, str.length) withTemplate:@"@@"];
+    
+    [_regexHeader enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        NSUInteger whiteLen = [self lenghOfBeginWhiteInString:str withRange:r];
+        NSUInteger sharpLen = [self lenghOfBeginChar:'#' inString:str withRange:NSMakeRange(r.location + whiteLen, r.length - whiteLen)];
+        if (sharpLen > 6) sharpLen = 6;
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location, whiteLen + sharpLen)];
+        [text yy_setColor:_headerTextColor range:NSMakeRange(r.location + whiteLen + sharpLen, r.length - whiteLen - sharpLen)];
+        [text yy_setFont:_headerFonts[sharpLen - 1] range:result.range];
+    }];
+    
+    [_regexH1 enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        NSRange linebreak = [str rangeOfString:@"\n" options:0 range:result.range locale:nil];
+        if (linebreak.location != NSNotFound) {
+            [text yy_setColor:_headerTextColor range:NSMakeRange(r.location, linebreak.location - r.location)];
+            [text yy_setFont:_headerFonts[0] range:NSMakeRange(r.location, linebreak.location - r.location + 1)];
+            [text yy_setColor:_controlTextColor range:NSMakeRange(linebreak.location + linebreak.length, r.location + r.length - linebreak.location - linebreak.length)];
+        }
+    }];
+    
+    [_regexH2 enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        NSRange linebreak = [str rangeOfString:@"\n" options:0 range:result.range locale:nil];
+        if (linebreak.location != NSNotFound) {
+            [text yy_setColor:_headerTextColor range:NSMakeRange(r.location, linebreak.location - r.location)];
+            [text yy_setFont:_headerFonts[1] range:NSMakeRange(r.location, linebreak.location - r.location + 1)];
+            [text yy_setColor:_controlTextColor range:NSMakeRange(linebreak.location + linebreak.length, r.location + r.length - linebreak.location - linebreak.length)];
+        }
+    }];
+    
+    [_regexBreakline enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        [text yy_setColor:_controlTextColor range:result.range];
+    }];
+    
+    [_regexEmphasis enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location, 1)];
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location + r.length - 1, 1)];
+        [text yy_setFont:_italicFont range:NSMakeRange(r.location + 1, r.length - 2)];
+    }];
+    
+    [_regexStrong enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location, 2)];
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location + r.length - 2, 2)];
+        [text yy_setFont:_boldFont range:NSMakeRange(r.location + 2, r.length - 4)];
+    }];
+    
+    [_regexStrongEmphasis enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location, 3)];
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location + r.length - 3, 3)];
+        [text yy_setFont:_boldItalicFont range:NSMakeRange(r.location + 3, r.length - 6)];
+    }];
+    
+    [_regexUnderline enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location, 2)];
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location + r.length - 2, 2)];
+        [text yy_setTextUnderline:[YYTextDecoration decorationWithStyle:YYTextLineStyleSingle width:@1 color:nil] range:NSMakeRange(r.location + 2, r.length - 4)];
+    }];
+    
+    [_regexStrikethrough enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location, 2)];
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location + r.length - 2, 2)];
+        [text yy_setTextStrikethrough:[YYTextDecoration decorationWithStyle:YYTextLineStyleSingle width:@1 color:nil] range:NSMakeRange(r.location + 2, r.length - 4)];
+    }];
+    
+    [_regexInlineCode enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        NSUInteger len = [self lenghOfBeginChar:'`' inString:str withRange:r];
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location, len)];
+        [text yy_setColor:_controlTextColor range:NSMakeRange(r.location + r.length - len, len)];
+        [text yy_setColor:_inlineTextColor range:NSMakeRange(r.location + len, r.length - 2 * len)];
+        [text yy_setFont:_monospaceFont range:r];
+        [text yy_setTextBorder:_border.copy range:r];
+    }];
+    
+    [_regexLink enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_linkTextColor range:r];
+    }];
+    
+    [_regexLinkRefer enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_controlTextColor range:r];
+    }];
+    
+    [_regexList enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_controlTextColor range:r];
+    }];
+    
+    [_regexBlockQuote enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        [text yy_setColor:_controlTextColor range:r];
+    }];
+    
+    [_regexCodeBlock enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
+        NSRange r = result.range;
+        NSRange firstLineRange = [_regexNotEmptyLine rangeOfFirstMatchInString:str options:kNilOptions range:r];
+        NSUInteger lenStart = (firstLineRange.location != NSNotFound) ? firstLineRange.location - r.location : 0;
+        NSUInteger lenEnd = [self lenghOfEndWhiteInString:str withRange:r];
+        if (lenStart + lenEnd < r.length) {
+            NSRange codeR = NSMakeRange(r.location + lenStart, r.length - lenStart - lenEnd);
+            [text yy_setColor:_codeTextColor range:codeR];
+            [text yy_setFont:_monospaceFont range:codeR];
+            YYTextBorder *border = [YYTextBorder new];
+            border.lineStyle = YYTextLineStyleSingle;
+            border.fillColor = [UIColor colorWithWhite:0.184 alpha:0.090];
+            border.strokeColor = [UIColor colorWithWhite:0.200 alpha:0.300];
+            border.insets = UIEdgeInsetsMake(-1, 0, -1, 0);
+            border.cornerRadius = 3;
+            border.strokeWidth = YYTextCGFloatFromPixel(2);
+            [text yy_setTextBlockBorder:_border.copy range:codeR];
+        }
+    }];
+    
+    return YES;
+}
+
+
+@end
+
+
+
+#pragma mark - Emoticon Parser
+
+#define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \
+__VA_ARGS__; \
+dispatch_semaphore_signal(_lock);
+
+@implementation YYTextSimpleEmoticonParser {
+    NSRegularExpression *_regex;
+    NSDictionary *_mapper;
+    dispatch_semaphore_t _lock;
+}
+
+- (instancetype)init {
+    self = [super init];
+    _lock = dispatch_semaphore_create(1);
+    return self;
+}
+
+- (NSDictionary *)emoticonMapper {
+    LOCK(NSDictionary *mapper = _mapper); return mapper;
+}
+
+- (void)setEmoticonMapper:(NSDictionary *)emoticonMapper {
+    LOCK(
+         _mapper = emoticonMapper.copy;
+         if (_mapper.count == 0) {
+             _regex = nil;
+         } else {
+             NSMutableString *pattern = @"(".mutableCopy;
+             NSArray *allKeys = _mapper.allKeys;
+             NSCharacterSet *charset = [NSCharacterSet characterSetWithCharactersInString:@"$^?+*.,#|{}[]()\\"];
+             for (NSUInteger i = 0, max = allKeys.count; i < max; i++) {
+                 NSMutableString *one = [allKeys[i] mutableCopy];
+                 
+                 // escape regex characters
+                 for (NSUInteger ci = 0, cmax = one.length; ci < cmax; ci++) {
+                     unichar c = [one characterAtIndex:ci];
+                     if ([charset characterIsMember:c]) {
+                         [one insertString:@"\\" atIndex:ci];
+                         ci++;
+                         cmax++;
+                     }
+                 }
+                 
+                 [pattern appendString:one];
+                 if (i != max - 1) [pattern appendString:@"|"];
+             }
+             [pattern appendString:@")"];
+             _regex = [[NSRegularExpression alloc] initWithPattern:pattern options:kNilOptions error:nil];
+         }
+    );
+}
+
+// correct the selected range during text replacement
+- (NSRange)_replaceTextInRange:(NSRange)range withLength:(NSUInteger)length selectedRange:(NSRange)selectedRange {
+    // no change
+    if (range.length == length) return selectedRange;
+    // right
+    if (range.location >= selectedRange.location + selectedRange.length) return selectedRange;
+    // left
+    if (selectedRange.location >= range.location + range.length) {
+        selectedRange.location = selectedRange.location + length - range.length;
+        return selectedRange;
+    }
+    // same
+    if (NSEqualRanges(range, selectedRange)) {
+        selectedRange.length = length;
+        return selectedRange;
+    }
+    // one edge same
+    if ((range.location == selectedRange.location && range.length < selectedRange.length) ||
+        (range.location + range.length == selectedRange.location + selectedRange.length && range.length < selectedRange.length)) {
+        selectedRange.length = selectedRange.length + length - range.length;
+        return selectedRange;
+    }
+    selectedRange.location = range.location + length;
+    selectedRange.length = 0;
+    return selectedRange;
+}
+
+- (BOOL)parseText:(NSMutableAttributedString *)text selectedRange:(NSRangePointer)range {
+    if (text.length == 0) return NO;
+    
+    NSDictionary *mapper;
+    NSRegularExpression *regex;
+    LOCK(mapper = _mapper; regex = _regex;);
+    if (mapper.count == 0 || regex == nil) return NO;
+    
+    NSArray *matches = [regex matchesInString:text.string options:kNilOptions range:NSMakeRange(0, text.length)];
+    if (matches.count == 0) return NO;
+    
+    NSRange selectedRange = range ? *range : NSMakeRange(0, 0);
+    NSUInteger cutLength = 0;
+    for (NSUInteger i = 0, max = matches.count; i < max; i++) {
+        NSTextCheckingResult *one = matches[i];
+        NSRange oneRange = one.range;
+        if (oneRange.length == 0) continue;
+        oneRange.location -= cutLength;
+        NSString *subStr = [text.string substringWithRange:oneRange];
+        UIImage *emoticon = mapper[subStr];
+        if (!emoticon) continue;
+        
+        CGFloat fontSize = 12; // CoreText default value
+        CTFontRef font = (__bridge CTFontRef)([text yy_attribute:NSFontAttributeName atIndex:oneRange.location]);
+        if (font) fontSize = CTFontGetSize(font);
+        NSMutableAttributedString *atr = [NSAttributedString yy_attachmentStringWithEmojiImage:emoticon fontSize:fontSize];
+        [atr yy_setTextBackedString:[YYTextBackedString stringWithString:subStr] range:NSMakeRange(0, atr.length)];
+        [text replaceCharactersInRange:oneRange withString:atr.string];
+        [text yy_removeDiscontinuousAttributesInRange:NSMakeRange(oneRange.location, atr.length)];
+        [text addAttributes:atr.yy_attributes range:NSMakeRange(oneRange.location, atr.length)];
+        selectedRange = [self _replaceTextInRange:oneRange withLength:atr.length selectedRange:selectedRange];
+        cutLength += oneRange.length - 1;
+    }
+    if (range) *range = selectedRange;
+    
+    return YES;
+}
+@end

+ 78 - 0
Pods/YYText/YYText/String/YYTextRubyAnnotation.h

@@ -0,0 +1,78 @@
+//
+//  YYTextRubyAnnotation.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/24.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+#import <CoreText/CoreText.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Wrapper for CTRubyAnnotationRef.
+ 
+ Example:
+ 
+     YYTextRubyAnnotation *ruby = [YYTextRubyAnnotation new];
+     ruby.textBefore = @"zhù yīn";
+     CTRubyAnnotationRef ctRuby = ruby.CTRubyAnnotation;
+     if (ctRuby) {
+        /// add to attributed string
+        CFRelease(ctRuby);
+     }
+ 
+ */
+@interface YYTextRubyAnnotation : NSObject <NSCopying, NSCoding>
+
+/// Specifies how the ruby text and the base text should be aligned relative to each other.
+@property (nonatomic) CTRubyAlignment alignment;
+
+/// Specifies how the ruby text can overhang adjacent characters.
+@property (nonatomic) CTRubyOverhang overhang;
+
+/// Specifies the size of the annotation text as a percent of the size of the base text.
+@property (nonatomic) CGFloat sizeFactor;
+
+
+/// The ruby text is positioned before the base text;
+/// i.e. above horizontal text and to the right of vertical text.
+@property (nullable, nonatomic, copy) NSString *textBefore;
+
+/// The ruby text is positioned after the base text;
+/// i.e. below horizontal text and to the left of vertical text.
+@property (nullable, nonatomic, copy) NSString *textAfter;
+
+/// The ruby text is positioned to the right of the base text whether it is horizontal or vertical.
+/// This is the way that Bopomofo annotations are attached to Chinese text in Taiwan.
+@property (nullable, nonatomic, copy) NSString *textInterCharacter;
+
+/// The ruby text follows the base text with no special styling.
+@property (nullable, nonatomic, copy) NSString *textInline;
+
+
+/**
+ Create a ruby object from CTRuby object.
+ 
+ @param ctRuby  A CTRuby object.
+ 
+ @return A ruby object, or nil when an error occurs.
+ */
++ (instancetype)rubyWithCTRubyRef:(CTRubyAnnotationRef)ctRuby NS_AVAILABLE_IOS(8_0);
+
+/**
+ Create a CTRuby object from the instance.
+ 
+ @return A new CTRuby object, or NULL when an error occurs.
+ The returned value should be release after used.
+ */
+- (nullable CTRubyAnnotationRef)CTRubyAnnotation CF_RETURNS_RETAINED NS_AVAILABLE_IOS(8_0);
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 83 - 0
Pods/YYText/YYText/String/YYTextRubyAnnotation.m

@@ -0,0 +1,83 @@
+//
+//  YYTextRubyAnnotation.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/24.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextRubyAnnotation.h"
+
+@implementation YYTextRubyAnnotation
+
+- (instancetype)init {
+    self = super.init;
+    self.alignment = kCTRubyAlignmentAuto;
+    self.overhang = kCTRubyOverhangAuto;
+    self.sizeFactor = 0.5;
+    return self;
+}
+
++ (instancetype)rubyWithCTRubyRef:(CTRubyAnnotationRef)ctRuby {
+    if (!ctRuby) return nil;
+    YYTextRubyAnnotation *one = [self new];
+    one.alignment = CTRubyAnnotationGetAlignment(ctRuby);
+    one.overhang = CTRubyAnnotationGetOverhang(ctRuby);
+    one.sizeFactor = CTRubyAnnotationGetSizeFactor(ctRuby);
+    one.textBefore = (__bridge NSString *)(CTRubyAnnotationGetTextForPosition(ctRuby, kCTRubyPositionBefore));
+    one.textAfter = (__bridge NSString *)(CTRubyAnnotationGetTextForPosition(ctRuby, kCTRubyPositionAfter));
+    one.textInterCharacter = (__bridge NSString *)(CTRubyAnnotationGetTextForPosition(ctRuby, kCTRubyPositionInterCharacter));
+    one.textInline = (__bridge NSString *)(CTRubyAnnotationGetTextForPosition(ctRuby, kCTRubyPositionInline));
+    return one;
+}
+
+- (CTRubyAnnotationRef)CTRubyAnnotation CF_RETURNS_RETAINED {
+    if (((long)CTRubyAnnotationCreate + 1) == 1) return NULL; // system not support
+    
+    CFStringRef text[kCTRubyPositionCount];
+    text[kCTRubyPositionBefore] = (__bridge CFStringRef)(_textBefore);
+    text[kCTRubyPositionAfter] = (__bridge CFStringRef)(_textAfter);
+    text[kCTRubyPositionInterCharacter] = (__bridge CFStringRef)(_textInterCharacter);
+    text[kCTRubyPositionInline] = (__bridge CFStringRef)(_textInline);
+    CTRubyAnnotationRef ruby = CTRubyAnnotationCreate(_alignment, _overhang, _sizeFactor, text);
+    return ruby;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    YYTextRubyAnnotation *one = [self.class new];
+    one.alignment = _alignment;
+    one.overhang = _overhang;
+    one.sizeFactor = _sizeFactor;
+    one.textBefore = _textBefore;
+    one.textAfter = _textAfter;
+    one.textInterCharacter = _textInterCharacter;
+    one.textInline = _textInline;
+    return one;
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [aCoder encodeObject:@(_alignment) forKey:@"alignment"];
+    [aCoder encodeObject:@(_overhang) forKey:@"overhang"];
+    [aCoder encodeObject:@(_sizeFactor) forKey:@"sizeFactor"];
+    [aCoder encodeObject:_textBefore forKey:@"textBefore"];
+    [aCoder encodeObject:_textAfter forKey:@"textAfter"];
+    [aCoder encodeObject:_textInterCharacter forKey:@"textInterCharacter"];
+    [aCoder encodeObject:_textInline forKey:@"textInline"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [self init];
+    _alignment = ((NSNumber *)[aDecoder decodeObjectForKey:@"alignment"]).intValue;
+    _overhang = ((NSNumber *)[aDecoder decodeObjectForKey:@"overhang"]).intValue;
+    _sizeFactor = ((NSNumber *)[aDecoder decodeObjectForKey:@"sizeFactor"]).intValue;
+    _textBefore = [aDecoder decodeObjectForKey:@"textBefore"];
+    _textAfter = [aDecoder decodeObjectForKey:@"textAfter"];
+    _textInterCharacter = [aDecoder decodeObjectForKey:@"textInterCharacter"];
+    _textInline = [aDecoder decodeObjectForKey:@"textInline"];
+    return self;
+}
+
+@end

+ 68 - 0
Pods/YYText/YYText/String/YYTextRunDelegate.h

@@ -0,0 +1,68 @@
+//
+//  YYTextRunDelegate.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 14/10/14.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+#import <CoreText/CoreText.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Wrapper for CTRunDelegateRef.
+ 
+ Example:
+ 
+     YYTextRunDelegate *delegate = [YYTextRunDelegate new];
+     delegate.ascent = 20;
+     delegate.descent = 4;
+     delegate.width = 20;
+     CTRunDelegateRef ctRunDelegate = delegate.CTRunDelegate;
+     if (ctRunDelegate) {
+         /// add to attributed string
+         CFRelease(ctRunDelegate);
+     }
+ 
+ */
+@interface YYTextRunDelegate : NSObject <NSCopying, NSCoding>
+
+/**
+ Creates and returns the CTRunDelegate.
+ 
+ @discussion You need call CFRelease() after used.
+ The CTRunDelegateRef has a strong reference to this YYTextRunDelegate object.
+ In CoreText, use CTRunDelegateGetRefCon() to get this YYTextRunDelegate object.
+ 
+ @return The CTRunDelegate object.
+ */
+- (nullable CTRunDelegateRef)CTRunDelegate CF_RETURNS_RETAINED;
+
+/**
+ Additional information about the the run delegate.
+ */
+@property (nullable, nonatomic, strong) NSDictionary *userInfo;
+
+/**
+ The typographic ascent of glyphs in the run.
+ */
+@property (nonatomic) CGFloat ascent;
+
+/**
+ The typographic descent of glyphs in the run.
+ */
+@property (nonatomic) CGFloat descent;
+
+/**
+ The typographic width of glyphs in the run.
+ */
+@property (nonatomic) CGFloat width;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 71 - 0
Pods/YYText/YYText/String/YYTextRunDelegate.m

@@ -0,0 +1,71 @@
+//
+//  YYTextRunDelegate.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 14/10/14.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "YYTextRunDelegate.h"
+
+static void DeallocCallback(void *ref) {
+    YYTextRunDelegate *self = (__bridge_transfer YYTextRunDelegate *)(ref);
+    self = nil; // release
+}
+
+static CGFloat GetAscentCallback(void *ref) {
+    YYTextRunDelegate *self = (__bridge YYTextRunDelegate *)(ref);
+    return self.ascent;
+}
+
+static CGFloat GetDecentCallback(void *ref) {
+    YYTextRunDelegate *self = (__bridge YYTextRunDelegate *)(ref);
+    return self.descent;
+}
+
+static CGFloat GetWidthCallback(void *ref) {
+    YYTextRunDelegate *self = (__bridge YYTextRunDelegate *)(ref);
+    return self.width;
+}
+
+@implementation YYTextRunDelegate
+
+- (CTRunDelegateRef)CTRunDelegate CF_RETURNS_RETAINED {
+    CTRunDelegateCallbacks callbacks;
+    callbacks.version = kCTRunDelegateCurrentVersion;
+    callbacks.dealloc = DeallocCallback;
+    callbacks.getAscent = GetAscentCallback;
+    callbacks.getDescent = GetDecentCallback;
+    callbacks.getWidth = GetWidthCallback;
+    return CTRunDelegateCreate(&callbacks, (__bridge_retained void *)(self.copy));
+}
+
+- (void)encodeWithCoder:(NSCoder *)aCoder {
+    [aCoder encodeObject:@(_ascent) forKey:@"ascent"];
+    [aCoder encodeObject:@(_descent) forKey:@"descent"];
+    [aCoder encodeObject:@(_width) forKey:@"width"];
+    [aCoder encodeObject:_userInfo forKey:@"userInfo"];
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder {
+    self = [super init];
+    _ascent = ((NSNumber *)[aDecoder decodeObjectForKey:@"ascent"]).floatValue;
+    _descent = ((NSNumber *)[aDecoder decodeObjectForKey:@"descent"]).floatValue;
+    _width = ((NSNumber *)[aDecoder decodeObjectForKey:@"width"]).floatValue;
+    _userInfo = [aDecoder decodeObjectForKey:@"userInfo"];
+    return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+    typeof(self) one = [self.class new];
+    one.ascent = self.ascent;
+    one.descent = self.descent;
+    one.width = self.width;
+    one.userInfo = self.userInfo;
+    return one;
+}
+
+@end

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1415 - 0
Pods/YYText/YYText/Utility/NSAttributedString+YYText.h


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1404 - 0
Pods/YYText/YYText/Utility/NSAttributedString+YYText.m


+ 37 - 0
Pods/YYText/YYText/Utility/NSParagraphStyle+YYText.h

@@ -0,0 +1,37 @@
+//
+//  NSParagraphStyle+YYText.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 14/10/7.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Provides extensions for `NSParagraphStyle` to work with CoreText.
+ */
+@interface NSParagraphStyle (YYText)
+
+/**
+ Creates a new NSParagraphStyle object from the CoreText Style.
+ 
+ @param CTStyle CoreText Paragraph Style.
+ 
+ @return a new NSParagraphStyle
+ */
++ (nullable NSParagraphStyle *)yy_styleWithCTStyle:(CTParagraphStyleRef)CTStyle;
+
+/**
+ Creates and returns a CoreText Paragraph Style. (need call CFRelease() after used)
+ */
+- (nullable CTParagraphStyleRef)yy_CTStyle CF_RETURNS_RETAINED;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 230 - 0
Pods/YYText/YYText/Utility/NSParagraphStyle+YYText.m

@@ -0,0 +1,230 @@
+//
+//  NSParagraphStyle+YYText.m
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 14/10/7.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import "NSParagraphStyle+YYText.h"
+#import "YYTextAttribute.h"
+#import <CoreText/CoreText.h>
+
+// Dummy class for category
+@interface NSParagraphStyle_YYText : NSObject @end
+@implementation NSParagraphStyle_YYText @end
+
+
+@implementation NSParagraphStyle (YYText)
+
++ (NSParagraphStyle *)yy_styleWithCTStyle:(CTParagraphStyleRef)CTStyle {
+    if (CTStyle == NULL) return nil;
+    
+    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
+    
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    CGFloat lineSpacing;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineSpacing, sizeof(CGFloat), &lineSpacing)) {
+        style.lineSpacing = lineSpacing;
+    }
+#pragma clang diagnostic pop
+    
+    CGFloat paragraphSpacing;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacing, sizeof(CGFloat), &paragraphSpacing)) {
+        style.paragraphSpacing = paragraphSpacing;
+    }
+    
+    CTTextAlignment alignment;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment)) {
+        style.alignment = NSTextAlignmentFromCTTextAlignment(alignment);
+    }
+    
+    CGFloat firstLineHeadIndent;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &firstLineHeadIndent)) {
+        style.firstLineHeadIndent = firstLineHeadIndent;
+    }
+    
+    CGFloat headIndent;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierHeadIndent, sizeof(CGFloat), &headIndent)) {
+        style.headIndent = headIndent;
+    }
+    
+    CGFloat tailIndent;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTailIndent, sizeof(CGFloat), &tailIndent)) {
+        style.tailIndent = tailIndent;
+    }
+    
+    CTLineBreakMode lineBreakMode;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineBreakMode, sizeof(CTLineBreakMode), &lineBreakMode)) {
+        style.lineBreakMode = (NSLineBreakMode)lineBreakMode;
+    }
+    
+    CGFloat minimumLineHeight;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMinimumLineHeight, sizeof(CGFloat), &minimumLineHeight)) {
+        style.minimumLineHeight = minimumLineHeight;
+    }
+    
+    CGFloat maximumLineHeight;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierMaximumLineHeight, sizeof(CGFloat), &maximumLineHeight)) {
+        style.maximumLineHeight = maximumLineHeight;
+    }
+    
+    CTWritingDirection baseWritingDirection;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierBaseWritingDirection, sizeof(CTWritingDirection), &baseWritingDirection)) {
+        style.baseWritingDirection = (NSWritingDirection)baseWritingDirection;
+    }
+    
+    CGFloat lineHeightMultiple;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierLineHeightMultiple, sizeof(CGFloat), &lineHeightMultiple)) {
+        style.lineHeightMultiple = lineHeightMultiple;
+    }
+    
+    CGFloat paragraphSpacingBefore;
+    if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierParagraphSpacingBefore, sizeof(CGFloat), &paragraphSpacingBefore)) {
+        style.paragraphSpacingBefore = paragraphSpacingBefore;
+    }
+    
+    if ([style respondsToSelector:@selector(tabStops)]) {
+        CFArrayRef tabStops;
+        if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops)) {
+            if ([style respondsToSelector:@selector(setTabStops:)]) {
+                NSMutableArray *tabs = [NSMutableArray new];
+                [((__bridge NSArray *)(tabStops))enumerateObjectsUsingBlock : ^(id obj, NSUInteger idx, BOOL *stop) {
+                    CTTextTabRef ctTab = (__bridge CFTypeRef)obj;
+                    
+                    NSTextTab *tab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentFromCTTextAlignment(CTTextTabGetAlignment(ctTab)) location:CTTextTabGetLocation(ctTab) options:(__bridge id)CTTextTabGetOptions(ctTab)];
+                    [tabs addObject:tab];
+                }];
+                if (tabs.count) {
+                    style.tabStops = tabs;
+                }
+            }
+        }
+        
+        CGFloat defaultTabInterval;
+        if (CTParagraphStyleGetValueForSpecifier(CTStyle, kCTParagraphStyleSpecifierDefaultTabInterval, sizeof(CGFloat), &defaultTabInterval)) {
+            if ([style respondsToSelector:@selector(setDefaultTabInterval:)]) {
+                style.defaultTabInterval = defaultTabInterval;
+            }
+        }
+    }
+    
+    return style;
+}
+
+- (CTParagraphStyleRef)yy_CTStyle CF_RETURNS_RETAINED {
+    CTParagraphStyleSetting set[kCTParagraphStyleSpecifierCount] = { 0 };
+    int count = 0;
+    
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    CGFloat lineSpacing = self.lineSpacing;
+    set[count].spec = kCTParagraphStyleSpecifierLineSpacing;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &lineSpacing;
+    count++;
+#pragma clang diagnostic pop
+    
+    CGFloat paragraphSpacing = self.paragraphSpacing;
+    set[count].spec = kCTParagraphStyleSpecifierParagraphSpacing;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &paragraphSpacing;
+    count++;
+    
+    CTTextAlignment alignment = NSTextAlignmentToCTTextAlignment(self.alignment);
+    set[count].spec = kCTParagraphStyleSpecifierAlignment;
+    set[count].valueSize = sizeof(CTTextAlignment);
+    set[count].value = &alignment;
+    count++;
+    
+    CGFloat firstLineHeadIndent = self.firstLineHeadIndent;
+    set[count].spec = kCTParagraphStyleSpecifierFirstLineHeadIndent;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &firstLineHeadIndent;
+    count++;
+    
+    CGFloat headIndent = self.headIndent;
+    set[count].spec = kCTParagraphStyleSpecifierHeadIndent;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &headIndent;
+    count++;
+    
+    CGFloat tailIndent = self.tailIndent;
+    set[count].spec = kCTParagraphStyleSpecifierTailIndent;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &tailIndent;
+    count++;
+    
+    CTLineBreakMode paraLineBreak = (CTLineBreakMode)self.lineBreakMode;
+    set[count].spec = kCTParagraphStyleSpecifierLineBreakMode;
+    set[count].valueSize = sizeof(CTLineBreakMode);
+    set[count].value = &paraLineBreak;
+    count++;
+    
+    CGFloat minimumLineHeight = self.minimumLineHeight;
+    set[count].spec = kCTParagraphStyleSpecifierMinimumLineHeight;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &minimumLineHeight;
+    count++;
+    
+    CGFloat maximumLineHeight = self.maximumLineHeight;
+    set[count].spec = kCTParagraphStyleSpecifierMaximumLineHeight;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &maximumLineHeight;
+    count++;
+    
+    CTWritingDirection paraWritingDirection = (CTWritingDirection)self.baseWritingDirection;
+    set[count].spec = kCTParagraphStyleSpecifierBaseWritingDirection;
+    set[count].valueSize = sizeof(CTWritingDirection);
+    set[count].value = &paraWritingDirection;
+    count++;
+    
+    CGFloat lineHeightMultiple = self.lineHeightMultiple;
+    set[count].spec = kCTParagraphStyleSpecifierLineHeightMultiple;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &lineHeightMultiple;
+    count++;
+    
+    CGFloat paragraphSpacingBefore = self.paragraphSpacingBefore;
+    set[count].spec = kCTParagraphStyleSpecifierParagraphSpacingBefore;
+    set[count].valueSize = sizeof(CGFloat);
+    set[count].value = &paragraphSpacingBefore;
+    count++;
+    
+    if([self respondsToSelector:@selector(tabStops)]) {
+        NSMutableArray *tabs = [NSMutableArray array];
+        if ([self respondsToSelector:@selector(tabStops)]) {
+            NSInteger numTabs = self.tabStops.count;
+            if (numTabs) {
+                [self.tabStops enumerateObjectsUsingBlock: ^(NSTextTab *tab, NSUInteger idx, BOOL *stop) {
+                    CTTextTabRef ctTab = CTTextTabCreate(NSTextAlignmentToCTTextAlignment(tab.alignment), tab.location, (__bridge CFTypeRef)tab.options);
+                    [tabs addObject:(__bridge id)ctTab];
+                    CFRelease(ctTab);
+                }];
+                
+                CFArrayRef tabStops = (__bridge CFArrayRef)(tabs);
+                set[count].spec = kCTParagraphStyleSpecifierTabStops;
+                set[count].valueSize = sizeof(CFArrayRef);
+                set[count].value = &tabStops;
+                count++;
+            }
+        }
+        
+        if ([self respondsToSelector:@selector(defaultTabInterval)]) {
+            CGFloat defaultTabInterval = self.defaultTabInterval;
+            set[count].spec = kCTParagraphStyleSpecifierDefaultTabInterval;
+            set[count].valueSize = sizeof(CGFloat);
+            set[count].value = &defaultTabInterval;
+            count++;
+        }
+    }
+    
+    CTParagraphStyleRef style = CTParagraphStyleCreate(set, count);
+    return style;
+}
+
+@end

+ 41 - 0
Pods/YYText/YYText/Utility/UIPasteboard+YYText.h

@@ -0,0 +1,41 @@
+//
+//  UIPasteboard+YYText.h
+//  YYText <https://github.com/ibireme/YYText>
+//
+//  Created by ibireme on 15/4/2.
+//  Copyright (c) 2015 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <UIKit/UIKit.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ Extend UIPasteboard to support image and attributed string.
+ */
+@interface UIPasteboard (YYText)
+
+@property (nullable, nonatomic, copy) NSData *yy_PNGData;    ///< PNG file data
+@property (nullable, nonatomic, copy) NSData *yy_JPEGData;   ///< JPEG file data
+@property (nullable, nonatomic, copy) NSData *yy_GIFData;    ///< GIF file data
+@property (nullable, nonatomic, copy) NSData *yy_WEBPData;   ///< WebP file data
+@property (nullable, nonatomic, copy) NSData *yy_ImageData;  ///< image file data
+
+/// Attributed string,
+/// Set this attributed will also set the string property which is copy from the attributed string.
+/// If the attributed string contains one or more image, it will also set the `images` property.
+@property (nullable, nonatomic, copy) NSAttributedString *yy_AttributedString;
+
+@end
+
+
+/// The name identifying the attributed string in pasteboard.
+UIKIT_EXTERN NSString *const YYTextPasteboardTypeAttributedString;
+
+/// The UTI Type identifying WebP data in pasteboard.
+UIKIT_EXTERN NSString *const YYTextUTTypeWEBP;
+
+NS_ASSUME_NONNULL_END

+ 0 - 0
Pods/YYText/YYText/Utility/UIPasteboard+YYText.m


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov