页面绘制

This commit is contained in:
pengguangjian 2024-09-21 18:49:01 +08:00
parent b3d4ea752c
commit 975311e9e6
159 changed files with 27501 additions and 2413 deletions

Binary file not shown.

View File

@ -24,6 +24,8 @@ target 'ProductApp' do
pod 'SSZipArchive'
pod 'YYText'
#pod 'AMapNavi'
#pod 'AMapLocation'
#pod 'AMapSearch'

View File

@ -32,6 +32,7 @@ PODS:
- SSZipArchive (2.4.3)
- "UITableView+FDTemplateLayoutCell (1.6)"
- YYModel (1.0.4)
- YYText (1.0.7)
DEPENDENCIES:
- AFNetworking
@ -50,6 +51,7 @@ DEPENDENCIES:
- SSZipArchive
- "UITableView+FDTemplateLayoutCell"
- YYModel
- YYText
SPEC REPOS:
trunk:
@ -69,9 +71,10 @@ SPEC REPOS:
- SSZipArchive
- "UITableView+FDTemplateLayoutCell"
- YYModel
- YYText
SPEC CHECKSUMS:
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
DZNEmptyDataSet: 9525833b9e68ac21c30253e1d3d7076cc828eaa7
IQKeyboardManager: 45a1fa55c1a5b02c61ac0fd7fd5b62bb4ad20d97
LSTTimer: caf8f02ff366ca175cf4c1778d26c166183c1b6f
@ -87,7 +90,8 @@ SPEC CHECKSUMS:
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
"UITableView+FDTemplateLayoutCell": 5c949b4a5059c404b442926c0e80f81d10a2d66f
YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
PODFILE CHECKSUM: 9e63afc3a6c622d807df578d0a72b7f55bd6d3f4
PODFILE CHECKSUM: 3a3f16ff03dd509a5468a270e3ff3de69a081d1e
COCOAPODS: 1.12.1
COCOAPODS: 1.15.2

View File

@ -32,6 +32,7 @@ PODS:
- SSZipArchive (2.4.3)
- "UITableView+FDTemplateLayoutCell (1.6)"
- YYModel (1.0.4)
- YYText (1.0.7)
DEPENDENCIES:
- AFNetworking
@ -50,6 +51,7 @@ DEPENDENCIES:
- SSZipArchive
- "UITableView+FDTemplateLayoutCell"
- YYModel
- YYText
SPEC REPOS:
trunk:
@ -69,9 +71,10 @@ SPEC REPOS:
- SSZipArchive
- "UITableView+FDTemplateLayoutCell"
- YYModel
- YYText
SPEC CHECKSUMS:
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
DZNEmptyDataSet: 9525833b9e68ac21c30253e1d3d7076cc828eaa7
IQKeyboardManager: 45a1fa55c1a5b02c61ac0fd7fd5b62bb4ad20d97
LSTTimer: caf8f02ff366ca175cf4c1778d26c166183c1b6f
@ -87,7 +90,8 @@ SPEC CHECKSUMS:
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
"UITableView+FDTemplateLayoutCell": 5c949b4a5059c404b442926c0e80f81d10a2d66f
YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
YYText: 5c461d709e24d55a182d1441c41dc639a18a4849
PODFILE CHECKSUM: 9e63afc3a6c622d807df578d0a72b7f55bd6d3f4
PODFILE CHECKSUM: 3a3f16ff03dd509a5468a270e3ff3de69a081d1e
COCOAPODS: 1.12.1
COCOAPODS: 1.15.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0130B3724283586C0E9D2A112D4F2AA1"
BuildableName = "AFNetworking.framework"
BlueprintName = "AFNetworking"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F1BCD9702276377FB5B3BDB6EAF709D7"
BuildableName = "DZNEmptyDataSet.framework"
BlueprintName = "DZNEmptyDataSet"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FBA456CB50E371584C11231929A0971E"
BuildableName = "IQKeyboardManager.framework"
BlueprintName = "IQKeyboardManager"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5A2609DB0612F95A3450D2E02CA850A0"
BuildableName = "LSTTimer.framework"
BlueprintName = "LSTTimer"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "82B0A41D3031FF27D78E17B0A9A46FB0"
BuildableName = "MBProgressHUD.framework"
BlueprintName = "MBProgressHUD"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6868056D761E163D10FDAF8CF1C4D9B8"
BuildableName = "MJRefresh.framework"
BlueprintName = "MJRefresh"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9B407B312D9EAE176A2DDE572135AC35"
BuildableName = "MOFSPickerManager.framework"
BlueprintName = "MOFSPickerManager"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "55AF53E6C77A10ED4985E04D74A8878E"
BuildableName = "Masonry.framework"
BlueprintName = "Masonry"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C350D0AF2821ACA667D8ABD5B11F720F"
BuildableName = "Pods_ProductApp.framework"
BlueprintName = "Pods-ProductApp"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "498769A9CE4D8F28BB9BCA14EDF3ADEE"
BuildableName = "PopupKit.framework"
BlueprintName = "PopupKit"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CAA047C0F5E4106F3904E8497FA17F97"
BuildableName = "Reachability.framework"
BlueprintName = "Reachability"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "03932C47FBFD347205B2F367F78E9B63"
BuildableName = "SDAutoLayout.framework"
BlueprintName = "SDAutoLayout"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CBED833AAD6266F3AEFE9BE31C68E094"
BuildableName = "SDCycleScrollView.framework"
BlueprintName = "SDCycleScrollView"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3847153A6E5EEFB86565BA840768F429"
BuildableName = "SDWebImage.framework"
BlueprintName = "SDWebImage"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F60E38364AFF5E1349FF07415B944396"
BuildableName = "SSZipArchive.framework"
BlueprintName = "SSZipArchive"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2006263064A94C0063192AE0D2C3F409"
BuildableName = "UITableView_FDTemplateLayoutCell.framework"
BlueprintName = "UITableView+FDTemplateLayoutCell"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "84B44807A12996D487A4A591A481D6A0"
BuildableName = "YYModel.framework"
BlueprintName = "YYModel"
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>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
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 = "YYText.framework"
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>

View File

@ -4,91 +4,98 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>AFNetworking.xcscheme_^#shared#^_</key>
<key>AFNetworking.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
<key>isShown</key>
<false/>
</dict>
<key>DZNEmptyDataSet.xcscheme_^#shared#^_</key>
<key>DZNEmptyDataSet.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>4</integer>
<key>isShown</key>
<false/>
</dict>
<key>IQKeyboardManager.xcscheme_^#shared#^_</key>
<key>IQKeyboardManager.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>17</integer>
<key>isShown</key>
<false/>
</dict>
<key>LSTTimer.xcscheme_^#shared#^_</key>
<key>LSTTimer.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>15</integer>
<key>isShown</key>
<false/>
</dict>
<key>MBProgressHUD.xcscheme_^#shared#^_</key>
<key>MBProgressHUD.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
<key>isShown</key>
<false/>
</dict>
<key>MJRefresh.xcscheme_^#shared#^_</key>
<key>MJRefresh.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>19</integer>
<key>isShown</key>
<false/>
</dict>
<key>MOFSPickerManager.xcscheme_^#shared#^_</key>
<key>MOFSPickerManager.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>11</integer>
<key>isShown</key>
<false/>
</dict>
<key>Masonry.xcscheme_^#shared#^_</key>
<key>Masonry.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>13</integer>
<key>isShown</key>
<false/>
</dict>
<key>Pods-ProductApp.xcscheme_^#shared#^_</key>
<key>Pods-ProductApp.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>16</integer>
<key>isShown</key>
<false/>
</dict>
<key>PopupKit.xcscheme_^#shared#^_</key>
<key>PopupKit.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>7</integer>
<key>isShown</key>
<false/>
</dict>
<key>Reachability.xcscheme_^#shared#^_</key>
<key>Reachability.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>18</integer>
<key>isShown</key>
<false/>
</dict>
<key>SDAutoLayout.xcscheme_^#shared#^_</key>
<key>SDAutoLayout.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>3</integer>
<key>isShown</key>
<false/>
</dict>
<key>SDCycleScrollView.xcscheme_^#shared#^_</key>
<key>SDCycleScrollView.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>10</integer>
<key>isShown</key>
<false/>
</dict>
<key>SDWebImage.xcscheme_^#shared#^_</key>
<key>SDWebImage.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>12</integer>
<key>isShown</key>
<false/>
</dict>
<key>SSZipArchive.xcscheme_^#shared#^_</key>
<key>SSZipArchive.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>6</integer>
<key>isShown</key>
<false/>
</dict>
<key>UITableView+FDTemplateLayoutCell.xcscheme_^#shared#^_</key>
<key>UITableView+FDTemplateLayoutCell.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>14</integer>
<key>isShown</key>
<false/>
</dict>
<key>YYModel.xcscheme_^#shared#^_</key>
<key>YYModel.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>9</integer>
<key>isShown</key>
<false/>
</dict>
<key>YYText.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict/>
</dict>
</plist>

View File

@ -366,4 +366,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

View File

@ -474,6 +474,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>

View File

@ -14,4 +14,5 @@ ${BUILT_PRODUCTS_DIR}/SDCycleScrollView/SDCycleScrollView.framework
${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework
${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework
${BUILT_PRODUCTS_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
${BUILT_PRODUCTS_DIR}/YYText/YYText.framework

View File

@ -13,4 +13,5 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDCycleScrollView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UITableView_FDTemplateLayoutCell.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYText.framework

View File

@ -14,4 +14,5 @@ ${BUILT_PRODUCTS_DIR}/SDCycleScrollView/SDCycleScrollView.framework
${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework
${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework
${BUILT_PRODUCTS_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
${BUILT_PRODUCTS_DIR}/YYText/YYText.framework

View File

@ -13,4 +13,5 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDCycleScrollView.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UITableView_FDTemplateLayoutCell.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYText.framework

View File

@ -18,7 +18,7 @@ echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
SWIFT_STDLIB_PATH="${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
BCSYMBOLMAP_DIR="BCSymbolMaps"
@ -192,6 +192,7 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework"
install_framework "${BUILT_PRODUCTS_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYText/YYText.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework"
@ -210,6 +211,7 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework"
install_framework "${BUILT_PRODUCTS_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYText/YYText.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait

View File

@ -1,9 +1,9 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYText"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager/IQKeyboardManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer/LSTTimer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager/MOFSPickerManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit/PopupKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout/SDAutoLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager/IQKeyboardManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer/LSTTimer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager/MOFSPickerManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit/PopupKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout/SDAutoLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYText/YYText.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -l"iconv" -l"z" -framework "AFNetworking" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DZNEmptyDataSet" -framework "Foundation" -framework "IQKeyboardManager" -framework "ImageIO" -framework "LSTTimer" -framework "MBProgressHUD" -framework "MJRefresh" -framework "MOFSPickerManager" -framework "Masonry" -framework "PopupKit" -framework "QuartzCore" -framework "Reachability" -framework "SDAutoLayout" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SSZipArchive" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -framework "UITableView_FDTemplateLayoutCell" -framework "YYModel"
OTHER_LDFLAGS = $(inherited) -l"iconv" -l"z" -framework "AFNetworking" -framework "Accelerate" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreText" -framework "DZNEmptyDataSet" -framework "Foundation" -framework "IQKeyboardManager" -framework "ImageIO" -framework "LSTTimer" -framework "MBProgressHUD" -framework "MJRefresh" -framework "MOFSPickerManager" -framework "Masonry" -framework "MobileCoreServices" -framework "PopupKit" -framework "QuartzCore" -framework "Reachability" -framework "SDAutoLayout" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SSZipArchive" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -framework "UITableView_FDTemplateLayoutCell" -framework "YYModel" -framework "YYText"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.

View File

@ -1,9 +1,9 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYText"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager/IQKeyboardManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer/LSTTimer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager/MOFSPickerManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit/PopupKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout/SDAutoLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers"
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManager/IQKeyboardManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/LSTTimer/LSTTimer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MOFSPickerManager/MOFSPickerManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PopupKit/PopupKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Reachability/Reachability.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDAutoLayout/SDAutoLayout.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SSZipArchive/SSZipArchive.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/UITableView+FDTemplateLayoutCell/UITableView_FDTemplateLayoutCell.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYText/YYText.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -l"iconv" -l"z" -framework "AFNetworking" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DZNEmptyDataSet" -framework "Foundation" -framework "IQKeyboardManager" -framework "ImageIO" -framework "LSTTimer" -framework "MBProgressHUD" -framework "MJRefresh" -framework "MOFSPickerManager" -framework "Masonry" -framework "PopupKit" -framework "QuartzCore" -framework "Reachability" -framework "SDAutoLayout" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SSZipArchive" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -framework "UITableView_FDTemplateLayoutCell" -framework "YYModel"
OTHER_LDFLAGS = $(inherited) -l"iconv" -l"z" -framework "AFNetworking" -framework "Accelerate" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreText" -framework "DZNEmptyDataSet" -framework "Foundation" -framework "IQKeyboardManager" -framework "ImageIO" -framework "LSTTimer" -framework "MBProgressHUD" -framework "MJRefresh" -framework "MOFSPickerManager" -framework "Masonry" -framework "MobileCoreServices" -framework "PopupKit" -framework "QuartzCore" -framework "Reachability" -framework "SDAutoLayout" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SSZipArchive" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -framework "UITableView_FDTemplateLayoutCell" -framework "YYModel" -framework "YYText"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.7</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

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

View File

@ -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

View File

@ -0,0 +1,41 @@
#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
#import "YYTextContainerView.h"
#import "YYTextDebugOption.h"
#import "YYTextEffectWindow.h"
#import "YYTextInput.h"
#import "YYTextKeyboardManager.h"
#import "YYTextLayout.h"
#import "YYTextLine.h"
#import "YYTextMagnifier.h"
#import "YYTextSelectionView.h"
#import "YYTextArchiver.h"
#import "YYTextAttribute.h"
#import "YYTextParser.h"
#import "YYTextRubyAnnotation.h"
#import "YYTextRunDelegate.h"
#import "NSAttributedString+YYText.h"
#import "NSParagraphStyle+YYText.h"
#import "UIPasteboard+YYText.h"
#import "UIView+YYText.h"
#import "YYTextAsyncLayer.h"
#import "YYTextTransaction.h"
#import "YYTextUtilities.h"
#import "YYTextWeakProxy.h"
#import "YYLabel.h"
#import "YYText.h"
#import "YYTextView.h"
FOUNDATION_EXPORT double YYTextVersionNumber;
FOUNDATION_EXPORT const unsigned char YYTextVersionString[];

View File

@ -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
OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CoreFoundation" -framework "CoreText" -framework "MobileCoreServices" -framework "QuartzCore" -framework "UIKit"
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

View File

@ -0,0 +1,6 @@
framework module YYText {
umbrella header "YYText-umbrella.h"
export *
module * { export * }
}

View File

@ -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
OTHER_LDFLAGS = $(inherited) -framework "Accelerate" -framework "CoreFoundation" -framework "CoreText" -framework "MobileCoreServices" -framework "QuartzCore" -framework "UIKit"
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
ProductApp/Pods/YYText/LICENSE generated Normal file
View File

@ -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.

1095
ProductApp/Pods/YYText/README.md generated Executable file

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,146 @@
//
// UIPasteboard+YYText.m
// 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 "UIPasteboard+YYText.h"
#import "NSAttributedString+YYText.h"
#import <MobileCoreServices/MobileCoreServices.h>
#if __has_include("YYImage.h")
#import "YYImage.h"
#define YYTextAnimatedImageAvailable 1
#elif __has_include(<YYImage/YYImage.h>)
#import <YYImage/YYImage.h>
#define YYTextAnimatedImageAvailable 1
#elif __has_include(<YYWebImage/YYImage.h>)
#import <YYWebImage/YYImage.h>
#define YYTextAnimatedImageAvailable 1
#else
#define YYTextAnimatedImageAvailable 0
#endif
// Dummy class for category
@interface UIPasteboard_YYText : NSObject @end
@implementation UIPasteboard_YYText @end
NSString *const YYTextPasteboardTypeAttributedString = @"com.ibireme.NSAttributedString";
NSString *const YYTextUTTypeWEBP = @"com.google.webp";
@implementation UIPasteboard (YYText)
- (void)setYy_PNGData:(NSData *)PNGData {
[self setData:PNGData forPasteboardType:(id)kUTTypePNG];
}
- (NSData *)yy_PNGData {
return [self dataForPasteboardType:(id)kUTTypePNG];
}
- (void)setYy_JPEGData:(NSData *)JPEGData {
[self setData:JPEGData forPasteboardType:(id)kUTTypeJPEG];
}
- (NSData *)yy_JPEGData {
return [self dataForPasteboardType:(id)kUTTypeJPEG];
}
- (void)setYy_GIFData:(NSData *)GIFData {
[self setData:GIFData forPasteboardType:(id)kUTTypeGIF];
}
- (NSData *)yy_GIFData {
return [self dataForPasteboardType:(id)kUTTypeGIF];
}
- (void)setYy_WEBPData:(NSData *)WEBPData {
[self setData:WEBPData forPasteboardType:YYTextUTTypeWEBP];
}
- (NSData *)yy_WEBPData {
return [self dataForPasteboardType:YYTextUTTypeWEBP];
}
- (void)setYy_ImageData:(NSData *)imageData {
[self setData:imageData forPasteboardType:(id)kUTTypeImage];
}
- (NSData *)yy_ImageData {
return [self dataForPasteboardType:(id)kUTTypeImage];
}
- (void)setYy_AttributedString:(NSAttributedString *)attributedString {
self.string = [attributedString yy_plainTextForRange:NSMakeRange(0, attributedString.length)];
NSData *data = [attributedString yy_archiveToData];
if (data) {
NSDictionary *item = @{YYTextPasteboardTypeAttributedString : data};
[self addItems:@[item]];
}
[attributedString enumerateAttribute:YYTextAttachmentAttributeName inRange:NSMakeRange(0, attributedString.length) options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(YYTextAttachment *attachment, NSRange range, BOOL *stop) {
// save image
UIImage *simpleImage = nil;
if ([attachment.content isKindOfClass:[UIImage class]]) {
simpleImage = attachment.content;
} else if ([attachment.content isKindOfClass:[UIImageView class]]) {
simpleImage = ((UIImageView *)attachment.content).image;
}
if (simpleImage) {
NSDictionary *item = @{@"com.apple.uikit.image" : simpleImage};
[self addItems:@[item]];
}
#if YYTextAnimatedImageAvailable
// save animated image
if ([attachment.content isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = attachment.content;
Class aniImageClass = NSClassFromString(@"YYImage");
UIImage *image = imageView.image;
if (aniImageClass && [image isKindOfClass:aniImageClass]) {
NSData *data = [image valueForKey:@"animatedImageData"];
NSNumber *type = [image valueForKey:@"animatedImageType"];
if (data) {
switch (type.unsignedIntegerValue) {
case YYImageTypeGIF: {
NSDictionary *item = @{(id)kUTTypeGIF : data};
[self addItems:@[item]];
} break;
case YYImageTypePNG: { // APNG
NSDictionary *item = @{(id)kUTTypePNG : data};
[self addItems:@[item]];
} break;
case YYImageTypeWebP: {
NSDictionary *item = @{(id)YYTextUTTypeWEBP : data};
[self addItems:@[item]];
} break;
default: break;
}
}
}
}
#endif
}];
}
- (NSAttributedString *)yy_AttributedString {
for (NSDictionary *items in self.items) {
NSData *data = items[YYTextPasteboardTypeAttributedString];
if (data) {
return [NSAttributedString yy_unarchiveFromData:data];
}
}
return nil;
}
@end

View File

@ -0,0 +1,72 @@
//
// UIView+YYText.h
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 13/4/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
/**
Provides extensions for `UIView`.
*/
@interface UIView (YYText)
/**
Returns the view's view controller (may be nil).
*/
@property (nullable, nonatomic, readonly) UIViewController *yy_viewController;
/**
Returns the visible alpha on screen, taking into account superview and window.
*/
@property (nonatomic, readonly) CGFloat yy_visibleAlpha;
/**
Converts a point from the receiver's coordinate system to that of the specified view or window.
@param point A point specified in the local coordinate system (bounds) of the receiver.
@param view The view or window into whose coordinate system point is to be converted.
If view is nil, this method instead converts to window base coordinates.
@return The point converted to the coordinate system of view.
*/
- (CGPoint)yy_convertPoint:(CGPoint)point toViewOrWindow:(UIView *)view;
/**
Converts a point from the coordinate system of a given view or window to that of the receiver.
@param point A point specified in the local coordinate system (bounds) of view.
@param view The view or window with point in its coordinate system.
If view is nil, this method instead converts from window base coordinates.
@return The point converted to the local coordinate system (bounds) of the receiver.
*/
- (CGPoint)yy_convertPoint:(CGPoint)point fromViewOrWindow:(UIView *)view;
/**
Converts a rectangle from the receiver's coordinate system to that of another view or window.
@param rect A rectangle specified in the local coordinate system (bounds) of the receiver.
@param view The view or window that is the target of the conversion operation. If view is nil, this method instead converts to window base coordinates.
@return The converted rectangle.
*/
- (CGRect)yy_convertRect:(CGRect)rect toViewOrWindow:(UIView *)view;
/**
Converts a rectangle from the coordinate system of another view or window to that of the receiver.
@param rect A rectangle specified in the local coordinate system (bounds) of view.
@param view The view or window with rect in its coordinate system.
If view is nil, this method instead converts from window base coordinates.
@return The converted rectangle.
*/
- (CGRect)yy_convertRect:(CGRect)rect fromViewOrWindow:(UIView *)view;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,123 @@
//
// UIView+YYText.m
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 13/4/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 "UIView+YYText.h"
// Dummy class for category
@interface UIView_YYText : NSObject @end
@implementation UIView_YYText @end
@implementation UIView (YYText)
- (UIViewController *)yy_viewController {
for (UIView *view = self; view; view = view.superview) {
UIResponder *nextResponder = [view nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return (UIViewController *)nextResponder;
}
}
return nil;
}
- (CGFloat)yy_visibleAlpha {
if ([self isKindOfClass:[UIWindow class]]) {
if (self.hidden) return 0;
return self.alpha;
}
if (!self.window) return 0;
CGFloat alpha = 1;
UIView *v = self;
while (v) {
if (v.hidden) {
alpha = 0;
break;
}
alpha *= v.alpha;
v = v.superview;
}
return alpha;
}
- (CGPoint)yy_convertPoint:(CGPoint)point toViewOrWindow:(UIView *)view {
if (!view) {
if ([self isKindOfClass:[UIWindow class]]) {
return [((UIWindow *)self) convertPoint:point toWindow:nil];
} else {
return [self convertPoint:point toView:nil];
}
}
UIWindow *from = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window;
UIWindow *to = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
if ((!from || !to) || (from == to)) return [self convertPoint:point toView:view];
point = [self convertPoint:point toView:from];
point = [to convertPoint:point fromWindow:from];
point = [view convertPoint:point fromView:to];
return point;
}
- (CGPoint)yy_convertPoint:(CGPoint)point fromViewOrWindow:(UIView *)view {
if (!view) {
if ([self isKindOfClass:[UIWindow class]]) {
return [((UIWindow *)self) convertPoint:point fromWindow:nil];
} else {
return [self convertPoint:point fromView:nil];
}
}
UIWindow *from = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
UIWindow *to = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window;
if ((!from || !to) || (from == to)) return [self convertPoint:point fromView:view];
point = [from convertPoint:point fromView:view];
point = [to convertPoint:point fromWindow:from];
point = [self convertPoint:point fromView:to];
return point;
}
- (CGRect)yy_convertRect:(CGRect)rect toViewOrWindow:(UIView *)view {
if (!view) {
if ([self isKindOfClass:[UIWindow class]]) {
return [((UIWindow *)self) convertRect:rect toWindow:nil];
} else {
return [self convertRect:rect toView:nil];
}
}
UIWindow *from = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window;
UIWindow *to = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
if (!from || !to) return [self convertRect:rect toView:view];
if (from == to) return [self convertRect:rect toView:view];
rect = [self convertRect:rect toView:from];
rect = [to convertRect:rect fromWindow:from];
rect = [view convertRect:rect fromView:to];
return rect;
}
- (CGRect)yy_convertRect:(CGRect)rect fromViewOrWindow:(UIView *)view {
if (!view) {
if ([self isKindOfClass:[UIWindow class]]) {
return [((UIWindow *)self) convertRect:rect fromWindow:nil];
} else {
return [self convertRect:rect fromView:nil];
}
}
UIWindow *from = [view isKindOfClass:[UIWindow class]] ? (id)view : view.window;
UIWindow *to = [self isKindOfClass:[UIWindow class]] ? (id)self : self.window;
if ((!from || !to) || (from == to)) return [self convertRect:rect fromView:view];
rect = [from convertRect:rect fromView:view];
rect = [to convertRect:rect fromWindow:from];
rect = [self convertRect:rect fromView:to];
return rect;
}
@end

View File

@ -0,0 +1,79 @@
//
// YYTextAsyncLayer.h
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 15/4/11.
// 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 <QuartzCore/QuartzCore.h>
@class YYTextAsyncLayerDisplayTask;
NS_ASSUME_NONNULL_BEGIN
/**
The YYTextAsyncLayer class is a subclass of CALayer used for render contents asynchronously.
@discussion When the layer need update it's contents, it will ask the delegate
for a async display task to render the contents in a background queue.
*/
@interface YYTextAsyncLayer : CALayer
/// Whether the render code is executed in background. Default is YES.
@property BOOL displaysAsynchronously;
@end
/**
The YYTextAsyncLayer's delegate protocol. The delegate of the YYTextAsyncLayer (typically a UIView)
must implements the method in this protocol.
*/
@protocol YYTextAsyncLayerDelegate <NSObject>
@required
/// This method is called to return a new display task when the layer's contents need update.
- (YYTextAsyncLayerDisplayTask *)newAsyncDisplayTask;
@end
/**
A display task used by YYTextAsyncLayer to render the contents in background queue.
*/
@interface YYTextAsyncLayerDisplayTask : NSObject
/**
This block will be called before the asynchronous drawing begins.
It will be called on the main thread.
@param layer The layer.
*/
@property (nullable, nonatomic, copy) void (^willDisplay)(CALayer *layer);
/**
This block is called to draw the layer's contents.
@discussion This block may be called on main thread or background thread,
so is should be thread-safe.
@param context A new bitmap content created by layer.
@param size The content size (typically same as layer's bound size).
@param isCancelled If this block returns `YES`, the method should cancel the
drawing process and return as quickly as possible.
*/
@property (nullable, nonatomic, copy) void (^display)(CGContextRef context, CGSize size, BOOL(^isCancelled)(void));
/**
This block will be called after the asynchronous drawing finished.
It will be called on the main thread.
@param layer The layer.
@param finished If the draw process is cancelled, it's `NO`, otherwise it's `YES`;
*/
@property (nullable, nonatomic, copy) void (^didDisplay)(CALayer *layer, BOOL finished);
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,234 @@
//
// YYTextAsyncLayer.m
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 15/4/11.
// 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 "YYTextAsyncLayer.h"
#import <libkern/OSAtomic.h>
/// Global display queue, used for content rendering.
static dispatch_queue_t YYTextAsyncLayerGetDisplayQueue() {
#define MAX_QUEUE_COUNT 16
static int queueCount;
static dispatch_queue_t queues[MAX_QUEUE_COUNT];
static dispatch_once_t onceToken;
static int32_t counter = 0;
dispatch_once(&onceToken, ^{
queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
for (NSUInteger i = 0; i < queueCount; i++) {
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
queues[i] = dispatch_queue_create("com.ibireme.text.render", attr);
}
} else {
for (NSUInteger i = 0; i < queueCount; i++) {
queues[i] = dispatch_queue_create("com.ibireme.text.render", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
}
}
});
uint32_t cur = (uint32_t)OSAtomicIncrement32(&counter);
return queues[(cur) % queueCount];
#undef MAX_QUEUE_COUNT
}
static dispatch_queue_t YYTextAsyncLayerGetReleaseQueue() {
#ifdef YYDispatchQueuePool_h
return YYDispatchQueueGetForQOS(NSQualityOfServiceDefault);
#else
return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
#endif
}
/// a thread safe incrementing counter.
@interface _YYTextSentinel : NSObject
/// Returns the current value of the counter.
@property (atomic, readonly) int32_t value;
/// Increase the value atomically. @return The new value.
- (int32_t)increase;
@end
@implementation _YYTextSentinel {
int32_t _value;
}
- (int32_t)value {
return _value;
}
- (int32_t)increase {
return OSAtomicIncrement32(&_value);
}
@end
@implementation YYTextAsyncLayerDisplayTask
@end
@implementation YYTextAsyncLayer {
_YYTextSentinel *_sentinel;
}
#pragma mark - Override
+ (id)defaultValueForKey:(NSString *)key {
if ([key isEqualToString:@"displaysAsynchronously"]) {
return @(YES);
} else {
return [super defaultValueForKey:key];
}
}
- (instancetype)init {
self = [super init];
static CGFloat scale; //global
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
scale = [UIScreen mainScreen].scale;
});
self.contentsScale = scale;
_sentinel = [_YYTextSentinel new];
_displaysAsynchronously = YES;
return self;
}
- (void)dealloc {
[_sentinel increase];
}
- (void)setNeedsDisplay {
[self _cancelAsyncDisplay];
[super setNeedsDisplay];
}
- (void)display {
super.contents = super.contents;
[self _displayAsync:_displaysAsynchronously];
}
#pragma mark - Private
- (void)_displayAsync:(BOOL)async {
__strong id<YYTextAsyncLayerDelegate> delegate = (id)self.delegate;
YYTextAsyncLayerDisplayTask *task = [delegate newAsyncDisplayTask];
if (!task.display) {
if (task.willDisplay) task.willDisplay(self);
self.contents = nil;
if (task.didDisplay) task.didDisplay(self, YES);
return;
}
if (async) {
if (task.willDisplay) task.willDisplay(self);
_YYTextSentinel *sentinel = _sentinel;
int32_t value = sentinel.value;
BOOL (^isCancelled)() = ^BOOL() {
return value != sentinel.value;
};
CGSize size = self.bounds.size;
BOOL opaque = self.opaque;
CGFloat scale = self.contentsScale;
CGColorRef backgroundColor = (opaque && self.backgroundColor) ? CGColorRetain(self.backgroundColor) : NULL;
if (size.width < 1 || size.height < 1) {
CGImageRef image = (__bridge_retained CGImageRef)(self.contents);
self.contents = nil;
if (image) {
dispatch_async(YYTextAsyncLayerGetReleaseQueue(), ^{
CFRelease(image);
});
}
if (task.didDisplay) task.didDisplay(self, YES);
CGColorRelease(backgroundColor);
return;
}
dispatch_async(YYTextAsyncLayerGetDisplayQueue(), ^{
if (isCancelled()) {
CGColorRelease(backgroundColor);
return;
}
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (opaque) {
CGContextSaveGState(context); {
if (!backgroundColor || CGColorGetAlpha(backgroundColor) < 1) {
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
CGContextFillPath(context);
}
if (backgroundColor) {
CGContextSetFillColorWithColor(context, backgroundColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale));
CGContextFillPath(context);
}
} CGContextRestoreGState(context);
CGColorRelease(backgroundColor);
}
task.display(context, size, isCancelled);
if (isCancelled()) {
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
if (task.didDisplay) task.didDisplay(self, NO);
});
return;
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (isCancelled()) {
dispatch_async(dispatch_get_main_queue(), ^{
if (task.didDisplay) task.didDisplay(self, NO);
});
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
if (isCancelled()) {
if (task.didDisplay) task.didDisplay(self, NO);
} else {
self.contents = (__bridge id)(image.CGImage);
if (task.didDisplay) task.didDisplay(self, YES);
}
});
});
} else {
[_sentinel increase];
if (task.willDisplay) task.willDisplay(self);
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (self.opaque) {
CGSize size = self.bounds.size;
size.width *= self.contentsScale;
size.height *= self.contentsScale;
CGContextSaveGState(context); {
if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) {
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
CGContextFillPath(context);
}
if (self.backgroundColor) {
CGContextSetFillColorWithColor(context, self.backgroundColor);
CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height));
CGContextFillPath(context);
}
} CGContextRestoreGState(context);
}
task.display(context, self.bounds.size, ^{return NO;});
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.contents = (__bridge id)(image.CGImage);
if (task.didDisplay) task.didDisplay(self, YES);
}
}
- (void)_cancelAsyncDisplay {
[_sentinel increase];
}
@end

View File

@ -0,0 +1,42 @@
//
// YYTextTransaction.h
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 15/4/18.
// 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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
YYTextTransaction let you perform a selector once before current runloop sleep.
*/
@interface YYTextTransaction : NSObject
/**
Creates and returns a transaction with a specified target and selector.
@param target A specified target, the target is retained until runloop end.
@param selector A selector for target.
@return A new transaction, or nil if an error occurs.
*/
+ (YYTextTransaction *)transactionWithTarget:(id)target selector:(SEL)selector;
/**
Commit the trancaction to main runloop.
@discussion It will perform the selector on the target once before main runloop's
current loop sleep. If the same transaction (same target and same selector) has
already commit to runloop in this loop, this method do nothing.
*/
- (void)commit;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,81 @@
//
// YYTextTransaction.m
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 15/4/18.
// 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 "YYTextTransaction.h"
@interface YYTextTransaction()
@property (nonatomic, strong) id target;
@property (nonatomic, assign) SEL selector;
@end
static NSMutableSet *transactionSet = nil;
static void YYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
if (transactionSet.count == 0) return;
NSSet *currentSet = transactionSet;
transactionSet = [NSMutableSet new];
[currentSet enumerateObjectsUsingBlock:^(YYTextTransaction *transaction, BOOL *stop) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[transaction.target performSelector:transaction.selector];
#pragma clang diagnostic pop
}];
}
static void YYTextTransactionSetup() {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
transactionSet = [NSMutableSet new];
CFRunLoopRef runloop = CFRunLoopGetMain();
CFRunLoopObserverRef observer;
observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(),
kCFRunLoopBeforeWaiting | kCFRunLoopExit,
true, // repeat
0xFFFFFF, // after CATransaction(2000000)
YYRunLoopObserverCallBack, NULL);
CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes);
CFRelease(observer);
});
}
@implementation YYTextTransaction
+ (YYTextTransaction *)transactionWithTarget:(id)target selector:(SEL)selector{
if (!target || !selector) return nil;
YYTextTransaction *t = [YYTextTransaction new];
t.target = target;
t.selector = selector;
return t;
}
- (void)commit {
if (!_target || !_selector) return;
YYTextTransactionSetup();
[transactionSet addObject:self];
}
- (NSUInteger)hash {
long v1 = (long)((void *)_selector);
long v2 = (long)_target;
return v1 ^ v2;
}
- (BOOL)isEqual:(id)object {
if (self == object) return YES;
if (![object isMemberOfClass:self.class]) return NO;
YYTextTransaction *other = object;
return other.selector == _selector && other.target == _target;
}
@end

View File

@ -0,0 +1,563 @@
//
// YYTextUtilities.h
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 15/4/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>
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.h>
#ifndef YYTEXT_CLAMP // return the clamped value
#define YYTEXT_CLAMP(_x_, _low_, _high_) (((_x_) > (_high_)) ? (_high_) : (((_x_) < (_low_)) ? (_low_) : (_x_)))
#endif
#ifndef YYTEXT_SWAP // swap two value
#define YYTEXT_SWAP(_a_, _b_) do { __typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = _tmp_; } while (0)
#endif
NS_ASSUME_NONNULL_BEGIN
/**
Whether the character is 'line break char':
U+000D (\\r or CR)
U+2028 (Unicode line separator)
U+000A (\\n or LF)
U+2029 (Unicode paragraph separator)
@param c A character
@return YES or NO.
*/
static inline BOOL YYTextIsLinebreakChar(unichar c) {
switch (c) {
case 0x000D:
case 0x2028:
case 0x000A:
case 0x2029:
return YES;
default:
return NO;
}
}
/**
Whether the string is a 'line break':
U+000D (\\r or CR)
U+2028 (Unicode line separator)
U+000A (\\n or LF)
U+2029 (Unicode paragraph separator)
\\r\\n, in that order (also known as CRLF)
@param str A string
@return YES or NO.
*/
static inline BOOL YYTextIsLinebreakString(NSString * _Nullable str) {
if (str.length > 2 || str.length == 0) return NO;
if (str.length == 1) {
unichar c = [str characterAtIndex:0];
return YYTextIsLinebreakChar(c);
} else {
return ([str characterAtIndex:0] == '\r') && ([str characterAtIndex:1] == '\n');
}
}
/**
If the string has a 'line break' suffix, return the 'line break' length.
@param str A string.
@return The length of the tail line break: 0, 1 or 2.
*/
static inline NSUInteger YYTextLinebreakTailLength(NSString * _Nullable str) {
if (str.length >= 2) {
unichar c2 = [str characterAtIndex:str.length - 1];
if (YYTextIsLinebreakChar(c2)) {
unichar c1 = [str characterAtIndex:str.length - 2];
if (c1 == '\r' && c2 == '\n') return 2;
else return 1;
} else {
return 0;
}
} else if (str.length == 1) {
return YYTextIsLinebreakChar([str characterAtIndex:0]) ? 1 : 0;
} else {
return 0;
}
}
/**
Convert `UIDataDetectorTypes` to `NSTextCheckingType`.
@param types The `UIDataDetectorTypes` type.
@return The `NSTextCheckingType` type.
*/
static inline NSTextCheckingType YYTextNSTextCheckingTypeFromUIDataDetectorType(UIDataDetectorTypes types) {
NSTextCheckingType t = 0;
if (types & UIDataDetectorTypePhoneNumber) t |= NSTextCheckingTypePhoneNumber;
if (types & UIDataDetectorTypeLink) t |= NSTextCheckingTypeLink;
if (types & UIDataDetectorTypeAddress) t |= NSTextCheckingTypeAddress;
if (types & UIDataDetectorTypeCalendarEvent) t |= NSTextCheckingTypeDate;
return t;
}
/**
Whether the font is `AppleColorEmoji` font.
@param font A font.
@return YES: the font is Emoji, NO: the font is not Emoji.
*/
static inline BOOL YYTextUIFontIsEmoji(UIFont *font) {
return [font.fontName isEqualToString:@"AppleColorEmoji"];
}
/**
Whether the font is `AppleColorEmoji` font.
@param font A font.
@return YES: the font is Emoji, NO: the font is not Emoji.
*/
static inline BOOL YYTextCTFontIsEmoji(CTFontRef font) {
BOOL isEmoji = NO;
CFStringRef name = CTFontCopyPostScriptName(font);
if (name && CFEqual(CFSTR("AppleColorEmoji"), name)) isEmoji = YES;
if (name) CFRelease(name);
return isEmoji;
}
/**
Whether the font is `AppleColorEmoji` font.
@param font A font.
@return YES: the font is Emoji, NO: the font is not Emoji.
*/
static inline BOOL YYTextCGFontIsEmoji(CGFontRef font) {
BOOL isEmoji = NO;
CFStringRef name = CGFontCopyPostScriptName(font);
if (name && CFEqual(CFSTR("AppleColorEmoji"), name)) isEmoji = YES;
if (name) CFRelease(name);
return isEmoji;
}
/**
Whether the font contains color bitmap glyphs.
@discussion Only `AppleColorEmoji` contains color bitmap glyphs in iOS system fonts.
@param font A font.
@return YES: the font contains color bitmap glyphs, NO: the font has no color bitmap glyph.
*/
static inline BOOL YYTextCTFontContainsColorBitmapGlyphs(CTFontRef font) {
return (CTFontGetSymbolicTraits(font) & kCTFontTraitColorGlyphs) != 0;
}
/**
Whether the glyph is bitmap.
@param font The glyph's font.
@param glyph The glyph which is created from the specified font.
@return YES: the glyph is bitmap, NO: the glyph is vector.
*/
static inline BOOL YYTextCGGlyphIsBitmap(CTFontRef font, CGGlyph glyph) {
if (!font && !glyph) return NO;
if (!YYTextCTFontContainsColorBitmapGlyphs(font)) return NO;
CGPathRef path = CTFontCreatePathForGlyph(font, glyph, NULL);
if (path) {
CFRelease(path);
return NO;
}
return YES;
}
/**
Get the `AppleColorEmoji` font's ascent with a specified font size.
It may used to create custom emoji.
@param fontSize The specified font size.
@return The font ascent.
*/
static inline CGFloat YYTextEmojiGetAscentWithFontSize(CGFloat fontSize) {
if (fontSize < 16) {
return 1.25 * fontSize;
} else if (16 <= fontSize && fontSize <= 24) {
return 0.5 * fontSize + 12;
} else {
return fontSize;
}
}
/**
Get the `AppleColorEmoji` font's descent with a specified font size.
It may used to create custom emoji.
@param fontSize The specified font size.
@return The font descent.
*/
static inline CGFloat YYTextEmojiGetDescentWithFontSize(CGFloat fontSize) {
if (fontSize < 16) {
return 0.390625 * fontSize;
} else if (16 <= fontSize && fontSize <= 24) {
return 0.15625 * fontSize + 3.75;
} else {
return 0.3125 * fontSize;
}
return 0;
}
/**
Get the `AppleColorEmoji` font's glyph bounding rect with a specified font size.
It may used to create custom emoji.
@param fontSize The specified font size.
@return The font glyph bounding rect.
*/
static inline CGRect YYTextEmojiGetGlyphBoundingRectWithFontSize(CGFloat fontSize) {
CGRect rect;
rect.origin.x = 0.75;
rect.size.width = rect.size.height = YYTextEmojiGetAscentWithFontSize(fontSize);
if (fontSize < 16) {
rect.origin.y = -0.2525 * fontSize;
} else if (16 <= fontSize && fontSize <= 24) {
rect.origin.y = 0.1225 * fontSize -6;
} else {
rect.origin.y = -0.1275 * fontSize;
}
return rect;
}
/**
Get the character set which should rotate in vertical form.
@return The shared character set.
*/
NSCharacterSet *YYTextVerticalFormRotateCharacterSet();
/**
Get the character set which should rotate and move in vertical form.
@return The shared character set.
*/
NSCharacterSet *YYTextVerticalFormRotateAndMoveCharacterSet();
/// Convert degrees to radians.
static inline CGFloat YYTextDegreesToRadians(CGFloat degrees) {
return degrees * M_PI / 180;
}
/// Convert radians to degrees.
static inline CGFloat YYTextRadiansToDegrees(CGFloat radians) {
return radians * 180 / M_PI;
}
/// Get the transform rotation.
/// @return the rotation in radians [-PI,PI] ([-180°,180°])
static inline CGFloat YYTextCGAffineTransformGetRotation(CGAffineTransform transform) {
return atan2(transform.b, transform.a);
}
/// Get the transform's scale.x
static inline CGFloat YYTextCGAffineTransformGetScaleX(CGAffineTransform transform) {
return sqrt(transform.a * transform.a + transform.c * transform.c);
}
/// Get the transform's scale.y
static inline CGFloat YYTextCGAffineTransformGetScaleY(CGAffineTransform transform) {
return sqrt(transform.b * transform.b + transform.d * transform.d);
}
/// Get the transform's translate.x
static inline CGFloat YYTextCGAffineTransformGetTranslateX(CGAffineTransform transform) {
return transform.tx;
}
/// Get the transform's translate.y
static inline CGFloat YYTextCGAffineTransformGetTranslateY(CGAffineTransform transform) {
return transform.ty;
}
/**
If you have 3 pair of points transformed by a same CGAffineTransform:
p1 (transform->) q1
p2 (transform->) q2
p3 (transform->) q3
This method returns the original transform matrix from these 3 pair of points.
@see http://stackoverflow.com/questions/13291796/calculate-values-for-a-cgaffinetransform-from-three-points-in-each-of-two-uiview
*/
CGAffineTransform YYTextCGAffineTransformGetFromPoints(CGPoint before[3], CGPoint after[3]);
/// Get the transform which can converts a point from the coordinate system of a given view to another.
CGAffineTransform YYTextCGAffineTransformGetFromViews(UIView *from, UIView *to);
/// Create a skew transform.
static inline CGAffineTransform YYTextCGAffineTransformMakeSkew(CGFloat x, CGFloat y){
CGAffineTransform transform = CGAffineTransformIdentity;
transform.c = -x;
transform.b = y;
return transform;
}
/// Negates/inverts a UIEdgeInsets.
static inline UIEdgeInsets YYTextUIEdgeInsetsInvert(UIEdgeInsets insets) {
return UIEdgeInsetsMake(-insets.top, -insets.left, -insets.bottom, -insets.right);
}
/// Convert CALayer's gravity string to UIViewContentMode.
UIViewContentMode YYTextCAGravityToUIViewContentMode(NSString *gravity);
/// Convert UIViewContentMode to CALayer's gravity string.
NSString *YYTextUIViewContentModeToCAGravity(UIViewContentMode contentMode);
/**
Returns a rectangle to fit the @param rect with specified content mode.
@param rect The constrant rect
@param size The content size
@param mode The content mode
@return A rectangle for the given content mode.
@discussion UIViewContentModeRedraw is same as UIViewContentModeScaleToFill.
*/
CGRect YYTextCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode);
/// Returns the center for the rectangle.
static inline CGPoint YYTextCGRectGetCenter(CGRect rect) {
return CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
}
/// Returns the area of the rectangle.
static inline CGFloat YYTextCGRectGetArea(CGRect rect) {
if (CGRectIsNull(rect)) return 0;
rect = CGRectStandardize(rect);
return rect.size.width * rect.size.height;
}
/// Returns the distance between two points.
static inline CGFloat YYTextCGPointGetDistanceToPoint(CGPoint p1, CGPoint p2) {
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
/// Returns the minmium distance between a point to a rectangle.
static inline CGFloat YYTextCGPointGetDistanceToRect(CGPoint p, CGRect r) {
r = CGRectStandardize(r);
if (CGRectContainsPoint(r, p)) return 0;
CGFloat distV, distH;
if (CGRectGetMinY(r) <= p.y && p.y <= CGRectGetMaxY(r)) {
distV = 0;
} else {
distV = p.y < CGRectGetMinY(r) ? CGRectGetMinY(r) - p.y : p.y - CGRectGetMaxY(r);
}
if (CGRectGetMinX(r) <= p.x && p.x <= CGRectGetMaxX(r)) {
distH = 0;
} else {
distH = p.x < CGRectGetMinX(r) ? CGRectGetMinX(r) - p.x : p.x - CGRectGetMaxX(r);
}
return MAX(distV, distH);
}
/// Get main screen's scale.
CGFloat YYTextScreenScale();
/// Get main screen's size. Height is always larger than width.
CGSize YYTextScreenSize();
/// Convert point to pixel.
static inline CGFloat YYTextCGFloatToPixel(CGFloat value) {
return value * YYTextScreenScale();
}
/// Convert pixel to point.
static inline CGFloat YYTextCGFloatFromPixel(CGFloat value) {
return value / YYTextScreenScale();
}
/// floor point value for pixel-aligned
static inline CGFloat YYTextCGFloatPixelFloor(CGFloat value) {
CGFloat scale = YYTextScreenScale();
return floor(value * scale) / scale;
}
/// round point value for pixel-aligned
static inline CGFloat YYTextCGFloatPixelRound(CGFloat value) {
CGFloat scale = YYTextScreenScale();
return round(value * scale) / scale;
}
/// ceil point value for pixel-aligned
static inline CGFloat YYTextCGFloatPixelCeil(CGFloat value) {
CGFloat scale = YYTextScreenScale();
return ceil(value * scale) / scale;
}
/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned)
static inline CGFloat YYTextCGFloatPixelHalf(CGFloat value) {
CGFloat scale = YYTextScreenScale();
return (floor(value * scale) + 0.5) / scale;
}
/// floor point value for pixel-aligned
static inline CGPoint YYTextCGPointPixelFloor(CGPoint point) {
CGFloat scale = YYTextScreenScale();
return CGPointMake(floor(point.x * scale) / scale,
floor(point.y * scale) / scale);
}
/// round point value for pixel-aligned
static inline CGPoint YYTextCGPointPixelRound(CGPoint point) {
CGFloat scale = YYTextScreenScale();
return CGPointMake(round(point.x * scale) / scale,
round(point.y * scale) / scale);
}
/// ceil point value for pixel-aligned
static inline CGPoint YYTextCGPointPixelCeil(CGPoint point) {
CGFloat scale = YYTextScreenScale();
return CGPointMake(ceil(point.x * scale) / scale,
ceil(point.y * scale) / scale);
}
/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned)
static inline CGPoint YYTextCGPointPixelHalf(CGPoint point) {
CGFloat scale = YYTextScreenScale();
return CGPointMake((floor(point.x * scale) + 0.5) / scale,
(floor(point.y * scale) + 0.5) / scale);
}
/// floor point value for pixel-aligned
static inline CGSize YYTextCGSizePixelFloor(CGSize size) {
CGFloat scale = YYTextScreenScale();
return CGSizeMake(floor(size.width * scale) / scale,
floor(size.height * scale) / scale);
}
/// round point value for pixel-aligned
static inline CGSize YYTextCGSizePixelRound(CGSize size) {
CGFloat scale = YYTextScreenScale();
return CGSizeMake(round(size.width * scale) / scale,
round(size.height * scale) / scale);
}
/// ceil point value for pixel-aligned
static inline CGSize YYTextCGSizePixelCeil(CGSize size) {
CGFloat scale = YYTextScreenScale();
return CGSizeMake(ceil(size.width * scale) / scale,
ceil(size.height * scale) / scale);
}
/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned)
static inline CGSize YYTextCGSizePixelHalf(CGSize size) {
CGFloat scale = YYTextScreenScale();
return CGSizeMake((floor(size.width * scale) + 0.5) / scale,
(floor(size.height * scale) + 0.5) / scale);
}
/// floor point value for pixel-aligned
static inline CGRect YYTextCGRectPixelFloor(CGRect rect) {
CGPoint origin = YYTextCGPointPixelCeil(rect.origin);
CGPoint corner = YYTextCGPointPixelFloor(CGPointMake(rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height));
CGRect ret = CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y);
if (ret.size.width < 0) ret.size.width = 0;
if (ret.size.height < 0) ret.size.height = 0;
return ret;
}
/// round point value for pixel-aligned
static inline CGRect YYTextCGRectPixelRound(CGRect rect) {
CGPoint origin = YYTextCGPointPixelRound(rect.origin);
CGPoint corner = YYTextCGPointPixelRound(CGPointMake(rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height));
return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y);
}
/// ceil point value for pixel-aligned
static inline CGRect YYTextCGRectPixelCeil(CGRect rect) {
CGPoint origin = YYTextCGPointPixelFloor(rect.origin);
CGPoint corner = YYTextCGPointPixelCeil(CGPointMake(rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height));
return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y);
}
/// round point value to .5 pixel for path stroke (odd pixel line width pixel-aligned)
static inline CGRect YYTextCGRectPixelHalf(CGRect rect) {
CGPoint origin = YYTextCGPointPixelHalf(rect.origin);
CGPoint corner = YYTextCGPointPixelHalf(CGPointMake(rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height));
return CGRectMake(origin.x, origin.y, corner.x - origin.x, corner.y - origin.y);
}
/// floor UIEdgeInset for pixel-aligned
static inline UIEdgeInsets YYTextUIEdgeInsetPixelFloor(UIEdgeInsets insets) {
insets.top = YYTextCGFloatPixelFloor(insets.top);
insets.left = YYTextCGFloatPixelFloor(insets.left);
insets.bottom = YYTextCGFloatPixelFloor(insets.bottom);
insets.right = YYTextCGFloatPixelFloor(insets.right);
return insets;
}
/// ceil UIEdgeInset for pixel-aligned
static inline UIEdgeInsets YYTextUIEdgeInsetPixelCeil(UIEdgeInsets insets) {
insets.top = YYTextCGFloatPixelCeil(insets.top);
insets.left = YYTextCGFloatPixelCeil(insets.left);
insets.bottom = YYTextCGFloatPixelCeil(insets.bottom);
insets.right = YYTextCGFloatPixelCeil(insets.right);
return insets;
}
static inline UIFont * _Nullable YYTextFontWithBold(UIFont *font) {
if (![font respondsToSelector:@selector(fontDescriptor)]) return font;
return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold] size:font.pointSize];
}
static inline UIFont * _Nullable YYTextFontWithItalic(UIFont *font) {
if (![font respondsToSelector:@selector(fontDescriptor)]) return font;
return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic] size:font.pointSize];
}
static inline UIFont * _Nullable YYTextFontWithBoldItalic(UIFont *font) {
if (![font respondsToSelector:@selector(fontDescriptor)]) return font;
return [UIFont fontWithDescriptor:[font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic] size:font.pointSize];
}
/**
Convert CFRange to NSRange
@param range CFRange @return NSRange
*/
static inline NSRange YYTextNSRangeFromCFRange(CFRange range) {
return NSMakeRange(range.location, range.length);
}
/**
Convert NSRange to CFRange
@param range NSRange @return CFRange
*/
static inline CFRange YYTextCFRangeFromNSRange(NSRange range) {
return CFRangeMake(range.location, range.length);
}
/// Returns YES in App Extension.
BOOL YYTextIsAppExtension();
/// Returns nil in App Extension.
UIApplication * _Nullable YYTextSharedApplication();
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,309 @@
//
// YYTextUtilities.m
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 15/4/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 "YYTextUtilities.h"
#import <Accelerate/Accelerate.h>
#import "UIView+YYText.h"
NSCharacterSet *YYTextVerticalFormRotateCharacterSet() {
static NSMutableCharacterSet *set;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSMutableCharacterSet new];
[set addCharactersInRange:NSMakeRange(0x1100, 256)]; // Hangul Jamo
[set addCharactersInRange:NSMakeRange(0x2460, 160)]; // Enclosed Alphanumerics
[set addCharactersInRange:NSMakeRange(0x2600, 256)]; // Miscellaneous Symbols
[set addCharactersInRange:NSMakeRange(0x2700, 192)]; // Dingbats
[set addCharactersInRange:NSMakeRange(0x2E80, 128)]; // CJK Radicals Supplement
[set addCharactersInRange:NSMakeRange(0x2F00, 224)]; // Kangxi Radicals
[set addCharactersInRange:NSMakeRange(0x2FF0, 16)]; // Ideographic Description Characters
[set addCharactersInRange:NSMakeRange(0x3000, 64)]; // CJK Symbols and Punctuation
[set removeCharactersInRange:NSMakeRange(0x3008, 10)];
[set removeCharactersInRange:NSMakeRange(0x3014, 12)];
[set addCharactersInRange:NSMakeRange(0x3040, 96)]; // Hiragana
[set addCharactersInRange:NSMakeRange(0x30A0, 96)]; // Katakana
[set addCharactersInRange:NSMakeRange(0x3100, 48)]; // Bopomofo
[set addCharactersInRange:NSMakeRange(0x3130, 96)]; // Hangul Compatibility Jamo
[set addCharactersInRange:NSMakeRange(0x3190, 16)]; // Kanbun
[set addCharactersInRange:NSMakeRange(0x31A0, 32)]; // Bopomofo Extended
[set addCharactersInRange:NSMakeRange(0x31C0, 48)]; // CJK Strokes
[set addCharactersInRange:NSMakeRange(0x31F0, 16)]; // Katakana Phonetic Extensions
[set addCharactersInRange:NSMakeRange(0x3200, 256)]; // Enclosed CJK Letters and Months
[set addCharactersInRange:NSMakeRange(0x3300, 256)]; // CJK Compatibility
[set addCharactersInRange:NSMakeRange(0x3400, 2582)]; // CJK Unified Ideographs Extension A
[set addCharactersInRange:NSMakeRange(0x4E00, 20941)]; // CJK Unified Ideographs
[set addCharactersInRange:NSMakeRange(0xAC00, 11172)]; // Hangul Syllables
[set addCharactersInRange:NSMakeRange(0xD7B0, 80)]; // Hangul Jamo Extended-B
[set addCharactersInString:@""]; // U+F8FF (Private Use Area)
[set addCharactersInRange:NSMakeRange(0xF900, 512)]; // CJK Compatibility Ideographs
[set addCharactersInRange:NSMakeRange(0xFE10, 16)]; // Vertical Forms
[set addCharactersInRange:NSMakeRange(0xFF00, 240)]; // Halfwidth and Fullwidth Forms
[set addCharactersInRange:NSMakeRange(0x1F200, 256)]; // Enclosed Ideographic Supplement
[set addCharactersInRange:NSMakeRange(0x1F300, 768)]; // Enclosed Ideographic Supplement
[set addCharactersInRange:NSMakeRange(0x1F600, 80)]; // Emoticons (Emoji)
[set addCharactersInRange:NSMakeRange(0x1F680, 128)]; // Transport and Map Symbols
// See http://unicode-table.com/ for more information.
});
return set;
}
NSCharacterSet *YYTextVerticalFormRotateAndMoveCharacterSet() {
static NSMutableCharacterSet *set;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
set = [NSMutableCharacterSet new];
[set addCharactersInString:@",。、."];
});
return set;
}
// return 0 when succeed
static int matrix_invert(__CLPK_integer N, double *matrix) {
__CLPK_integer error = 0;
__CLPK_integer pivot_tmp[6 * 6];
__CLPK_integer *pivot = pivot_tmp;
double workspace_tmp[6 * 6];
double *workspace = workspace_tmp;
bool need_free = false;
if (N > 6) {
need_free = true;
pivot = malloc(N * N * sizeof(__CLPK_integer));
if (!pivot) return -1;
workspace = malloc(N * sizeof(double));
if (!workspace) {
free(pivot);
return -1;
}
}
dgetrf_(&N, &N, matrix, &N, pivot, &error);
if (error == 0) {
dgetri_(&N, matrix, &N, pivot, workspace, &N, &error);
}
if (need_free) {
free(pivot);
free(workspace);
}
return error;
}
CGAffineTransform YYTextCGAffineTransformGetFromPoints(CGPoint before[3], CGPoint after[3]) {
if (before == NULL || after == NULL) return CGAffineTransformIdentity;
CGPoint p1, p2, p3, q1, q2, q3;
p1 = before[0]; p2 = before[1]; p3 = before[2];
q1 = after[0]; q2 = after[1]; q3 = after[2];
double A[36];
A[ 0] = p1.x; A[ 1] = p1.y; A[ 2] = 0; A[ 3] = 0; A[ 4] = 1; A[ 5] = 0;
A[ 6] = 0; A[ 7] = 0; A[ 8] = p1.x; A[ 9] = p1.y; A[10] = 0; A[11] = 1;
A[12] = p2.x; A[13] = p2.y; A[14] = 0; A[15] = 0; A[16] = 1; A[17] = 0;
A[18] = 0; A[19] = 0; A[20] = p2.x; A[21] = p2.y; A[22] = 0; A[23] = 1;
A[24] = p3.x; A[25] = p3.y; A[26] = 0; A[27] = 0; A[28] = 1; A[29] = 0;
A[30] = 0; A[31] = 0; A[32] = p3.x; A[33] = p3.y; A[34] = 0; A[35] = 1;
int error = matrix_invert(6, A);
if (error) return CGAffineTransformIdentity;
double B[6];
B[0] = q1.x; B[1] = q1.y; B[2] = q2.x; B[3] = q2.y; B[4] = q3.x; B[5] = q3.y;
double M[6];
M[0] = A[ 0] * B[0] + A[ 1] * B[1] + A[ 2] * B[2] + A[ 3] * B[3] + A[ 4] * B[4] + A[ 5] * B[5];
M[1] = A[ 6] * B[0] + A[ 7] * B[1] + A[ 8] * B[2] + A[ 9] * B[3] + A[10] * B[4] + A[11] * B[5];
M[2] = A[12] * B[0] + A[13] * B[1] + A[14] * B[2] + A[15] * B[3] + A[16] * B[4] + A[17] * B[5];
M[3] = A[18] * B[0] + A[19] * B[1] + A[20] * B[2] + A[21] * B[3] + A[22] * B[4] + A[23] * B[5];
M[4] = A[24] * B[0] + A[25] * B[1] + A[26] * B[2] + A[27] * B[3] + A[28] * B[4] + A[29] * B[5];
M[5] = A[30] * B[0] + A[31] * B[1] + A[32] * B[2] + A[33] * B[3] + A[34] * B[4] + A[35] * B[5];
CGAffineTransform transform = CGAffineTransformMake(M[0], M[2], M[1], M[3], M[4], M[5]);
return transform;
}
CGAffineTransform YYTextCGAffineTransformGetFromViews(UIView *from, UIView *to) {
if (!from || !to) return CGAffineTransformIdentity;
CGPoint before[3], after[3];
before[0] = CGPointMake(0, 0);
before[1] = CGPointMake(0, 1);
before[2] = CGPointMake(1, 0);
after[0] = [from yy_convertPoint:before[0] toViewOrWindow:to];
after[1] = [from yy_convertPoint:before[1] toViewOrWindow:to];
after[2] = [from yy_convertPoint:before[2] toViewOrWindow:to];
return YYTextCGAffineTransformGetFromPoints(before, after);
}
UIViewContentMode YYTextCAGravityToUIViewContentMode(NSString *gravity) {
static NSDictionary *dic;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dic = @{ kCAGravityCenter:@(UIViewContentModeCenter),
kCAGravityTop:@(UIViewContentModeTop),
kCAGravityBottom:@(UIViewContentModeBottom),
kCAGravityLeft:@(UIViewContentModeLeft),
kCAGravityRight:@(UIViewContentModeRight),
kCAGravityTopLeft:@(UIViewContentModeTopLeft),
kCAGravityTopRight:@(UIViewContentModeTopRight),
kCAGravityBottomLeft:@(UIViewContentModeBottomLeft),
kCAGravityBottomRight:@(UIViewContentModeBottomRight),
kCAGravityResize:@(UIViewContentModeScaleToFill),
kCAGravityResizeAspect:@(UIViewContentModeScaleAspectFit),
kCAGravityResizeAspectFill:@(UIViewContentModeScaleAspectFill) };
});
if (!gravity) return UIViewContentModeScaleToFill;
return (UIViewContentMode)((NSNumber *)dic[gravity]).integerValue;
}
NSString *YYTextUIViewContentModeToCAGravity(UIViewContentMode contentMode) {
switch (contentMode) {
case UIViewContentModeScaleToFill: return kCAGravityResize;
case UIViewContentModeScaleAspectFit: return kCAGravityResizeAspect;
case UIViewContentModeScaleAspectFill: return kCAGravityResizeAspectFill;
case UIViewContentModeRedraw: return kCAGravityResize;
case UIViewContentModeCenter: return kCAGravityCenter;
case UIViewContentModeTop: return kCAGravityTop;
case UIViewContentModeBottom: return kCAGravityBottom;
case UIViewContentModeLeft: return kCAGravityLeft;
case UIViewContentModeRight: return kCAGravityRight;
case UIViewContentModeTopLeft: return kCAGravityTopLeft;
case UIViewContentModeTopRight: return kCAGravityTopRight;
case UIViewContentModeBottomLeft: return kCAGravityBottomLeft;
case UIViewContentModeBottomRight: return kCAGravityBottomRight;
default: return kCAGravityResize;
}
}
CGRect YYTextCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode) {
rect = CGRectStandardize(rect);
size.width = size.width < 0 ? -size.width : size.width;
size.height = size.height < 0 ? -size.height : size.height;
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
switch (mode) {
case UIViewContentModeScaleAspectFit:
case UIViewContentModeScaleAspectFill: {
if (rect.size.width < 0.01 || rect.size.height < 0.01 ||
size.width < 0.01 || size.height < 0.01) {
rect.origin = center;
rect.size = CGSizeZero;
} else {
CGFloat scale;
if (mode == UIViewContentModeScaleAspectFit) {
if (size.width / size.height < rect.size.width / rect.size.height) {
scale = rect.size.height / size.height;
} else {
scale = rect.size.width / size.width;
}
} else {
if (size.width / size.height < rect.size.width / rect.size.height) {
scale = rect.size.width / size.width;
} else {
scale = rect.size.height / size.height;
}
}
size.width *= scale;
size.height *= scale;
rect.size = size;
rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5);
}
} break;
case UIViewContentModeCenter: {
rect.size = size;
rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5);
} break;
case UIViewContentModeTop: {
rect.origin.x = center.x - size.width * 0.5;
rect.size = size;
} break;
case UIViewContentModeBottom: {
rect.origin.x = center.x - size.width * 0.5;
rect.origin.y += rect.size.height - size.height;
rect.size = size;
} break;
case UIViewContentModeLeft: {
rect.origin.y = center.y - size.height * 0.5;
rect.size = size;
} break;
case UIViewContentModeRight: {
rect.origin.y = center.y - size.height * 0.5;
rect.origin.x += rect.size.width - size.width;
rect.size = size;
} break;
case UIViewContentModeTopLeft: {
rect.size = size;
} break;
case UIViewContentModeTopRight: {
rect.origin.x += rect.size.width - size.width;
rect.size = size;
} break;
case UIViewContentModeBottomLeft: {
rect.origin.y += rect.size.height - size.height;
rect.size = size;
} break;
case UIViewContentModeBottomRight: {
rect.origin.x += rect.size.width - size.width;
rect.origin.y += rect.size.height - size.height;
rect.size = size;
} break;
case UIViewContentModeScaleToFill:
case UIViewContentModeRedraw:
default: {
rect = rect;
}
}
return rect;
}
CGFloat YYTextScreenScale() {
static CGFloat scale;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
scale = [UIScreen mainScreen].scale;
});
return scale;
}
CGSize YYTextScreenSize() {
static CGSize size;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
size = [UIScreen mainScreen].bounds.size;
if (size.height < size.width) {
CGFloat tmp = size.height;
size.height = size.width;
size.width = tmp;
}
});
return size;
}
BOOL YYTextIsAppExtension() {
static BOOL isAppExtension = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"UIApplication");
if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES;
if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES;
});
return isAppExtension;
}
UIApplication *YYTextSharedApplication() {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
return YYTextIsAppExtension() ? nil : [UIApplication performSelector:@selector(sharedApplication)];
#pragma clang diagnostic pop
}

View File

@ -0,0 +1,61 @@
//
// YYTextWeakProxy.h
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 14/10/18.
// 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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
A proxy used to hold a weak object.
It can be used to avoid retain cycles, such as the target in NSTimer or CADisplayLink.
sample code:
@implementation MyView {
NSTimer *_timer;
}
- (void)initTimer {
YYTextWeakProxy *proxy = [YYTextWeakProxy proxyWithTarget:self];
_timer = [NSTimer timerWithTimeInterval:0.1 target:proxy selector:@selector(tick:) userInfo:nil repeats:YES];
}
- (void)tick:(NSTimer *)timer {...}
@end
*/
@interface YYTextWeakProxy : NSProxy
/**
The proxy target.
*/
@property (nullable, nonatomic, weak, readonly) id target;
/**
Creates a new weak proxy for target.
@param target Target object.
@return A new proxy object.
*/
- (instancetype)initWithTarget:(id)target;
/**
Creates a new weak proxy for target.
@param target Target object.
@return A new proxy object.
*/
+ (instancetype)proxyWithTarget:(id)target;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,83 @@
//
// YYTextWeakProxy.m
// YYText <https://github.com/ibireme/YYText>
//
// Created by ibireme on 14/10/18.
// 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 "YYTextWeakProxy.h"
@implementation YYTextWeakProxy
- (instancetype)initWithTarget:(id)target {
_target = target;
return self;
}
+ (instancetype)proxyWithTarget:(id)target {
return [[YYTextWeakProxy alloc] initWithTarget:target];
}
- (id)forwardingTargetForSelector:(SEL)selector {
return _target;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
void *null = NULL;
[invocation setReturnValue:&null];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_target respondsToSelector:aSelector];
}
- (BOOL)isEqual:(id)object {
return [_target isEqual:object];
}
- (NSUInteger)hash {
return [_target hash];
}
- (Class)superclass {
return [_target superclass];
}
- (Class)class {
return [_target class];
}
- (BOOL)isKindOfClass:(Class)aClass {
return [_target isKindOfClass:aClass];
}
- (BOOL)isMemberOfClass:(Class)aClass {
return [_target isMemberOfClass:aClass];
}
- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
return [_target conformsToProtocol:aProtocol];
}
- (BOOL)isProxy {
return YES;
}
- (NSString *)description {
return [_target description];
}
- (NSString *)debugDescription {
return [_target debugDescription];
}
@end

380
ProductApp/Pods/YYText/YYText/YYLabel.h generated Normal file
View File

@ -0,0 +1,380 @@
//
// YYLabel.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/YYTextParser.h>
#import <YYText/YYTextLayout.h>
#import <YYText/YYTextAttribute.h>
#else
#import "YYTextParser.h"
#import "YYTextLayout.h"
#import "YYTextAttribute.h"
#endif
NS_ASSUME_NONNULL_BEGIN
#if !TARGET_INTERFACE_BUILDER
/**
The YYLabel class implements a read-only text view.
@discussion The API and behavior is similar to UILabel, but provides more features:
* It supports asynchronous layout and rendering (to avoid blocking UI thread).
* It extends the CoreText attributes to support more text effects.
* It allows to add UIImage, UIView and CALayer as text attachments.
* It allows to add 'highlight' link to some range of text to allow user interact with.
* It allows to add container path and exclusion paths to control text container's shape.
* It supports vertical form layout to display CJK text.
See NSAttributedString+YYText.h for more convenience methods to set the attributes.
See YYTextAttribute.h and YYTextLayout.h for more information.
*/
@interface YYLabel : UIView <NSCoding>
#pragma mark - Accessing the Text Attributes
///=============================================================================
/// @name Accessing the Text Attributes
///=============================================================================
/**
The text displayed by the label. Default is nil.
Set a new value to this property also replaces the text in `attributedText`.
Get the value returns the plain text in `attributedText`.
*/
@property (nullable, nonatomic, copy) NSString *text;
/**
The font of the text. Default is 17-point system font.
Set a new value to this property also causes the new font to be applied to the entire `attributedText`.
Get the value returns the font at the head of `attributedText`.
*/
@property (null_resettable, nonatomic, strong) UIFont *font;
/**
The color of the text. Default is black.
Set a new value to this property also causes the new color to be applied to the entire `attributedText`.
Get the value returns the color at the head of `attributedText`.
*/
@property (null_resettable, nonatomic, strong) UIColor *textColor;
/**
The shadow color of the text. Default is nil.
Set a new value to this property also causes the shadow color to be applied to the entire `attributedText`.
Get the value returns the shadow color at the head of `attributedText`.
*/
@property (nullable, nonatomic, strong) UIColor *shadowColor;
/**
The shadow offset of the text. Default is CGSizeZero.
Set a new value to this property also causes the shadow offset to be applied to the entire `attributedText`.
Get the value returns the shadow offset at the head of `attributedText`.
*/
@property (nonatomic) CGSize shadowOffset;
/**
The shadow blur of the text. Default is 0.
Set a new value to this property also causes the shadow blur to be applied to the entire `attributedText`.
Get the value returns the shadow blur at the head of `attributedText`.
*/
@property (nonatomic) CGFloat shadowBlurRadius;
/**
The technique to use for aligning the text. Default is NSTextAlignmentNatural.
Set a new value to this property also causes the new alignment to be applied to the entire `attributedText`.
Get the value returns the alignment at the head of `attributedText`.
*/
@property (nonatomic) NSTextAlignment textAlignment;
/**
The text vertical aligmnent in container. Default is YYTextVerticalAlignmentCenter.
*/
@property (nonatomic) YYTextVerticalAlignment textVerticalAlignment;
/**
The styled text displayed by the label.
Set a new value to this property also replaces the value of the `text`, `font`, `textColor`,
`textAlignment` and other properties in label.
@discussion It only support the attributes declared in CoreText and YYTextAttribute.
See `NSAttributedString+YYText` for more convenience methods to set the attributes.
*/
@property (nullable, nonatomic, copy) NSAttributedString *attributedText;
/**
The technique to use for wrapping and truncating the label's text.
Default is NSLineBreakByTruncatingTail.
*/
@property (nonatomic) NSLineBreakMode lineBreakMode;
/**
The truncation token string used when text is truncated. Default is nil.
When the value is nil, the label use "" as default truncation token.
*/
@property (nullable, nonatomic, copy) NSAttributedString *truncationToken;
/**
The maximum number of lines to use for rendering text. Default value is 1.
0 means no limit.
*/
@property (nonatomic) NSUInteger numberOfLines;
/**
When `text` or `attributedText` is changed, the parser will be called to modify the text.
It can be used to add code highlighting or emoticon replacement to text view.
The default value is nil.
See `YYTextParser` protocol for more information.
*/
@property (nullable, nonatomic, strong) id<YYTextParser> textParser;
/**
The current text layout in text view. It can be used to query the text layout information.
Set a new value to this property also replaces most properties in this label, such as `text`,
`color`, `attributedText`, `lineBreakMode`, `textContainerPath`, `exclusionPaths` and so on.
*/
@property (nullable, nonatomic, strong) YYTextLayout *textLayout;
#pragma mark - Configuring the Text Container
///=============================================================================
/// @name Configuring the Text Container
///=============================================================================
/**
A UIBezierPath object that specifies the shape of the text frame. Default value is nil.
*/
@property (nullable, nonatomic, copy) UIBezierPath *textContainerPath;
/**
An array of UIBezierPath objects representing the exclusion paths inside the
receiver's bounding rectangle. Default value is nil.
*/
@property (nullable, nonatomic, copy) NSArray<UIBezierPath *> *exclusionPaths;
/**
The inset of the text container's layout area within the text view's content area.
Default value is UIEdgeInsetsZero.
*/
@property (nonatomic) UIEdgeInsets textContainerInset;
/**
Whether the receiver's layout orientation is vertical form. Default is NO.
It may used to display CJK text.
*/
@property (nonatomic, getter=isVerticalForm) BOOL verticalForm;
/**
The text line position modifier used to modify the lines' position in layout.
Default value is nil.
See `YYTextLinePositionModifier` protocol for more information.
*/
@property (nullable, nonatomic, copy) id<YYTextLinePositionModifier> linePositionModifier;
/**
The debug option to display CoreText layout result.
The default value is [YYTextDebugOption sharedDebugOption].
*/
@property (nullable, nonatomic, copy) YYTextDebugOption *debugOption;
#pragma mark - Getting the Layout Constraints
///=============================================================================
/// @name Getting the Layout Constraints
///=============================================================================
/**
The preferred maximum width (in points) for a multiline label.
@discussion This property affects the size of the label when layout constraints
are applied to it. During layout, if the text extends beyond the width
specified by this property, the additional text is flowed to one or more new
lines, thereby increasing the height of the label. If the text is vertical
form, this value will match to text height.
*/
@property (nonatomic) CGFloat preferredMaxLayoutWidth;
#pragma mark - Interacting with Text Data
///=============================================================================
/// @name Interacting with Text Data
///=============================================================================
/**
When user tap the label, this action will be called (similar to tap gesture).
The default value is nil.
*/
@property (nullable, nonatomic, copy) YYTextAction textTapAction;
/**
When user long press the label, this action will be called (similar to long press gesture).
The default value is nil.
*/
@property (nullable, nonatomic, copy) YYTextAction textLongPressAction;
/**
When user tap the highlight range of text, this action will be called.
The default value is nil.
*/
@property (nullable, nonatomic, copy) YYTextAction highlightTapAction;
/**
When user long press the highlight range of text, this action will be called.
The default value is nil.
*/
@property (nullable, nonatomic, copy) YYTextAction highlightLongPressAction;
#pragma mark - Configuring the Display Mode
///=============================================================================
/// @name Configuring the Display Mode
///=============================================================================
/**
A Boolean value indicating whether the layout and rendering codes are running
asynchronously on background threads.
The default value is `NO`.
*/
@property (nonatomic) BOOL displaysAsynchronously;
/**
If the value is YES, and the layer is rendered asynchronously, then it will
set label.layer.contents to nil before display.
The default value is `YES`.
@discussion When the asynchronously display is enabled, the layer's content will
be updated after the background render process finished. If the render process
can not finished in a vsync time (1/60 second), the old content will be still kept
for display. You may manually clear the content by set the layer.contents to nil
after you update the label's properties, or you can just set this property to YES.
*/
@property (nonatomic) BOOL clearContentsBeforeAsynchronouslyDisplay;
/**
If the value is YES, and the layer is rendered asynchronously, then it will add
a fade animation on layer when the contents of layer changed.
The default value is `YES`.
*/
@property (nonatomic) BOOL fadeOnAsynchronouslyDisplay;
/**
If the value is YES, then it will add a fade animation on layer when some range
of text become highlighted.
The default value is `YES`.
*/
@property (nonatomic) BOOL fadeOnHighlight;
/**
Ignore common properties (such as text, font, textColor, attributedText...) and
only use "textLayout" to display content.
The default value is `NO`.
@discussion If you control the label content only through "textLayout", then
you may set this value to YES for higher performance.
*/
@property (nonatomic) BOOL ignoreCommonProperties;
/*
Tips:
1. If you only need a UILabel alternative to display rich text and receive link touch event,
you do not need to adjust the display mode properties.
2. If you have performance issues, you may enable the asynchronous display mode
by setting the `displaysAsynchronously` to YES.
3. If you want to get the highest performance, you should do text layout with
`YYTextLayout` class in background thread. Here's an example:
YYLabel *label = [YYLabel new];
label.displaysAsynchronously = YES;
label.ignoreCommonProperties = YES;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Create attributed string.
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Some Text"];
text.yy_font = [UIFont systemFontOfSize:16];
text.yy_color = [UIColor grayColor];
[text yy_setColor:[UIColor redColor] range:NSMakeRange(0, 4)];
// Create text container
YYTextContainer *container = [YYTextContainer new];
container.size = CGSizeMake(100, CGFLOAT_MAX);
container.maximumNumberOfRows = 0;
// Generate a text layout.
YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:text];
dispatch_async(dispatch_get_main_queue(), ^{
label.size = layout.textBoundingSize;
label.textLayout = layout;
});
});
*/
@end
#else // TARGET_INTERFACE_BUILDER
IB_DESIGNABLE
@interface YYLabel : UIView <NSCoding>
@property (nullable, nonatomic, copy) IBInspectable NSString *text;
@property (null_resettable, nonatomic, strong) IBInspectable UIColor *textColor;
@property (nullable, nonatomic, strong) IBInspectable NSString *fontName_;
@property (nonatomic) IBInspectable CGFloat fontSize_;
@property (nonatomic) IBInspectable BOOL fontIsBold_;
@property (nonatomic) IBInspectable NSUInteger numberOfLines;
@property (nonatomic) IBInspectable NSInteger lineBreakMode;
@property (nonatomic) IBInspectable CGFloat preferredMaxLayoutWidth;
@property (nonatomic, getter=isVerticalForm) IBInspectable BOOL verticalForm;
@property (nonatomic) IBInspectable NSInteger textAlignment;
@property (nonatomic) IBInspectable NSInteger textVerticalAlignment;
@property (nullable, nonatomic, strong) IBInspectable UIColor *shadowColor;
@property (nonatomic) IBInspectable CGPoint shadowOffset;
@property (nonatomic) IBInspectable CGFloat shadowBlurRadius;
@property (nullable, nonatomic, copy) IBInspectable NSAttributedString *attributedText;
@property (nonatomic) IBInspectable CGFloat insetTop_;
@property (nonatomic) IBInspectable CGFloat insetBottom_;
@property (nonatomic) IBInspectable CGFloat insetLeft_;
@property (nonatomic) IBInspectable CGFloat insetRight_;
@property (nonatomic) IBInspectable BOOL debugEnabled_;
@property (null_resettable, nonatomic, strong) UIFont *font;
@property (nullable, nonatomic, copy) NSAttributedString *truncationToken;
@property (nullable, nonatomic, strong) id<YYTextParser> textParser;
@property (nullable, nonatomic, strong) YYTextLayout *textLayout;
@property (nullable, nonatomic, copy) UIBezierPath *textContainerPath;
@property (nullable, nonatomic, copy) NSArray<UIBezierPath*> *exclusionPaths;
@property (nonatomic) UIEdgeInsets textContainerInset;
@property (nullable, nonatomic, copy) id<YYTextLinePositionModifier> linePositionModifier;
@property (nonnull, nonatomic, copy) YYTextDebugOption *debugOption;
@property (nullable, nonatomic, copy) YYTextAction textTapAction;
@property (nullable, nonatomic, copy) YYTextAction textLongPressAction;
@property (nullable, nonatomic, copy) YYTextAction highlightTapAction;
@property (nullable, nonatomic, copy) YYTextAction highlightLongPressAction;
@property (nonatomic) BOOL displaysAsynchronously;
@property (nonatomic) BOOL clearContentsBeforeAsynchronouslyDisplay;
@property (nonatomic) BOOL fadeOnAsynchronouslyDisplay;
@property (nonatomic) BOOL fadeOnHighlight;
@property (nonatomic) BOOL ignoreCommonProperties;
@end
#endif // !TARGET_INTERFACE_BUILDER
NS_ASSUME_NONNULL_END

1306
ProductApp/Pods/YYText/YYText/YYLabel.m generated Normal file

File diff suppressed because it is too large Load Diff

50
ProductApp/Pods/YYText/YYText/YYText.h generated Normal file
View File

@ -0,0 +1,50 @@
//
// YYText.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>)
FOUNDATION_EXPORT double YYTextVersionNumber;
FOUNDATION_EXPORT const unsigned char YYTextVersionString[];
#import <YYText/YYLabel.h>
#import <YYText/YYTextView.h>
#import <YYText/YYTextAttribute.h>
#import <YYText/YYTextArchiver.h>
#import <YYText/YYTextParser.h>
#import <YYText/YYTextRunDelegate.h>
#import <YYText/YYTextRubyAnnotation.h>
#import <YYText/YYTextLayout.h>
#import <YYText/YYTextLine.h>
#import <YYText/YYTextInput.h>
#import <YYText/YYTextDebugOption.h>
#import <YYText/YYTextKeyboardManager.h>
#import <YYText/YYTextUtilities.h>
#import <YYText/NSAttributedString+YYText.h>
#import <YYText/NSParagraphStyle+YYText.h>
#import <YYText/UIPasteboard+YYText.h>
#else
#import "YYLabel.h"
#import "YYTextView.h"
#import "YYTextAttribute.h"
#import "YYTextArchiver.h"
#import "YYTextParser.h"
#import "YYTextRunDelegate.h"
#import "YYTextRubyAnnotation.h"
#import "YYTextLayout.h"
#import "YYTextLine.h"
#import "YYTextInput.h"
#import "YYTextDebugOption.h"
#import "YYTextKeyboardManager.h"
#import "YYTextUtilities.h"
#import "NSAttributedString+YYText.h"
#import "NSParagraphStyle+YYText.h"
#import "UIPasteboard+YYText.h"
#endif

410
ProductApp/Pods/YYText/YYText/YYTextView.h generated Normal file
View File

@ -0,0 +1,410 @@
//
// YYTextView.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/YYTextParser.h>
#import <YYText/YYTextLayout.h>
#import <YYText/YYTextAttribute.h>
#else
#import "YYTextParser.h"
#import "YYTextLayout.h"
#import "YYTextAttribute.h"
#endif
@class YYTextView;
NS_ASSUME_NONNULL_BEGIN
/**
The YYTextViewDelegate protocol defines a set of optional methods you can use
to receive editing-related messages for YYTextView objects.
@discussion The API and behavior is similar to UITextViewDelegate,
see UITextViewDelegate's documentation for more information.
*/
@protocol YYTextViewDelegate <NSObject, UIScrollViewDelegate>
@optional
- (BOOL)textViewShouldBeginEditing:(YYTextView *)textView;
- (BOOL)textViewShouldEndEditing:(YYTextView *)textView;
- (void)textViewDidBeginEditing:(YYTextView *)textView;
- (void)textViewDidEndEditing:(YYTextView *)textView;
- (BOOL)textView:(YYTextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
- (void)textViewDidChange:(YYTextView *)textView;
- (void)textViewDidChangeSelection:(YYTextView *)textView;
- (BOOL)textView:(YYTextView *)textView shouldTapHighlight:(YYTextHighlight *)highlight inRange:(NSRange)characterRange;
- (void)textView:(YYTextView *)textView didTapHighlight:(YYTextHighlight *)highlight inRange:(NSRange)characterRange rect:(CGRect)rect;
- (BOOL)textView:(YYTextView *)textView shouldLongPressHighlight:(YYTextHighlight *)highlight inRange:(NSRange)characterRange;
- (void)textView:(YYTextView *)textView didLongPressHighlight:(YYTextHighlight *)highlight inRange:(NSRange)characterRange rect:(CGRect)rect;
@end
#if !TARGET_INTERFACE_BUILDER
/**
The YYTextView class implements the behavior for a scrollable, multiline text region.
@discussion The API and behavior is similar to UITextView, but provides more features:
* It extends the CoreText attributes to support more text effects.
* It allows to add UIImage, UIView and CALayer as text attachments.
* It allows to add 'highlight' link to some range of text to allow user interact with.
* It allows to add exclusion paths to control text container's shape.
* It supports vertical form layout to display and edit CJK text.
* It allows user to copy/paste image and attributed text from/to text view.
* It allows to set an attributed text as placeholder.
See NSAttributedString+YYText.h for more convenience methods to set the attributes.
See YYTextAttribute.h and YYTextLayout.h for more information.
*/
@interface YYTextView : UIScrollView <UITextInput>
#pragma mark - Accessing the Delegate
///=============================================================================
/// @name Accessing the Delegate
///=============================================================================
@property (nullable, nonatomic, weak) id<YYTextViewDelegate> delegate;
#pragma mark - Configuring the Text Attributes
///=============================================================================
/// @name Configuring the Text Attributes
///=============================================================================
/**
The text displayed by the text view.
Set a new value to this property also replaces the text in `attributedText`.
Get the value returns the plain text in `attributedText`.
*/
@property (null_resettable, nonatomic, copy) NSString *text;
/**
The font of the text. Default is 12-point system font.
Set a new value to this property also causes the new font to be applied to the entire `attributedText`.
Get the value returns the font at the head of `attributedText`.
*/
@property (nullable, nonatomic, strong) UIFont *font;
/**
The color of the text. Default is black.
Set a new value to this property also causes the new color to be applied to the entire `attributedText`.
Get the value returns the color at the head of `attributedText`.
*/
@property (nullable, nonatomic, strong) UIColor *textColor;
/**
The technique to use for aligning the text. Default is NSTextAlignmentNatural.
Set a new value to this property also causes the new alignment to be applied to the entire `attributedText`.
Get the value returns the alignment at the head of `attributedText`.
*/
@property (nonatomic) NSTextAlignment textAlignment;
/**
The text vertical aligmnent in container. Default is YYTextVerticalAlignmentTop.
*/
@property (nonatomic) YYTextVerticalAlignment textVerticalAlignment;
/**
The types of data converted to clickable URLs in the text view. Default is UIDataDetectorTypeNone.
The tap or long press action should be handled by delegate.
*/
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes;
/**
The attributes to apply to links at normal state. Default is light blue color.
When a range of text is detected by the `dataDetectorTypes`, this value would be
used to modify the original attributes in the range.
*/
@property (nullable, nonatomic, copy) NSDictionary<NSString *, id> *linkTextAttributes;
/**
The attributes to apply to links at highlight state. Default is a gray border.
When a range of text is detected by the `dataDetectorTypes` and the range was touched by user,
this value would be used to modify the original attributes in the range.
*/
@property (nullable, nonatomic, copy) NSDictionary<NSString *, id> *highlightTextAttributes;
/**
The attributes to apply to new text being entered by the user.
When the text view's selection changes, this value is reset automatically.
*/
@property (nullable, nonatomic, copy) NSDictionary<NSString *, id> *typingAttributes;
/**
The styled text displayed by the text view.
Set a new value to this property also replaces the value of the `text`, `font`, `textColor`,
`textAlignment` and other properties in text view.
@discussion It only support the attributes declared in CoreText and YYTextAttribute.
See `NSAttributedString+YYText` for more convenience methods to set the attributes.
*/
@property (nullable, nonatomic, copy) NSAttributedString *attributedText;
/**
When `text` or `attributedText` is changed, the parser will be called to modify the text.
It can be used to add code highlighting or emoticon replacement to text view.
The default value is nil.
See `YYTextParser` protocol for more information.
*/
@property (nullable, nonatomic, strong) id<YYTextParser> textParser;
/**
The current text layout in text view (readonly).
It can be used to query the text layout information.
*/
@property (nullable, nonatomic, strong, readonly) YYTextLayout *textLayout;
#pragma mark - Configuring the Placeholder
///=============================================================================
/// @name Configuring the Placeholder
///=============================================================================
/**
The placeholder text displayed by the text view (when the text view is empty).
Set a new value to this property also replaces the text in `placeholderAttributedText`.
Get the value returns the plain text in `placeholderAttributedText`.
*/
@property (nullable, nonatomic, copy) NSString *placeholderText;
/**
The font of the placeholder text. Default is same as `font` property.
Set a new value to this property also causes the new font to be applied to the entire `placeholderAttributedText`.
Get the value returns the font at the head of `placeholderAttributedText`.
*/
@property (nullable, nonatomic, strong) UIFont *placeholderFont;
/**
The color of the placeholder text. Default is gray.
Set a new value to this property also causes the new color to be applied to the entire `placeholderAttributedText`.
Get the value returns the color at the head of `placeholderAttributedText`.
*/
@property (nullable, nonatomic, strong) UIColor *placeholderTextColor;
/**
The styled placeholder text displayed by the text view (when the text view is empty).
Set a new value to this property also replaces the value of the `placeholderText`,
`placeholderFont`, `placeholderTextColor`.
@discussion It only support the attributes declared in CoreText and YYTextAttribute.
See `NSAttributedString+YYText` for more convenience methods to set the attributes.
*/
@property (nullable, nonatomic, copy) NSAttributedString *placeholderAttributedText;
#pragma mark - Configuring the Text Container
///=============================================================================
/// @name Configuring the Text Container
///=============================================================================
/**
The inset of the text container's layout area within the text view's content area.
*/
@property (nonatomic) UIEdgeInsets textContainerInset;
/**
An array of UIBezierPath objects representing the exclusion paths inside the
receiver's bounding rectangle. Default value is nil.
*/
@property (nullable, nonatomic, copy) NSArray<UIBezierPath *> *exclusionPaths;
/**
Whether the receiver's layout orientation is vertical form. Default is NO.
It may used to edit/display CJK text.
*/
@property (nonatomic, getter=isVerticalForm) BOOL verticalForm;
/**
The text line position modifier used to modify the lines' position in layout.
See `YYTextLinePositionModifier` protocol for more information.
*/
@property (nullable, nonatomic, copy) id<YYTextLinePositionModifier> linePositionModifier;
/**
The debug option to display CoreText layout result.
The default value is [YYTextDebugOption sharedDebugOption].
*/
@property (nullable, nonatomic, copy) YYTextDebugOption *debugOption;
#pragma mark - Working with the Selection and Menu
///=============================================================================
/// @name Working with the Selection and Menu
///=============================================================================
/**
Scrolls the receiver until the text in the specified range is visible.
*/
- (void)scrollRangeToVisible:(NSRange)range;
/**
The current selection range of the receiver.
*/
@property (nonatomic) NSRange selectedRange;
/**
A Boolean value indicating whether inserting text replaces the previous contents.
The default value is NO.
*/
@property (nonatomic) BOOL clearsOnInsertion;
/**
A Boolean value indicating whether the receiver is selectable. Default is YES.
When the value of this property is NO, user cannot select content or edit text.
*/
@property (nonatomic, getter=isSelectable) BOOL selectable;
/**
A Boolean value indicating whether the receiver is highlightable. Default is YES.
When the value of this property is NO, user cannot interact with the highlight range of text.
*/
@property (nonatomic, getter=isHighlightable) BOOL highlightable;
/**
A Boolean value indicating whether the receiver is editable. Default is YES.
When the value of this property is NO, user cannot edit text.
*/
@property (nonatomic, getter=isEditable) BOOL editable;
/**
A Boolean value indicating whether the receiver can paste image from pasteboard. Default is NO.
When the value of this property is YES, user can paste image from pasteboard via "paste" menu.
*/
@property (nonatomic) BOOL allowsPasteImage;
/**
A Boolean value indicating whether the receiver can paste attributed text from pasteboard. Default is NO.
When the value of this property is YES, user can paste attributed text from pasteboard via "paste" menu.
*/
@property (nonatomic) BOOL allowsPasteAttributedString;
/**
A Boolean value indicating whether the receiver can copy attributed text to pasteboard. Default is YES.
When the value of this property is YES, user can copy attributed text (with attachment image)
from text view to pasteboard via "copy" menu.
*/
@property (nonatomic) BOOL allowsCopyAttributedString;
#pragma mark - Manage the undo and redo
///=============================================================================
/// @name Manage the undo and redo
///=============================================================================
/**
A Boolean value indicating whether the receiver can undo and redo typing with
shake gesture. The default value is YES.
*/
@property (nonatomic) BOOL allowsUndoAndRedo;
/**
The maximum undo/redo level. The default value is 20.
*/
@property (nonatomic) NSUInteger maximumUndoLevel;
#pragma mark - Replacing the System Input Views
///=============================================================================
/// @name Replacing the System Input Views
///=============================================================================
/**
The custom input view to display when the text view becomes the first responder.
It can be used to replace system keyboard.
@discussion If set the value while first responder, it will not take effect until
'reloadInputViews' is called.
*/
@property (nullable, nonatomic, readwrite, strong) __kindof UIView *inputView;
/**
The custom accessory view to display when the text view becomes the first responder.
It can be used to add a toolbar at the top of keyboard.
@discussion If set the value while first responder, it will not take effect until
'reloadInputViews' is called.
*/
@property (nullable, nonatomic, readwrite, strong) __kindof UIView *inputAccessoryView;
/**
If you use an custom accessory view without "inputAccessoryView" property,
you may set the accessory view's height. It may used by auto scroll calculation.
*/
@property (nonatomic) CGFloat extraAccessoryViewHeight;
@end
#else // TARGET_INTERFACE_BUILDER
IB_DESIGNABLE
@interface YYTextView : UIScrollView <UITextInput>
@property (null_resettable, nonatomic, copy) IBInspectable NSString *text;
@property (nullable, nonatomic, strong) IBInspectable UIColor *textColor;
@property (nullable, nonatomic, strong) IBInspectable NSString *fontName_;
@property (nonatomic) IBInspectable CGFloat fontSize_;
@property (nonatomic) IBInspectable BOOL fontIsBold_;
@property (nonatomic) IBInspectable NSTextAlignment textAlignment;
@property (nonatomic) IBInspectable YYTextVerticalAlignment textVerticalAlignment;
@property (nullable, nonatomic, copy) IBInspectable NSString *placeholderText;
@property (nullable, nonatomic, strong) IBInspectable UIColor *placeholderTextColor;
@property (nullable, nonatomic, strong) IBInspectable NSString *placeholderFontName_;
@property (nonatomic) IBInspectable CGFloat placeholderFontSize_;
@property (nonatomic) IBInspectable BOOL placeholderFontIsBold_;
@property (nonatomic, getter=isVerticalForm) IBInspectable BOOL verticalForm;
@property (nonatomic) IBInspectable BOOL clearsOnInsertion;
@property (nonatomic, getter=isSelectable) IBInspectable BOOL selectable;
@property (nonatomic, getter=isHighlightable) IBInspectable BOOL highlightable;
@property (nonatomic, getter=isEditable) IBInspectable BOOL editable;
@property (nonatomic) IBInspectable BOOL allowsPasteImage;
@property (nonatomic) IBInspectable BOOL allowsPasteAttributedString;
@property (nonatomic) IBInspectable BOOL allowsCopyAttributedString;
@property (nonatomic) IBInspectable BOOL allowsUndoAndRedo;
@property (nonatomic) IBInspectable NSUInteger maximumUndoLevel;
@property (nonatomic) IBInspectable CGFloat insetTop_;
@property (nonatomic) IBInspectable CGFloat insetBottom_;
@property (nonatomic) IBInspectable CGFloat insetLeft_;
@property (nonatomic) IBInspectable CGFloat insetRight_;
@property (nonatomic) IBInspectable BOOL debugEnabled_;
@property (nullable, nonatomic, weak) id<YYTextViewDelegate> delegate;
@property (nullable, nonatomic, strong) UIFont *font;
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes;
@property (nullable, nonatomic, copy) NSDictionary *linkTextAttributes;
@property (nullable, nonatomic, copy) NSDictionary *highlightTextAttributes;
@property (nullable, nonatomic, copy) NSDictionary *typingAttributes;
@property (nullable, nonatomic, copy) NSAttributedString *attributedText;
@property (nullable, nonatomic, strong) id<YYTextParser> textParser;
@property (nullable, nonatomic, strong, readonly) YYTextLayout *textLayout;
@property (nullable, nonatomic, strong) UIFont *placeholderFont;
@property (nullable, nonatomic, copy) NSAttributedString *placeholderAttributedText;
@property (nonatomic) UIEdgeInsets textContainerInset;
@property (nullable, nonatomic, copy) NSArray *exclusionPaths;
@property (nullable, nonatomic, copy) id<YYTextLinePositionModifier> linePositionModifier;
@property (nullable, nonatomic, copy) YYTextDebugOption *debugOption;
- (void)scrollRangeToVisible:(NSRange)range;
@property (nonatomic) NSRange selectedRange;
@property (nullable, nonatomic, readwrite, strong) __kindof UIView *inputView;
@property (nullable, nonatomic, readwrite, strong) __kindof UIView *inputAccessoryView;
@property (nonatomic) CGFloat extraAccessoryViewHeight;
@end
#endif // !TARGET_INTERFACE_BUILDER
// Notifications, see UITextView's documentation for more information.
UIKIT_EXTERN NSString *const YYTextViewTextDidBeginEditingNotification;
UIKIT_EXTERN NSString *const YYTextViewTextDidChangeNotification;
UIKIT_EXTERN NSString *const YYTextViewTextDidEndEditingNotification;
NS_ASSUME_NONNULL_END

3830
ProductApp/Pods/YYText/YYText/YYTextView.m generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,20 @@
/* Begin PBXBuildFile section */
664551746CA42328436EB43A /* Pods_ProductApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7E9D1598912BF8D031CA536 /* Pods_ProductApp.framework */; };
CA8181882C9E58B100EE7E6E /* StartKTXZWenZhangViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181872C9E58B100EE7E6E /* StartKTXZWenZhangViewController.m */; };
CA81818B2C9E598300EE7E6E /* StartKTXZWenZhangView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA81818A2C9E598300EE7E6E /* StartKTXZWenZhangView.m */; };
CA81818E2C9E60B100EE7E6E /* ViewLable.m in Sources */ = {isa = PBXBuildFile; fileRef = CA81818D2C9E60B100EE7E6E /* ViewLable.m */; };
CA8181922C9E748500EE7E6E /* StartKTXZPGViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181912C9E748500EE7E6E /* StartKTXZPGViewController.m */; };
CA8181962C9E751700EE7E6E /* StartKTXZPGView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181952C9E751700EE7E6E /* StartKTXZPGView.m */; };
CA8181992C9E77FB00EE7E6E /* DownJDLoadView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181972C9E77FB00EE7E6E /* DownJDLoadView.m */; };
CA81819D2C9E9C6600EE7E6E /* StartKTXZPGDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CA81819C2C9E9C6600EE7E6E /* StartKTXZPGDetailViewController.m */; };
CA8181A12C9E9CC500EE7E6E /* StartKTXZPGDetailView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181A02C9E9CC500EE7E6E /* StartKTXZPGDetailView.m */; };
CA8181A42C9EA07300EE7E6E /* StartKTXZPGDetailTabView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181A32C9EA07300EE7E6E /* StartKTXZPGDetailTabView.m */; };
CA8181A72C9EAB8700EE7E6E /* PGJUILabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181A62C9EAB8700EE7E6E /* PGJUILabel.m */; };
CA8181AD2C9EB95700EE7E6E /* StartKTXZPGDetailZHView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181AC2C9EB95700EE7E6E /* StartKTXZPGDetailZHView.m */; };
CA8181B02C9EB96000EE7E6E /* StartKTXZPGDetailFJView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181AF2C9EB96000EE7E6E /* StartKTXZPGDetailFJView.m */; };
CA8181B32C9EB96F00EE7E6E /* StartKTXZPGDetailXZView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181B22C9EB96F00EE7E6E /* StartKTXZPGDetailXZView.m */; };
CA8181B62C9ECB5800EE7E6E /* StartKTXZPGDetailFJItemView.m in Sources */ = {isa = PBXBuildFile; fileRef = CA8181B52C9ECB5800EE7E6E /* StartKTXZPGDetailFJItemView.m */; };
CABA14612C9D0B1C002CB3B1 /* ShouYeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CABA14602C9D0B1C002CB3B1 /* ShouYeViewController.m */; };
CABA14642C9D0B25002CB3B1 /* FanWenViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CABA14632C9D0B25002CB3B1 /* FanWenViewController.m */; };
CABA14672C9D0B31002CB3B1 /* AIChatViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CABA14662C9D0B31002CB3B1 /* AIChatViewController.m */; };
@ -213,6 +227,34 @@
/* Begin PBXFileReference section */
87167CC240BDAA55C86F80DE /* Pods-ProductApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ProductApp.debug.xcconfig"; path = "Target Support Files/Pods-ProductApp/Pods-ProductApp.debug.xcconfig"; sourceTree = "<group>"; };
B3DBEC0C6ED0E182F65EDBEA /* Pods-ProductApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ProductApp.release.xcconfig"; path = "Target Support Files/Pods-ProductApp/Pods-ProductApp.release.xcconfig"; sourceTree = "<group>"; };
CA8181862C9E58B100EE7E6E /* StartKTXZWenZhangViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZWenZhangViewController.h; sourceTree = "<group>"; };
CA8181872C9E58B100EE7E6E /* StartKTXZWenZhangViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZWenZhangViewController.m; sourceTree = "<group>"; };
CA8181892C9E598300EE7E6E /* StartKTXZWenZhangView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZWenZhangView.h; sourceTree = "<group>"; };
CA81818A2C9E598300EE7E6E /* StartKTXZWenZhangView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZWenZhangView.m; sourceTree = "<group>"; };
CA81818C2C9E60B100EE7E6E /* ViewLable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewLable.h; sourceTree = "<group>"; };
CA81818D2C9E60B100EE7E6E /* ViewLable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewLable.m; sourceTree = "<group>"; };
CA8181902C9E748500EE7E6E /* StartKTXZPGViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGViewController.h; sourceTree = "<group>"; };
CA8181912C9E748500EE7E6E /* StartKTXZPGViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGViewController.m; sourceTree = "<group>"; };
CA8181942C9E751700EE7E6E /* StartKTXZPGView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGView.h; sourceTree = "<group>"; };
CA8181952C9E751700EE7E6E /* StartKTXZPGView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGView.m; sourceTree = "<group>"; };
CA8181972C9E77FB00EE7E6E /* DownJDLoadView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DownJDLoadView.m; sourceTree = "<group>"; };
CA8181982C9E77FB00EE7E6E /* DownJDLoadView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DownJDLoadView.h; sourceTree = "<group>"; };
CA81819B2C9E9C6600EE7E6E /* StartKTXZPGDetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGDetailViewController.h; sourceTree = "<group>"; };
CA81819C2C9E9C6600EE7E6E /* StartKTXZPGDetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGDetailViewController.m; sourceTree = "<group>"; };
CA81819F2C9E9CC500EE7E6E /* StartKTXZPGDetailView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGDetailView.h; sourceTree = "<group>"; };
CA8181A02C9E9CC500EE7E6E /* StartKTXZPGDetailView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGDetailView.m; sourceTree = "<group>"; };
CA8181A22C9EA07300EE7E6E /* StartKTXZPGDetailTabView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGDetailTabView.h; sourceTree = "<group>"; };
CA8181A32C9EA07300EE7E6E /* StartKTXZPGDetailTabView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGDetailTabView.m; sourceTree = "<group>"; };
CA8181A52C9EAB8700EE7E6E /* PGJUILabel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PGJUILabel.h; sourceTree = "<group>"; };
CA8181A62C9EAB8700EE7E6E /* PGJUILabel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PGJUILabel.m; sourceTree = "<group>"; };
CA8181AB2C9EB95700EE7E6E /* StartKTXZPGDetailZHView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGDetailZHView.h; sourceTree = "<group>"; };
CA8181AC2C9EB95700EE7E6E /* StartKTXZPGDetailZHView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGDetailZHView.m; sourceTree = "<group>"; };
CA8181AE2C9EB96000EE7E6E /* StartKTXZPGDetailFJView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGDetailFJView.h; sourceTree = "<group>"; };
CA8181AF2C9EB96000EE7E6E /* StartKTXZPGDetailFJView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGDetailFJView.m; sourceTree = "<group>"; };
CA8181B12C9EB96F00EE7E6E /* StartKTXZPGDetailXZView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGDetailXZView.h; sourceTree = "<group>"; };
CA8181B22C9EB96F00EE7E6E /* StartKTXZPGDetailXZView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGDetailXZView.m; sourceTree = "<group>"; };
CA8181B42C9ECB5800EE7E6E /* StartKTXZPGDetailFJItemView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StartKTXZPGDetailFJItemView.h; sourceTree = "<group>"; };
CA8181B52C9ECB5800EE7E6E /* StartKTXZPGDetailFJItemView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StartKTXZPGDetailFJItemView.m; sourceTree = "<group>"; };
CABA145F2C9D0B1C002CB3B1 /* ShouYeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShouYeViewController.h; sourceTree = "<group>"; };
CABA14602C9D0B1C002CB3B1 /* ShouYeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShouYeViewController.m; sourceTree = "<group>"; };
CABA14622C9D0B25002CB3B1 /* FanWenViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FanWenViewController.h; sourceTree = "<group>"; };
@ -661,6 +703,80 @@
path = Pods;
sourceTree = "<group>";
};
CA81818F2C9E742400EE7E6E /* 批改识别 */ = {
isa = PBXGroup;
children = (
CA8181932C9E750900EE7E6E /* view */,
CA8181902C9E748500EE7E6E /* StartKTXZPGViewController.h */,
CA8181912C9E748500EE7E6E /* StartKTXZPGViewController.m */,
);
path = "批改识别";
sourceTree = "<group>";
};
CA8181932C9E750900EE7E6E /* view */ = {
isa = PBXGroup;
children = (
CA8181942C9E751700EE7E6E /* StartKTXZPGView.h */,
CA8181952C9E751700EE7E6E /* StartKTXZPGView.m */,
CA8181982C9E77FB00EE7E6E /* DownJDLoadView.h */,
CA8181972C9E77FB00EE7E6E /* DownJDLoadView.m */,
);
path = view;
sourceTree = "<group>";
};
CA81819A2C9E9C3100EE7E6E /* 批改信息 */ = {
isa = PBXGroup;
children = (
CA81819E2C9E9CB900EE7E6E /* view */,
CA81819B2C9E9C6600EE7E6E /* StartKTXZPGDetailViewController.h */,
CA81819C2C9E9C6600EE7E6E /* StartKTXZPGDetailViewController.m */,
);
path = "批改信息";
sourceTree = "<group>";
};
CA81819E2C9E9CB900EE7E6E /* view */ = {
isa = PBXGroup;
children = (
CA8181AA2C9EB92C00EE7E6E /* 写作思路 */,
CA8181A92C9EB92800EE7E6E /* 分句点评 */,
CA8181A82C9EB91800EE7E6E /* 综合评价 */,
CA81819F2C9E9CC500EE7E6E /* StartKTXZPGDetailView.h */,
CA8181A02C9E9CC500EE7E6E /* StartKTXZPGDetailView.m */,
CA8181A22C9EA07300EE7E6E /* StartKTXZPGDetailTabView.h */,
CA8181A32C9EA07300EE7E6E /* StartKTXZPGDetailTabView.m */,
);
path = view;
sourceTree = "<group>";
};
CA8181A82C9EB91800EE7E6E /* 综合评价 */ = {
isa = PBXGroup;
children = (
CA8181AB2C9EB95700EE7E6E /* StartKTXZPGDetailZHView.h */,
CA8181AC2C9EB95700EE7E6E /* StartKTXZPGDetailZHView.m */,
);
path = "综合评价";
sourceTree = "<group>";
};
CA8181A92C9EB92800EE7E6E /* 分句点评 */ = {
isa = PBXGroup;
children = (
CA8181AE2C9EB96000EE7E6E /* StartKTXZPGDetailFJView.h */,
CA8181AF2C9EB96000EE7E6E /* StartKTXZPGDetailFJView.m */,
CA8181B42C9ECB5800EE7E6E /* StartKTXZPGDetailFJItemView.h */,
CA8181B52C9ECB5800EE7E6E /* StartKTXZPGDetailFJItemView.m */,
);
path = "分句点评";
sourceTree = "<group>";
};
CA8181AA2C9EB92C00EE7E6E /* 写作思路 */ = {
isa = PBXGroup;
children = (
CA8181B12C9EB96F00EE7E6E /* StartKTXZPGDetailXZView.h */,
CA8181B22C9EB96F00EE7E6E /* StartKTXZPGDetailXZView.m */,
);
path = "写作思路";
sourceTree = "<group>";
};
CABA145A2C9D0AF2002CB3B1 /* 首页 */ = {
isa = PBXGroup;
children = (
@ -853,9 +969,13 @@
CABA14E42C9D76B7002CB3B1 /* 课堂写作 */ = {
isa = PBXGroup;
children = (
CA81819A2C9E9C3100EE7E6E /* 批改信息 */,
CA81818F2C9E742400EE7E6E /* 批改识别 */,
CABA14E82C9D7AA5002CB3B1 /* view */,
CABA14E52C9D76F8002CB3B1 /* StartKeTangXieZuoViewController.h */,
CABA14E62C9D76F8002CB3B1 /* StartKeTangXieZuoViewController.m */,
CA8181862C9E58B100EE7E6E /* StartKTXZWenZhangViewController.h */,
CA8181872C9E58B100EE7E6E /* StartKTXZWenZhangViewController.m */,
);
path = "课堂写作";
sourceTree = "<group>";
@ -865,6 +985,8 @@
children = (
CABA14E92C9D7AB2002CB3B1 /* StartKeTangXieZuoView.h */,
CABA14EA2C9D7AB2002CB3B1 /* StartKeTangXieZuoView.m */,
CA8181892C9E598300EE7E6E /* StartKTXZWenZhangView.h */,
CA81818A2C9E598300EE7E6E /* StartKTXZWenZhangView.m */,
);
path = view;
sourceTree = "<group>";
@ -946,6 +1068,10 @@
CB489E082744A0BC00DA044A /* UIDevice+PGJ.m */,
CB489DEE2744A0BC00DA044A /* YQLocationTransform.h */,
CB489E0B2744A0BC00DA044A /* YQLocationTransform.m */,
CA81818C2C9E60B100EE7E6E /* ViewLable.h */,
CA81818D2C9E60B100EE7E6E /* ViewLable.m */,
CA8181A52C9EAB8700EE7E6E /* PGJUILabel.h */,
CA8181A62C9EAB8700EE7E6E /* PGJUILabel.m */,
);
path = Other;
sourceTree = "<group>";
@ -1834,6 +1960,7 @@
CABA14CB2C9D4395002CB3B1 /* GTMBases64.m in Sources */,
CB489FA02744A0BD00DA044A /* UIView+ZJFrame.m in Sources */,
CABA148D2C9D4312002CB3B1 /* PublicNetWorkManager.m in Sources */,
CA81818B2C9E598300EE7E6E /* StartKTXZWenZhangView.m in Sources */,
CB489FA72744A0BD00DA044A /* ZJScrollPageView.m in Sources */,
CB489F942744A0BD00DA044A /* AreaprovinceListModel.m in Sources */,
CABA14B02C9D4318002CB3B1 /* UserAccountModel.m in Sources */,
@ -1853,6 +1980,7 @@
CB489FB72744A0BD00DA044A /* UIAlertController+Blocks.m in Sources */,
CABA14612C9D0B1C002CB3B1 /* ShouYeViewController.m in Sources */,
CABA14B62C9D4318002CB3B1 /* UserShareModel.m in Sources */,
CA8181A12C9E9CC500EE7E6E /* StartKTXZPGDetailView.m in Sources */,
CABA14B42C9D4318002CB3B1 /* UserLoginModel.m in Sources */,
CB489FCC2744A0BD00DA044A /* ConfigireSetting.m in Sources */,
CABA14CC2C9D4395002CB3B1 /* NSData+PEHIExtension.m in Sources */,
@ -1866,10 +1994,14 @@
CABA14B52C9D4318002CB3B1 /* UserModel.m in Sources */,
CB489FA92744A0BD00DA044A /* UIView+XLExtension.m in Sources */,
CB489F512744A0BD00DA044A /* PGJSearchView.m in Sources */,
CA8181B02C9EB96000EE7E6E /* StartKTXZPGDetailFJView.m in Sources */,
CB489F7A2744A0BD00DA044A /* FSTextView.m in Sources */,
CABA14B32C9D4318002CB3B1 /* UserConfigModel.m in Sources */,
CA8181992C9E77FB00EE7E6E /* DownJDLoadView.m in Sources */,
CB489F6F2744A0BD00DA044A /* XLAlertView.m in Sources */,
CA81819D2C9E9C6600EE7E6E /* StartKTXZPGDetailViewController.m in Sources */,
CB489FA32744A0BD00DA044A /* ZJContentView.m in Sources */,
CA8181922C9E748500EE7E6E /* StartKTXZPGViewController.m in Sources */,
CB489F6C2744A0BD00DA044A /* RadianDisView.m in Sources */,
CABA14AE2C9D4318002CB3B1 /* OrderInfoModel.m in Sources */,
CB489F502744A0BD00DA044A /* Tools.m in Sources */,
@ -1883,6 +2015,9 @@
CB489F662744A0BD00DA044A /* DAYUtils.m in Sources */,
CB489F6A2744A0BD00DA044A /* UILabel+PGGLabText.m in Sources */,
CB489F772744A0BD00DA044A /* WYCamaImageTools.m in Sources */,
CA8181B62C9ECB5800EE7E6E /* StartKTXZPGDetailFJItemView.m in Sources */,
CA8181A72C9EAB8700EE7E6E /* PGJUILabel.m in Sources */,
CA81818E2C9E60B100EE7E6E /* ViewLable.m in Sources */,
CB489FB42744A0BD00DA044A /* CXDatePickerView.m in Sources */,
CB489F7B2744A0BD00DA044A /* UIImageView+VideoImageView.m in Sources */,
CB489F602744A0BD00DA044A /* GETCorelLocation.m in Sources */,
@ -1893,6 +2028,7 @@
CB489F922744A0BD00DA044A /* IndexGetNewsModel.m in Sources */,
CABA14E02C9D6F30002CB3B1 /* RoleTreeModel.m in Sources */,
CB489F4F2744A0BD00DA044A /* AddressListAlterViewTableViewCell.m in Sources */,
CA8181A42C9EA07300EE7E6E /* StartKTXZPGDetailTabView.m in Sources */,
CABA14E32C9D707E002CB3B1 /* TextbookInfoModel.m in Sources */,
CB489F792744A0BD00DA044A /* UIApplication+Visible.m in Sources */,
CB489FD72744A30000DA044A /* PGGMoviePlayer.m in Sources */,
@ -1916,6 +2052,7 @@
CB489F932744A0BD00DA044A /* PutImageModel.m in Sources */,
CB489F7D2744A0BD00DA044A /* OpenSSLWrapper.m in Sources */,
CB489FAB2744A0BD00DA044A /* XLZoomingScrollView.m in Sources */,
CA8181962C9E751700EE7E6E /* StartKTXZPGView.m in Sources */,
CB489F752744A0BD00DA044A /* NavgationController.m in Sources */,
CB489F682744A0BD00DA044A /* DAYCalendarView.m in Sources */,
CABA14AD2C9D4318002CB3B1 /* OrderInfoListModel.m in Sources */,
@ -1929,10 +2066,12 @@
CB489FB82744A0BD00DA044A /* HXLoadingHUD.m in Sources */,
CABA14D12C9D4961002CB3B1 /* StartGNView.m in Sources */,
CB489FA82744A0BD00DA044A /* XLProgressView.m in Sources */,
CA8181AD2C9EB95700EE7E6E /* StartKTXZPGDetailZHView.m in Sources */,
CB489F802744A0BD00DA044A /* NSString+Base64.m in Sources */,
CB489FD82744A30000DA044A /* PGGMovieLineSetView.m in Sources */,
CB489F4D2744A0BD00DA044A /* AddressListAlterView.m in Sources */,
CB489FA22744A0BD00DA044A /* UIViewController+ZJScrollPageController.m in Sources */,
CA8181882C9E58B100EE7E6E /* StartKTXZWenZhangViewController.m in Sources */,
CB489F5A2744A0BD00DA044A /* YQLocationTransform.m in Sources */,
CB489F782744A0BD00DA044A /* NSBundle+DAUtils.m in Sources */,
CB489F4C2744A0BD00DA044A /* AddressListUrlAlterView.m in Sources */,
@ -1952,6 +2091,7 @@
CB489F562744A0BD00DA044A /* amrFileCodec.m in Sources */,
CABA14822C9D1D0A002CB3B1 /* TextHeaderCollectionViewCell.m in Sources */,
CB489F572744A0BD00DA044A /* AudioRecored.m in Sources */,
CA8181B32C9EB96F00EE7E6E /* StartKTXZPGDetailXZView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -3,116 +3,4 @@
uuid = "41EC793F-022E-49E6-A76A-A910FEF0483C"
type = "0"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "6E30908F-C99A-4DF7-91AF-786CDB51EF6D"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "ProductApp/ProductMain/NetWorkManager/NetWorkManager.m"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "80"
endingLineNumber = "80"
landmarkName = "+requestExampleTextbookInfoData:role_id:pid:Callback:"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "D6799B8E-E195-4F66-A212-E247BE50C3E3"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "ProductApp/ProductMain/&#x5f15;&#x5bfc;/&#x8bfe;&#x5802;&#x5199;&#x4f5c;/view/StartKeTangXieZuoView.m"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "189"
endingLineNumber = "189"
landmarkName = "-drawInfoView"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "974ECB6D-A4AA-482F-A467-4C973D39D840"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "ProductApp/ProductMain/&#x5f15;&#x5bfc;/&#x8bfe;&#x5802;&#x5199;&#x4f5c;/view/StartKeTangXieZuoView.m"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "167"
endingLineNumber = "167"
landmarkName = "-drawInfoView"
landmarkType = "7">
<Locations>
<Location
uuid = "974ECB6D-A4AA-482F-A467-4C973D39D840 - 24aa26b3e1595c4d"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "-[StartKeTangXieZuoView drawInfoView]"
moduleName = "ProductApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/gongzuo/Desktop/%E5%85%AC%E5%8F%B8GIT/%E6%9E%81%E5%85%89%E5%86%99%E4%BD%9C/ProductApp/ProductApp/ProductMain/%E5%BC%95%E5%AF%BC/%E8%AF%BE%E5%A0%82%E5%86%99%E4%BD%9C/view/StartKeTangXieZuoView.m"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "167"
endingLineNumber = "167">
</Location>
<Location
uuid = "974ECB6D-A4AA-482F-A467-4C973D39D840 - 3fb8e11c6301110d"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
symbolName = "__37-[StartKeTangXieZuoView drawInfoView]_block_invoke_4"
moduleName = "ProductApp"
usesParentBreakpointCondition = "Yes"
urlString = "file:///Users/gongzuo/Desktop/%E5%85%AC%E5%8F%B8GIT/%E6%9E%81%E5%85%89%E5%86%99%E4%BD%9C/ProductApp/ProductApp/ProductMain/%E5%BC%95%E5%AF%BC/%E8%AF%BE%E5%A0%82%E5%86%99%E4%BD%9C/view/StartKeTangXieZuoView.m"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "168"
endingLineNumber = "168">
</Location>
</Locations>
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "656A30EF-4FF3-413F-BAD2-B0459E8ADFD1"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "ProductApp/ProductMain/&#x5f15;&#x5bfc;/&#x8bfe;&#x5802;&#x5199;&#x4f5c;/view/StartKeTangXieZuoView.m"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "76"
endingLineNumber = "76"
landmarkName = "-initWithFrame:"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "EEDC1596-54F4-43CA-B45E-50F3FA65E2F9"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "ProductApp/ProductMain/&#x5f15;&#x5bfc;/&#x8bfe;&#x5802;&#x5199;&#x4f5c;/view/StartKeTangXieZuoView.m"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "60"
endingLineNumber = "60"
landmarkName = "-initWithFrame:"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>

View File

@ -9,6 +9,9 @@
#import "NavgationController.h"
#import "StartViewController.h"
#import "StartKTXZPGDetailViewController.h"
@interface AppDelegate ()
@end
@ -30,7 +33,7 @@
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window setRootViewController:[[NavgationController alloc] initWithRootViewController:[StartViewController new]]];
[self.window setRootViewController:[[NavgationController alloc] initWithRootViewController:[StartKTXZPGDetailViewController new]]];
[self.window makeKeyAndVisible];

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "切图 206@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "切图 206@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "WechatIMG9.jpg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Some files were not shown because too many files have changed in this diff Show More