├── .gitignore
├── Example
├── AppDelegate.swift
├── Base.lproj
│ ├── LaunchScreen.xib
│ └── Main.storyboard
├── Images.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
└── ViewController.swift
├── LICENSE
├── MarkdownTextView.podspec
├── MarkdownTextView.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ └── MarkdownTextView.xcscheme
├── MarkdownTextView
├── HighlighterTextStorage.swift
├── HighlighterType.swift
├── Info.plist
├── LinkHighlighter.swift
├── MarkdownAttributes.swift
├── MarkdownFencedCodeHighlighter.swift
├── MarkdownHeaderHighlighter.swift
├── MarkdownLinkHighlighter.swift
├── MarkdownListHighlighter.swift
├── MarkdownStrikethroughHighlighter.swift
├── MarkdownSuperscriptHighlighter.swift
├── MarkdownTextStorage.swift
├── MarkdownTextView.h
├── MarkdownTextView.swift
├── RegularExpressionHighlighter.swift
└── TextUtilities.swift
├── MarkdownTextViewTests
├── Info.plist
└── MarkdownTextViewTests.swift
├── README.md
└── screenshot.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | # Pods/
27 |
28 | # Carthage
29 | #
30 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
31 | # Carthage/Checkouts
32 |
33 | Carthage/Build
34 |
--------------------------------------------------------------------------------
/Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Example
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/Example/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Example/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Example/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/Example/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Example/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/27/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MarkdownTextView
11 |
12 | class ViewController: UIViewController {
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | let attributes = MarkdownAttributes()
17 | let textStorage = MarkdownTextStorage(attributes: attributes)
18 | do {
19 | textStorage.addHighlighter(try LinkHighlighter())
20 | } catch let error {
21 | fatalError("Error initializing LinkHighlighter: \(error)")
22 | }
23 | textStorage.addHighlighter(MarkdownStrikethroughHighlighter())
24 | textStorage.addHighlighter(MarkdownSuperscriptHighlighter())
25 | if let codeBlockAttributes = attributes.codeBlockAttributes {
26 | textStorage.addHighlighter(MarkdownFencedCodeHighlighter(attributes: codeBlockAttributes))
27 | }
28 |
29 | let textView = MarkdownTextView(frame: CGRectZero, textStorage: textStorage)
30 | textView.translatesAutoresizingMaskIntoConstraints = false
31 | view.addSubview(textView)
32 |
33 | let views = ["textView": textView]
34 | var constraints = NSLayoutConstraint.constraintsWithVisualFormat("V:|-20-[textView]-20-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)
35 | constraints += NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[textView]-20-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)
36 | NSLayoutConstraint.activateConstraints(constraints)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Indragie Karunaratne
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/MarkdownTextView.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "MarkdownTextView"
3 | s.version = "1.0.0"
4 | s.summary = "Rich Markdown editing control for iOS"
5 | s.homepage = "https://github.com/indragiek/MarkdownTextView"
6 | s.screenshots = "https://github.com/indragiek/MarkdownTextView/blob/master/screenshot.png"
7 | s.license = 'MIT'
8 | s.author = { "indragiek" => "i@indragie.com"}
9 | s.source = { :git => "https://github.com/indragiek/MarkdownTextView.git", :tag => s.version.to_s }
10 | s.platform = :ios, '8.0'
11 | s.requires_arc = true
12 | s.source_files = 'MarkdownTextView/**/*'
13 | end
14 |
--------------------------------------------------------------------------------
/MarkdownTextView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 7252E0331AF1BC1D006D0535 /* MarkdownTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 7252E0321AF1BC1D006D0535 /* MarkdownTextView.h */; settings = {ATTRIBUTES = (Public, ); }; };
11 | 7252E0391AF1BC1E006D0535 /* MarkdownTextView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7252E02D1AF1BC1D006D0535 /* MarkdownTextView.framework */; };
12 | 7252E0401AF1BC1E006D0535 /* MarkdownTextViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E03F1AF1BC1E006D0535 /* MarkdownTextViewTests.swift */; };
13 | 7252E0571AF1BCD0006D0535 /* HighlighterTextStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E0491AF1BCD0006D0535 /* HighlighterTextStorage.swift */; };
14 | 7252E0581AF1BCD0006D0535 /* HighlighterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E04A1AF1BCD0006D0535 /* HighlighterType.swift */; };
15 | 7252E0591AF1BCD0006D0535 /* LinkHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E04B1AF1BCD0006D0535 /* LinkHighlighter.swift */; };
16 | 7252E05A1AF1BCD0006D0535 /* MarkdownAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E04C1AF1BCD0006D0535 /* MarkdownAttributes.swift */; };
17 | 7252E05B1AF1BCD0006D0535 /* MarkdownFencedCodeHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E04D1AF1BCD0006D0535 /* MarkdownFencedCodeHighlighter.swift */; };
18 | 7252E05C1AF1BCD0006D0535 /* MarkdownHeaderHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E04E1AF1BCD0006D0535 /* MarkdownHeaderHighlighter.swift */; };
19 | 7252E05D1AF1BCD0006D0535 /* MarkdownLinkHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E04F1AF1BCD0006D0535 /* MarkdownLinkHighlighter.swift */; };
20 | 7252E05E1AF1BCD0006D0535 /* MarkdownListHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E0501AF1BCD0006D0535 /* MarkdownListHighlighter.swift */; };
21 | 7252E05F1AF1BCD1006D0535 /* MarkdownStrikethroughHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E0511AF1BCD0006D0535 /* MarkdownStrikethroughHighlighter.swift */; };
22 | 7252E0601AF1BCD1006D0535 /* MarkdownSuperscriptHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E0521AF1BCD0006D0535 /* MarkdownSuperscriptHighlighter.swift */; };
23 | 7252E0611AF1BCD1006D0535 /* MarkdownTextStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E0531AF1BCD0006D0535 /* MarkdownTextStorage.swift */; };
24 | 7252E0621AF1BCD1006D0535 /* MarkdownTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E0541AF1BCD0006D0535 /* MarkdownTextView.swift */; };
25 | 7252E0631AF1BCD1006D0535 /* RegularExpressionHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E0551AF1BCD0006D0535 /* RegularExpressionHighlighter.swift */; };
26 | 7252E0641AF1BCD1006D0535 /* TextUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E0561AF1BCD0006D0535 /* TextUtilities.swift */; };
27 | 7252E06E1AF1BD61006D0535 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E06D1AF1BD61006D0535 /* AppDelegate.swift */; };
28 | 7252E0701AF1BD61006D0535 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252E06F1AF1BD61006D0535 /* ViewController.swift */; };
29 | 7252E0731AF1BD61006D0535 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7252E0711AF1BD61006D0535 /* Main.storyboard */; };
30 | 7252E0751AF1BD61006D0535 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7252E0741AF1BD61006D0535 /* Images.xcassets */; };
31 | 7252E0781AF1BD61006D0535 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7252E0761AF1BD61006D0535 /* LaunchScreen.xib */; };
32 | 7252E08D1AF1BD86006D0535 /* MarkdownTextView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7252E02D1AF1BC1D006D0535 /* MarkdownTextView.framework */; };
33 | 7252E08F1AF1BD93006D0535 /* MarkdownTextView.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 7252E02D1AF1BC1D006D0535 /* MarkdownTextView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
34 | /* End PBXBuildFile section */
35 |
36 | /* Begin PBXContainerItemProxy section */
37 | 7252E03A1AF1BC1E006D0535 /* PBXContainerItemProxy */ = {
38 | isa = PBXContainerItemProxy;
39 | containerPortal = 7252E0241AF1BC1D006D0535 /* Project object */;
40 | proxyType = 1;
41 | remoteGlobalIDString = 7252E02C1AF1BC1D006D0535;
42 | remoteInfo = MarkdownTextView;
43 | };
44 | 7252E08B1AF1BD82006D0535 /* PBXContainerItemProxy */ = {
45 | isa = PBXContainerItemProxy;
46 | containerPortal = 7252E0241AF1BC1D006D0535 /* Project object */;
47 | proxyType = 1;
48 | remoteGlobalIDString = 7252E02C1AF1BC1D006D0535;
49 | remoteInfo = MarkdownTextView;
50 | };
51 | /* End PBXContainerItemProxy section */
52 |
53 | /* Begin PBXCopyFilesBuildPhase section */
54 | 7252E08E1AF1BD88006D0535 /* Copy Frameworks */ = {
55 | isa = PBXCopyFilesBuildPhase;
56 | buildActionMask = 2147483647;
57 | dstPath = "";
58 | dstSubfolderSpec = 10;
59 | files = (
60 | 7252E08F1AF1BD93006D0535 /* MarkdownTextView.framework in Copy Frameworks */,
61 | );
62 | name = "Copy Frameworks";
63 | runOnlyForDeploymentPostprocessing = 0;
64 | };
65 | /* End PBXCopyFilesBuildPhase section */
66 |
67 | /* Begin PBXFileReference section */
68 | 7252E02D1AF1BC1D006D0535 /* MarkdownTextView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MarkdownTextView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
69 | 7252E0311AF1BC1D006D0535 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
70 | 7252E0321AF1BC1D006D0535 /* MarkdownTextView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MarkdownTextView.h; sourceTree = ""; };
71 | 7252E0381AF1BC1E006D0535 /* MarkdownTextViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MarkdownTextViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
72 | 7252E03E1AF1BC1E006D0535 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
73 | 7252E03F1AF1BC1E006D0535 /* MarkdownTextViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTextViewTests.swift; sourceTree = ""; };
74 | 7252E0491AF1BCD0006D0535 /* HighlighterTextStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighlighterTextStorage.swift; sourceTree = ""; };
75 | 7252E04A1AF1BCD0006D0535 /* HighlighterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighlighterType.swift; sourceTree = ""; };
76 | 7252E04B1AF1BCD0006D0535 /* LinkHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkHighlighter.swift; sourceTree = ""; };
77 | 7252E04C1AF1BCD0006D0535 /* MarkdownAttributes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownAttributes.swift; sourceTree = ""; };
78 | 7252E04D1AF1BCD0006D0535 /* MarkdownFencedCodeHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownFencedCodeHighlighter.swift; sourceTree = ""; };
79 | 7252E04E1AF1BCD0006D0535 /* MarkdownHeaderHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownHeaderHighlighter.swift; sourceTree = ""; };
80 | 7252E04F1AF1BCD0006D0535 /* MarkdownLinkHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownLinkHighlighter.swift; sourceTree = ""; };
81 | 7252E0501AF1BCD0006D0535 /* MarkdownListHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownListHighlighter.swift; sourceTree = ""; };
82 | 7252E0511AF1BCD0006D0535 /* MarkdownStrikethroughHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownStrikethroughHighlighter.swift; sourceTree = ""; };
83 | 7252E0521AF1BCD0006D0535 /* MarkdownSuperscriptHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownSuperscriptHighlighter.swift; sourceTree = ""; };
84 | 7252E0531AF1BCD0006D0535 /* MarkdownTextStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTextStorage.swift; sourceTree = ""; };
85 | 7252E0541AF1BCD0006D0535 /* MarkdownTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTextView.swift; sourceTree = ""; };
86 | 7252E0551AF1BCD0006D0535 /* RegularExpressionHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegularExpressionHighlighter.swift; sourceTree = ""; };
87 | 7252E0561AF1BCD0006D0535 /* TextUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextUtilities.swift; sourceTree = ""; };
88 | 7252E0691AF1BD61006D0535 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
89 | 7252E06C1AF1BD61006D0535 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
90 | 7252E06D1AF1BD61006D0535 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
91 | 7252E06F1AF1BD61006D0535 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
92 | 7252E0721AF1BD61006D0535 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
93 | 7252E0741AF1BD61006D0535 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
94 | 7252E0771AF1BD61006D0535 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
95 | /* End PBXFileReference section */
96 |
97 | /* Begin PBXFrameworksBuildPhase section */
98 | 7252E0291AF1BC1D006D0535 /* Frameworks */ = {
99 | isa = PBXFrameworksBuildPhase;
100 | buildActionMask = 2147483647;
101 | files = (
102 | );
103 | runOnlyForDeploymentPostprocessing = 0;
104 | };
105 | 7252E0351AF1BC1E006D0535 /* Frameworks */ = {
106 | isa = PBXFrameworksBuildPhase;
107 | buildActionMask = 2147483647;
108 | files = (
109 | 7252E0391AF1BC1E006D0535 /* MarkdownTextView.framework in Frameworks */,
110 | );
111 | runOnlyForDeploymentPostprocessing = 0;
112 | };
113 | 7252E0661AF1BD61006D0535 /* Frameworks */ = {
114 | isa = PBXFrameworksBuildPhase;
115 | buildActionMask = 2147483647;
116 | files = (
117 | 7252E08D1AF1BD86006D0535 /* MarkdownTextView.framework in Frameworks */,
118 | );
119 | runOnlyForDeploymentPostprocessing = 0;
120 | };
121 | /* End PBXFrameworksBuildPhase section */
122 |
123 | /* Begin PBXGroup section */
124 | 7252E0231AF1BC1D006D0535 = {
125 | isa = PBXGroup;
126 | children = (
127 | 7252E02F1AF1BC1D006D0535 /* MarkdownTextView */,
128 | 7252E03C1AF1BC1E006D0535 /* MarkdownTextViewTests */,
129 | 7252E06A1AF1BD61006D0535 /* Example */,
130 | 7252E02E1AF1BC1D006D0535 /* Products */,
131 | );
132 | sourceTree = "";
133 | };
134 | 7252E02E1AF1BC1D006D0535 /* Products */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 7252E02D1AF1BC1D006D0535 /* MarkdownTextView.framework */,
138 | 7252E0381AF1BC1E006D0535 /* MarkdownTextViewTests.xctest */,
139 | 7252E0691AF1BD61006D0535 /* Example.app */,
140 | );
141 | name = Products;
142 | sourceTree = "";
143 | };
144 | 7252E02F1AF1BC1D006D0535 /* MarkdownTextView */ = {
145 | isa = PBXGroup;
146 | children = (
147 | 7252E0321AF1BC1D006D0535 /* MarkdownTextView.h */,
148 | 7252E0491AF1BCD0006D0535 /* HighlighterTextStorage.swift */,
149 | 7252E04A1AF1BCD0006D0535 /* HighlighterType.swift */,
150 | 7252E04B1AF1BCD0006D0535 /* LinkHighlighter.swift */,
151 | 7252E04C1AF1BCD0006D0535 /* MarkdownAttributes.swift */,
152 | 7252E04D1AF1BCD0006D0535 /* MarkdownFencedCodeHighlighter.swift */,
153 | 7252E04E1AF1BCD0006D0535 /* MarkdownHeaderHighlighter.swift */,
154 | 7252E04F1AF1BCD0006D0535 /* MarkdownLinkHighlighter.swift */,
155 | 7252E0501AF1BCD0006D0535 /* MarkdownListHighlighter.swift */,
156 | 7252E0511AF1BCD0006D0535 /* MarkdownStrikethroughHighlighter.swift */,
157 | 7252E0521AF1BCD0006D0535 /* MarkdownSuperscriptHighlighter.swift */,
158 | 7252E0531AF1BCD0006D0535 /* MarkdownTextStorage.swift */,
159 | 7252E0541AF1BCD0006D0535 /* MarkdownTextView.swift */,
160 | 7252E0551AF1BCD0006D0535 /* RegularExpressionHighlighter.swift */,
161 | 7252E0561AF1BCD0006D0535 /* TextUtilities.swift */,
162 | 7252E0301AF1BC1D006D0535 /* Supporting Files */,
163 | );
164 | path = MarkdownTextView;
165 | sourceTree = "";
166 | };
167 | 7252E0301AF1BC1D006D0535 /* Supporting Files */ = {
168 | isa = PBXGroup;
169 | children = (
170 | 7252E0311AF1BC1D006D0535 /* Info.plist */,
171 | );
172 | name = "Supporting Files";
173 | sourceTree = "";
174 | };
175 | 7252E03C1AF1BC1E006D0535 /* MarkdownTextViewTests */ = {
176 | isa = PBXGroup;
177 | children = (
178 | 7252E03F1AF1BC1E006D0535 /* MarkdownTextViewTests.swift */,
179 | 7252E03D1AF1BC1E006D0535 /* Supporting Files */,
180 | );
181 | path = MarkdownTextViewTests;
182 | sourceTree = "";
183 | };
184 | 7252E03D1AF1BC1E006D0535 /* Supporting Files */ = {
185 | isa = PBXGroup;
186 | children = (
187 | 7252E03E1AF1BC1E006D0535 /* Info.plist */,
188 | );
189 | name = "Supporting Files";
190 | sourceTree = "";
191 | };
192 | 7252E06A1AF1BD61006D0535 /* Example */ = {
193 | isa = PBXGroup;
194 | children = (
195 | 7252E06D1AF1BD61006D0535 /* AppDelegate.swift */,
196 | 7252E06F1AF1BD61006D0535 /* ViewController.swift */,
197 | 7252E0711AF1BD61006D0535 /* Main.storyboard */,
198 | 7252E0741AF1BD61006D0535 /* Images.xcassets */,
199 | 7252E0761AF1BD61006D0535 /* LaunchScreen.xib */,
200 | 7252E06B1AF1BD61006D0535 /* Supporting Files */,
201 | );
202 | path = Example;
203 | sourceTree = "";
204 | };
205 | 7252E06B1AF1BD61006D0535 /* Supporting Files */ = {
206 | isa = PBXGroup;
207 | children = (
208 | 7252E06C1AF1BD61006D0535 /* Info.plist */,
209 | );
210 | name = "Supporting Files";
211 | sourceTree = "";
212 | };
213 | /* End PBXGroup section */
214 |
215 | /* Begin PBXHeadersBuildPhase section */
216 | 7252E02A1AF1BC1D006D0535 /* Headers */ = {
217 | isa = PBXHeadersBuildPhase;
218 | buildActionMask = 2147483647;
219 | files = (
220 | 7252E0331AF1BC1D006D0535 /* MarkdownTextView.h in Headers */,
221 | );
222 | runOnlyForDeploymentPostprocessing = 0;
223 | };
224 | /* End PBXHeadersBuildPhase section */
225 |
226 | /* Begin PBXNativeTarget section */
227 | 7252E02C1AF1BC1D006D0535 /* MarkdownTextView */ = {
228 | isa = PBXNativeTarget;
229 | buildConfigurationList = 7252E0431AF1BC1E006D0535 /* Build configuration list for PBXNativeTarget "MarkdownTextView" */;
230 | buildPhases = (
231 | 7252E0281AF1BC1D006D0535 /* Sources */,
232 | 7252E0291AF1BC1D006D0535 /* Frameworks */,
233 | 7252E02A1AF1BC1D006D0535 /* Headers */,
234 | 7252E02B1AF1BC1D006D0535 /* Resources */,
235 | );
236 | buildRules = (
237 | );
238 | dependencies = (
239 | );
240 | name = MarkdownTextView;
241 | productName = MarkdownTextView;
242 | productReference = 7252E02D1AF1BC1D006D0535 /* MarkdownTextView.framework */;
243 | productType = "com.apple.product-type.framework";
244 | };
245 | 7252E0371AF1BC1E006D0535 /* MarkdownTextViewTests */ = {
246 | isa = PBXNativeTarget;
247 | buildConfigurationList = 7252E0461AF1BC1E006D0535 /* Build configuration list for PBXNativeTarget "MarkdownTextViewTests" */;
248 | buildPhases = (
249 | 7252E0341AF1BC1E006D0535 /* Sources */,
250 | 7252E0351AF1BC1E006D0535 /* Frameworks */,
251 | 7252E0361AF1BC1E006D0535 /* Resources */,
252 | );
253 | buildRules = (
254 | );
255 | dependencies = (
256 | 7252E03B1AF1BC1E006D0535 /* PBXTargetDependency */,
257 | );
258 | name = MarkdownTextViewTests;
259 | productName = MarkdownTextViewTests;
260 | productReference = 7252E0381AF1BC1E006D0535 /* MarkdownTextViewTests.xctest */;
261 | productType = "com.apple.product-type.bundle.unit-test";
262 | };
263 | 7252E0681AF1BD61006D0535 /* Example */ = {
264 | isa = PBXNativeTarget;
265 | buildConfigurationList = 7252E0851AF1BD61006D0535 /* Build configuration list for PBXNativeTarget "Example" */;
266 | buildPhases = (
267 | 7252E0651AF1BD61006D0535 /* Sources */,
268 | 7252E0661AF1BD61006D0535 /* Frameworks */,
269 | 7252E0671AF1BD61006D0535 /* Resources */,
270 | 7252E08E1AF1BD88006D0535 /* Copy Frameworks */,
271 | );
272 | buildRules = (
273 | );
274 | dependencies = (
275 | 7252E08C1AF1BD82006D0535 /* PBXTargetDependency */,
276 | );
277 | name = Example;
278 | productName = Example;
279 | productReference = 7252E0691AF1BD61006D0535 /* Example.app */;
280 | productType = "com.apple.product-type.application";
281 | };
282 | /* End PBXNativeTarget section */
283 |
284 | /* Begin PBXProject section */
285 | 7252E0241AF1BC1D006D0535 /* Project object */ = {
286 | isa = PBXProject;
287 | attributes = {
288 | LastSwiftUpdateCheck = 0720;
289 | LastUpgradeCheck = 0720;
290 | ORGANIZATIONNAME = "Indragie Karunaratne";
291 | TargetAttributes = {
292 | 7252E02C1AF1BC1D006D0535 = {
293 | CreatedOnToolsVersion = 6.3.1;
294 | };
295 | 7252E0371AF1BC1E006D0535 = {
296 | CreatedOnToolsVersion = 6.3.1;
297 | };
298 | 7252E0681AF1BD61006D0535 = {
299 | CreatedOnToolsVersion = 6.3.1;
300 | };
301 | };
302 | };
303 | buildConfigurationList = 7252E0271AF1BC1D006D0535 /* Build configuration list for PBXProject "MarkdownTextView" */;
304 | compatibilityVersion = "Xcode 3.2";
305 | developmentRegion = English;
306 | hasScannedForEncodings = 0;
307 | knownRegions = (
308 | en,
309 | Base,
310 | );
311 | mainGroup = 7252E0231AF1BC1D006D0535;
312 | productRefGroup = 7252E02E1AF1BC1D006D0535 /* Products */;
313 | projectDirPath = "";
314 | projectRoot = "";
315 | targets = (
316 | 7252E02C1AF1BC1D006D0535 /* MarkdownTextView */,
317 | 7252E0371AF1BC1E006D0535 /* MarkdownTextViewTests */,
318 | 7252E0681AF1BD61006D0535 /* Example */,
319 | );
320 | };
321 | /* End PBXProject section */
322 |
323 | /* Begin PBXResourcesBuildPhase section */
324 | 7252E02B1AF1BC1D006D0535 /* Resources */ = {
325 | isa = PBXResourcesBuildPhase;
326 | buildActionMask = 2147483647;
327 | files = (
328 | );
329 | runOnlyForDeploymentPostprocessing = 0;
330 | };
331 | 7252E0361AF1BC1E006D0535 /* Resources */ = {
332 | isa = PBXResourcesBuildPhase;
333 | buildActionMask = 2147483647;
334 | files = (
335 | );
336 | runOnlyForDeploymentPostprocessing = 0;
337 | };
338 | 7252E0671AF1BD61006D0535 /* Resources */ = {
339 | isa = PBXResourcesBuildPhase;
340 | buildActionMask = 2147483647;
341 | files = (
342 | 7252E0731AF1BD61006D0535 /* Main.storyboard in Resources */,
343 | 7252E0781AF1BD61006D0535 /* LaunchScreen.xib in Resources */,
344 | 7252E0751AF1BD61006D0535 /* Images.xcassets in Resources */,
345 | );
346 | runOnlyForDeploymentPostprocessing = 0;
347 | };
348 | /* End PBXResourcesBuildPhase section */
349 |
350 | /* Begin PBXSourcesBuildPhase section */
351 | 7252E0281AF1BC1D006D0535 /* Sources */ = {
352 | isa = PBXSourcesBuildPhase;
353 | buildActionMask = 2147483647;
354 | files = (
355 | 7252E0621AF1BCD1006D0535 /* MarkdownTextView.swift in Sources */,
356 | 7252E0631AF1BCD1006D0535 /* RegularExpressionHighlighter.swift in Sources */,
357 | 7252E0571AF1BCD0006D0535 /* HighlighterTextStorage.swift in Sources */,
358 | 7252E0641AF1BCD1006D0535 /* TextUtilities.swift in Sources */,
359 | 7252E05D1AF1BCD0006D0535 /* MarkdownLinkHighlighter.swift in Sources */,
360 | 7252E05A1AF1BCD0006D0535 /* MarkdownAttributes.swift in Sources */,
361 | 7252E05B1AF1BCD0006D0535 /* MarkdownFencedCodeHighlighter.swift in Sources */,
362 | 7252E05C1AF1BCD0006D0535 /* MarkdownHeaderHighlighter.swift in Sources */,
363 | 7252E0591AF1BCD0006D0535 /* LinkHighlighter.swift in Sources */,
364 | 7252E05E1AF1BCD0006D0535 /* MarkdownListHighlighter.swift in Sources */,
365 | 7252E0581AF1BCD0006D0535 /* HighlighterType.swift in Sources */,
366 | 7252E05F1AF1BCD1006D0535 /* MarkdownStrikethroughHighlighter.swift in Sources */,
367 | 7252E0611AF1BCD1006D0535 /* MarkdownTextStorage.swift in Sources */,
368 | 7252E0601AF1BCD1006D0535 /* MarkdownSuperscriptHighlighter.swift in Sources */,
369 | );
370 | runOnlyForDeploymentPostprocessing = 0;
371 | };
372 | 7252E0341AF1BC1E006D0535 /* Sources */ = {
373 | isa = PBXSourcesBuildPhase;
374 | buildActionMask = 2147483647;
375 | files = (
376 | 7252E0401AF1BC1E006D0535 /* MarkdownTextViewTests.swift in Sources */,
377 | );
378 | runOnlyForDeploymentPostprocessing = 0;
379 | };
380 | 7252E0651AF1BD61006D0535 /* Sources */ = {
381 | isa = PBXSourcesBuildPhase;
382 | buildActionMask = 2147483647;
383 | files = (
384 | 7252E0701AF1BD61006D0535 /* ViewController.swift in Sources */,
385 | 7252E06E1AF1BD61006D0535 /* AppDelegate.swift in Sources */,
386 | );
387 | runOnlyForDeploymentPostprocessing = 0;
388 | };
389 | /* End PBXSourcesBuildPhase section */
390 |
391 | /* Begin PBXTargetDependency section */
392 | 7252E03B1AF1BC1E006D0535 /* PBXTargetDependency */ = {
393 | isa = PBXTargetDependency;
394 | target = 7252E02C1AF1BC1D006D0535 /* MarkdownTextView */;
395 | targetProxy = 7252E03A1AF1BC1E006D0535 /* PBXContainerItemProxy */;
396 | };
397 | 7252E08C1AF1BD82006D0535 /* PBXTargetDependency */ = {
398 | isa = PBXTargetDependency;
399 | target = 7252E02C1AF1BC1D006D0535 /* MarkdownTextView */;
400 | targetProxy = 7252E08B1AF1BD82006D0535 /* PBXContainerItemProxy */;
401 | };
402 | /* End PBXTargetDependency section */
403 |
404 | /* Begin PBXVariantGroup section */
405 | 7252E0711AF1BD61006D0535 /* Main.storyboard */ = {
406 | isa = PBXVariantGroup;
407 | children = (
408 | 7252E0721AF1BD61006D0535 /* Base */,
409 | );
410 | name = Main.storyboard;
411 | sourceTree = "";
412 | };
413 | 7252E0761AF1BD61006D0535 /* LaunchScreen.xib */ = {
414 | isa = PBXVariantGroup;
415 | children = (
416 | 7252E0771AF1BD61006D0535 /* Base */,
417 | );
418 | name = LaunchScreen.xib;
419 | sourceTree = "";
420 | };
421 | /* End PBXVariantGroup section */
422 |
423 | /* Begin XCBuildConfiguration section */
424 | 7252E0411AF1BC1E006D0535 /* Debug */ = {
425 | isa = XCBuildConfiguration;
426 | buildSettings = {
427 | ALWAYS_SEARCH_USER_PATHS = NO;
428 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
429 | CLANG_CXX_LIBRARY = "libc++";
430 | CLANG_ENABLE_MODULES = YES;
431 | CLANG_ENABLE_OBJC_ARC = YES;
432 | CLANG_WARN_BOOL_CONVERSION = YES;
433 | CLANG_WARN_CONSTANT_CONVERSION = YES;
434 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
435 | CLANG_WARN_EMPTY_BODY = YES;
436 | CLANG_WARN_ENUM_CONVERSION = YES;
437 | CLANG_WARN_INT_CONVERSION = YES;
438 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
439 | CLANG_WARN_UNREACHABLE_CODE = YES;
440 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
441 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
442 | COPY_PHASE_STRIP = NO;
443 | CURRENT_PROJECT_VERSION = 1;
444 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
445 | ENABLE_STRICT_OBJC_MSGSEND = YES;
446 | ENABLE_TESTABILITY = YES;
447 | GCC_C_LANGUAGE_STANDARD = gnu99;
448 | GCC_DYNAMIC_NO_PIC = NO;
449 | GCC_NO_COMMON_BLOCKS = YES;
450 | GCC_OPTIMIZATION_LEVEL = 0;
451 | GCC_PREPROCESSOR_DEFINITIONS = (
452 | "DEBUG=1",
453 | "$(inherited)",
454 | );
455 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
456 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
457 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
458 | GCC_WARN_UNDECLARED_SELECTOR = YES;
459 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
460 | GCC_WARN_UNUSED_FUNCTION = YES;
461 | GCC_WARN_UNUSED_VARIABLE = YES;
462 | IPHONEOS_DEPLOYMENT_TARGET = 8.3;
463 | MTL_ENABLE_DEBUG_INFO = YES;
464 | ONLY_ACTIVE_ARCH = YES;
465 | SDKROOT = iphoneos;
466 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
467 | TARGETED_DEVICE_FAMILY = "1,2";
468 | VERSIONING_SYSTEM = "apple-generic";
469 | VERSION_INFO_PREFIX = "";
470 | };
471 | name = Debug;
472 | };
473 | 7252E0421AF1BC1E006D0535 /* Release */ = {
474 | isa = XCBuildConfiguration;
475 | buildSettings = {
476 | ALWAYS_SEARCH_USER_PATHS = NO;
477 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
478 | CLANG_CXX_LIBRARY = "libc++";
479 | CLANG_ENABLE_MODULES = YES;
480 | CLANG_ENABLE_OBJC_ARC = YES;
481 | CLANG_WARN_BOOL_CONVERSION = YES;
482 | CLANG_WARN_CONSTANT_CONVERSION = YES;
483 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
484 | CLANG_WARN_EMPTY_BODY = YES;
485 | CLANG_WARN_ENUM_CONVERSION = YES;
486 | CLANG_WARN_INT_CONVERSION = YES;
487 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
488 | CLANG_WARN_UNREACHABLE_CODE = YES;
489 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
490 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
491 | COPY_PHASE_STRIP = NO;
492 | CURRENT_PROJECT_VERSION = 1;
493 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
494 | ENABLE_NS_ASSERTIONS = NO;
495 | ENABLE_STRICT_OBJC_MSGSEND = YES;
496 | GCC_C_LANGUAGE_STANDARD = gnu99;
497 | GCC_NO_COMMON_BLOCKS = YES;
498 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
499 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
500 | GCC_WARN_UNDECLARED_SELECTOR = YES;
501 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
502 | GCC_WARN_UNUSED_FUNCTION = YES;
503 | GCC_WARN_UNUSED_VARIABLE = YES;
504 | IPHONEOS_DEPLOYMENT_TARGET = 8.3;
505 | MTL_ENABLE_DEBUG_INFO = NO;
506 | SDKROOT = iphoneos;
507 | TARGETED_DEVICE_FAMILY = "1,2";
508 | VALIDATE_PRODUCT = YES;
509 | VERSIONING_SYSTEM = "apple-generic";
510 | VERSION_INFO_PREFIX = "";
511 | };
512 | name = Release;
513 | };
514 | 7252E0441AF1BC1E006D0535 /* Debug */ = {
515 | isa = XCBuildConfiguration;
516 | buildSettings = {
517 | CLANG_ENABLE_MODULES = YES;
518 | DEFINES_MODULE = YES;
519 | DYLIB_COMPATIBILITY_VERSION = 1;
520 | DYLIB_CURRENT_VERSION = 1;
521 | DYLIB_INSTALL_NAME_BASE = "@rpath";
522 | INFOPLIST_FILE = MarkdownTextView/Info.plist;
523 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
524 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
525 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
526 | PRODUCT_NAME = "$(TARGET_NAME)";
527 | SKIP_INSTALL = YES;
528 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
529 | };
530 | name = Debug;
531 | };
532 | 7252E0451AF1BC1E006D0535 /* Release */ = {
533 | isa = XCBuildConfiguration;
534 | buildSettings = {
535 | CLANG_ENABLE_MODULES = YES;
536 | DEFINES_MODULE = YES;
537 | DYLIB_COMPATIBILITY_VERSION = 1;
538 | DYLIB_CURRENT_VERSION = 1;
539 | DYLIB_INSTALL_NAME_BASE = "@rpath";
540 | INFOPLIST_FILE = MarkdownTextView/Info.plist;
541 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
542 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
543 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
544 | PRODUCT_NAME = "$(TARGET_NAME)";
545 | SKIP_INSTALL = YES;
546 | };
547 | name = Release;
548 | };
549 | 7252E0471AF1BC1E006D0535 /* Debug */ = {
550 | isa = XCBuildConfiguration;
551 | buildSettings = {
552 | FRAMEWORK_SEARCH_PATHS = (
553 | "$(SDKROOT)/Developer/Library/Frameworks",
554 | "$(inherited)",
555 | );
556 | GCC_PREPROCESSOR_DEFINITIONS = (
557 | "DEBUG=1",
558 | "$(inherited)",
559 | );
560 | INFOPLIST_FILE = MarkdownTextViewTests/Info.plist;
561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
562 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
563 | PRODUCT_NAME = "$(TARGET_NAME)";
564 | };
565 | name = Debug;
566 | };
567 | 7252E0481AF1BC1E006D0535 /* Release */ = {
568 | isa = XCBuildConfiguration;
569 | buildSettings = {
570 | FRAMEWORK_SEARCH_PATHS = (
571 | "$(SDKROOT)/Developer/Library/Frameworks",
572 | "$(inherited)",
573 | );
574 | INFOPLIST_FILE = MarkdownTextViewTests/Info.plist;
575 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
576 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
577 | PRODUCT_NAME = "$(TARGET_NAME)";
578 | };
579 | name = Release;
580 | };
581 | 7252E0861AF1BD61006D0535 /* Debug */ = {
582 | isa = XCBuildConfiguration;
583 | buildSettings = {
584 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
585 | GCC_PREPROCESSOR_DEFINITIONS = (
586 | "DEBUG=1",
587 | "$(inherited)",
588 | );
589 | INFOPLIST_FILE = Example/Info.plist;
590 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
591 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
592 | PRODUCT_NAME = "$(TARGET_NAME)";
593 | };
594 | name = Debug;
595 | };
596 | 7252E0871AF1BD61006D0535 /* Release */ = {
597 | isa = XCBuildConfiguration;
598 | buildSettings = {
599 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
600 | INFOPLIST_FILE = Example/Info.plist;
601 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
602 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.$(PRODUCT_NAME:rfc1034identifier)";
603 | PRODUCT_NAME = "$(TARGET_NAME)";
604 | };
605 | name = Release;
606 | };
607 | /* End XCBuildConfiguration section */
608 |
609 | /* Begin XCConfigurationList section */
610 | 7252E0271AF1BC1D006D0535 /* Build configuration list for PBXProject "MarkdownTextView" */ = {
611 | isa = XCConfigurationList;
612 | buildConfigurations = (
613 | 7252E0411AF1BC1E006D0535 /* Debug */,
614 | 7252E0421AF1BC1E006D0535 /* Release */,
615 | );
616 | defaultConfigurationIsVisible = 0;
617 | defaultConfigurationName = Release;
618 | };
619 | 7252E0431AF1BC1E006D0535 /* Build configuration list for PBXNativeTarget "MarkdownTextView" */ = {
620 | isa = XCConfigurationList;
621 | buildConfigurations = (
622 | 7252E0441AF1BC1E006D0535 /* Debug */,
623 | 7252E0451AF1BC1E006D0535 /* Release */,
624 | );
625 | defaultConfigurationIsVisible = 0;
626 | defaultConfigurationName = Release;
627 | };
628 | 7252E0461AF1BC1E006D0535 /* Build configuration list for PBXNativeTarget "MarkdownTextViewTests" */ = {
629 | isa = XCConfigurationList;
630 | buildConfigurations = (
631 | 7252E0471AF1BC1E006D0535 /* Debug */,
632 | 7252E0481AF1BC1E006D0535 /* Release */,
633 | );
634 | defaultConfigurationIsVisible = 0;
635 | defaultConfigurationName = Release;
636 | };
637 | 7252E0851AF1BD61006D0535 /* Build configuration list for PBXNativeTarget "Example" */ = {
638 | isa = XCConfigurationList;
639 | buildConfigurations = (
640 | 7252E0861AF1BD61006D0535 /* Debug */,
641 | 7252E0871AF1BD61006D0535 /* Release */,
642 | );
643 | defaultConfigurationIsVisible = 0;
644 | defaultConfigurationName = Release;
645 | };
646 | /* End XCConfigurationList section */
647 | };
648 | rootObject = 7252E0241AF1BC1D006D0535 /* Project object */;
649 | }
650 |
--------------------------------------------------------------------------------
/MarkdownTextView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MarkdownTextView.xcodeproj/xcshareddata/xcschemes/MarkdownTextView.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/MarkdownTextView/HighlighterTextStorage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RegularExpressionTextStorage.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/28/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Text storage with support for automatically highlighting text
13 | * as it changes.
14 | */
15 | public class HighlighterTextStorage: NSTextStorage {
16 | private let backingStore: NSMutableAttributedString
17 | private var highlighters = [HighlighterType]()
18 |
19 | /// Default attributes to use for styling text.
20 | public var defaultAttributes: [String: AnyObject] = [
21 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
22 | ] {
23 | didSet { editedAll(.EditedAttributes) }
24 | }
25 |
26 | // MARK: API
27 |
28 | /**
29 | Adds a highlighter to use for highlighting text.
30 |
31 | Highlighters are invoked in the order in which they are added.
32 |
33 | :param: highlighter The highlighter to add.
34 | */
35 | public func addHighlighter(highlighter: HighlighterType) {
36 | highlighters.append(highlighter)
37 | editedAll(.EditedAttributes)
38 | }
39 |
40 | // MARK: Initialization
41 |
42 | public override init() {
43 | backingStore = NSMutableAttributedString(string: "", attributes: defaultAttributes)
44 | super.init()
45 | }
46 |
47 | required public init?(coder aDecoder: NSCoder) {
48 | backingStore = NSMutableAttributedString(string: "", attributes: defaultAttributes)
49 | super.init(coder: aDecoder)
50 | }
51 |
52 | // MARK: NSTextStorage
53 |
54 | public override var string: String {
55 | return backingStore.string
56 | }
57 |
58 | public override func attributesAtIndex(location: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] {
59 | return backingStore.attributesAtIndex(location, effectiveRange: range)
60 | }
61 |
62 | public override func replaceCharactersInRange(range: NSRange, withAttributedString attrString: NSAttributedString) {
63 | backingStore.replaceCharactersInRange(range, withAttributedString: attrString)
64 | edited(.EditedCharacters, range: range, changeInLength: attrString.length - range.length)
65 | }
66 |
67 | public override func setAttributes(attrs: [String : AnyObject]?, range: NSRange) {
68 | backingStore.setAttributes(attrs, range: range)
69 | edited(.EditedAttributes, range: range, changeInLength: 0)
70 | }
71 |
72 | public override func processEditing() {
73 | // This is inefficient but necessary because certain
74 | // edits can cause formatting changes that span beyond
75 | // line or paragraph boundaries. This should be alright
76 | // for small amounts of text (which is the use case that
77 | // this was designed for), but would need to be optimized
78 | // for any kind of heavy editing.
79 | highlightRange(NSRange(location: 0, length: (string as NSString).length))
80 | super.processEditing()
81 | }
82 |
83 | private func editedAll(actions: NSTextStorageEditActions) {
84 | edited(actions, range: NSRange(location: 0, length: backingStore.length), changeInLength: 0)
85 | }
86 |
87 | private func highlightRange(range: NSRange) {
88 | backingStore.beginEditing()
89 | setAttributes(defaultAttributes, range: range)
90 | let attrString = backingStore.attributedSubstringFromRange(range).mutableCopy() as! NSMutableAttributedString
91 | for highlighter in highlighters {
92 | highlighter.highlightAttributedString(attrString)
93 | }
94 | replaceCharactersInRange(range, withAttributedString: attrString)
95 | backingStore.endEditing()
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/MarkdownTextView/HighlighterType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownExtensionType.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Used with `HighlighterTextStorage` to add support for highlighting
13 | * text inside the text storage when the text changes.
14 | */
15 | public protocol HighlighterType {
16 | /**
17 | * Highlights the text in `attributedString`
18 | */
19 | func highlightAttributedString(attributedString: NSMutableAttributedString)
20 | }
21 |
--------------------------------------------------------------------------------
/MarkdownTextView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/MarkdownTextView/LinkHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LinkHighlighter.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Highlights URLs.
13 | */
14 | public final class LinkHighlighter: HighlighterType {
15 | private var detector: NSDataDetector!
16 |
17 | public init() throws {
18 | detector = try NSDataDetector(types: NSTextCheckingType.Link.rawValue)
19 | }
20 |
21 | // MARK: HighlighterType
22 |
23 | public func highlightAttributedString(attributedString: NSMutableAttributedString) {
24 | enumerateMatches(detector, string: attributedString.string) {
25 | if let URL = $0.URL {
26 | let linkAttributes = [
27 | NSLinkAttributeName: URL
28 | ]
29 | attributedString.addAttributes(linkAttributes, range: $0.range)
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownAttributes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownAttributes.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/28/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Encapsulates the attributes to use for styling various types
13 | * of Markdown elements.
14 | */
15 | public struct MarkdownAttributes {
16 | public var defaultAttributes: TextAttributes = [
17 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
18 | ]
19 |
20 | public var strongAttributes: TextAttributes?
21 | public var emphasisAttributes: TextAttributes?
22 |
23 | public struct HeaderAttributes {
24 | public var h1Attributes: TextAttributes? = [
25 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
26 | ]
27 |
28 | public var h2Attributes: TextAttributes? = [
29 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
30 | ]
31 |
32 | public var h3Attributes: TextAttributes? = [
33 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
34 | ]
35 |
36 | public var h4Attributes: TextAttributes? = [
37 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
38 | ]
39 |
40 | public var h5Attributes: TextAttributes? = [
41 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
42 | ]
43 |
44 | public var h6Attributes: TextAttributes? = [
45 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
46 | ]
47 |
48 | func attributesForHeaderLevel(level: Int) -> TextAttributes? {
49 | switch level {
50 | case 1: return h1Attributes
51 | case 2: return h2Attributes
52 | case 3: return h3Attributes
53 | case 4: return h4Attributes
54 | case 5: return h5Attributes
55 | case 6: return h6Attributes
56 | default: return nil
57 | }
58 | }
59 |
60 | public init() {}
61 | }
62 |
63 | public var headerAttributes: HeaderAttributes? = HeaderAttributes()
64 |
65 | private static let MonospaceFont: UIFont = {
66 | let bodyFont = UIFont.preferredFontForTextStyle(UIFontTextStyleBody)
67 | let size = bodyFont.pointSize
68 | return UIFont(name: "Menlo", size: size) ?? UIFont(name: "Courier", size: size) ?? bodyFont
69 | }()
70 |
71 | public var codeBlockAttributes: TextAttributes? = [
72 | NSFontAttributeName: MarkdownAttributes.MonospaceFont
73 | ]
74 |
75 | public var inlineCodeAttributes: TextAttributes? = [
76 | NSFontAttributeName: MarkdownAttributes.MonospaceFont
77 | ]
78 |
79 | public var blockQuoteAttributes: TextAttributes? = [
80 | NSForegroundColorAttributeName: UIColor.darkGrayColor()
81 | ]
82 |
83 | public var orderedListAttributes: TextAttributes? = [
84 | NSFontAttributeName: fontWithTraits(.TraitBold, font: UIFont.preferredFontForTextStyle(UIFontTextStyleBody))
85 | ]
86 |
87 | public var orderedListItemAttributes: TextAttributes? = [
88 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleBody),
89 | NSForegroundColorAttributeName: UIColor.darkGrayColor()
90 | ]
91 |
92 | public var unorderedListAttributes: TextAttributes? = [
93 | NSFontAttributeName: fontWithTraits(.TraitBold, font: UIFont.preferredFontForTextStyle(UIFontTextStyleBody))
94 | ]
95 |
96 | public var unorderedListItemAttributes: TextAttributes? = [
97 | NSFontAttributeName: UIFont.preferredFontForTextStyle(UIFontTextStyleBody),
98 | NSForegroundColorAttributeName: UIColor.darkGrayColor()
99 | ]
100 |
101 | public init() {}
102 | }
103 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownFencedCodeHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownFencedCodeHighlighter.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Highlights
13 | * ```
14 | * fenced code
15 | * ```
16 | * blocks in Markdown text.
17 | */
18 | public final class MarkdownFencedCodeHighlighter: RegularExpressionHighlighter {
19 | private static let FencedCodeRegex = regexFromPattern("^(`{3})(?:.*)?$\n[\\s\\S]*\n\\1$")
20 |
21 | /**
22 | Creates a new instance of the receiver.
23 |
24 | :param: attributes Attributes to apply to fenced code blocks.
25 |
26 | :returns: A new instance of the receiver.
27 | */
28 | public init(attributes: TextAttributes) {
29 | super.init(regularExpression: self.dynamicType.FencedCodeRegex, attributes: attributes)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownHeaderHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownHeaderHighlighter.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Highlights atx-style Markdown headers.
13 | */
14 | public final class MarkdownHeaderHighlighter: HighlighterType {
15 | // From markdown.pl v1.0.1
16 | private static let HeaderRegex = regexFromPattern("^(\\#{1,6})[ \t]*(?:.+?)[ \t]*\\#*\n+")
17 | private let attributes: MarkdownAttributes.HeaderAttributes
18 |
19 | /**
20 | Creates a new instance of the receiver.
21 |
22 | :param: attributes Attributes to apply to Markdown headers.
23 |
24 | :returns: An initialized instance of the receiver.
25 | */
26 | public init(attributes: MarkdownAttributes.HeaderAttributes) {
27 | self.attributes = attributes
28 | }
29 |
30 | // MARK: HighlighterType
31 |
32 | public func highlightAttributedString(attributedString: NSMutableAttributedString) {
33 | enumerateMatches(self.dynamicType.HeaderRegex, string: attributedString.string) {
34 | let level = $0.rangeAtIndex(1).length
35 | if let attributes = self.attributes.attributesForHeaderLevel(level) {
36 | attributedString.addAttributes(attributes, range: $0.range)
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownLinkHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownLinkHighlighter.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Highlights Markdown links (not including link references)
13 | */
14 | public final class MarkdownLinkHighlighter: HighlighterType {
15 | // From markdown.pl v1.0.1
16 | private static let LinkRegex = regexFromPattern("\\[([^\\[]+)\\]\\([ \t]*(.*?)>?[ \t]*((['\"])(.*?)\\4)?\\)")
17 |
18 | // MARK: HighlighterType
19 |
20 | public func highlightAttributedString(attributedString: NSMutableAttributedString) {
21 | let string = attributedString.string
22 | enumerateMatches(self.dynamicType.LinkRegex, string: string) {
23 | let URLString = (string as NSString).substringWithRange($0.rangeAtIndex(2))
24 | let linkAttributes = [
25 | NSLinkAttributeName: URLString
26 | ]
27 | attributedString.addAttributes(linkAttributes, range: $0.range)
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownListHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownListHighlighter.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Highlights Markdown lists using specifiable marker patterns.
13 | */
14 | public final class MarkdownListHighlighter: HighlighterType {
15 | private let regularExpression: NSRegularExpression
16 | private let attributes: TextAttributes?
17 | private let itemAttributes: TextAttributes?
18 |
19 | /**
20 | Creates a new instance of the receiver.
21 |
22 | :param: markerPattern Regular expression pattern to use for matching
23 | list markers.
24 | :param: attributes Attributes to apply to the entire list.
25 | :param: itemAttributes Attributes to apply to list items (excluding
26 | list markers)
27 |
28 | :returns: An initialized instance of the receiver.
29 | */
30 | public init(markerPattern: String, attributes: TextAttributes?, itemAttributes: TextAttributes?) {
31 | self.regularExpression = listItemRegexWithMarkerPattern(markerPattern)
32 | self.attributes = attributes
33 | self.itemAttributes = itemAttributes
34 | }
35 |
36 | // MARK: HighlighterType
37 |
38 | public func highlightAttributedString(attributedString: NSMutableAttributedString) {
39 | if (attributes == nil && itemAttributes == nil) { return }
40 |
41 | enumerateMatches(regularExpression, string: attributedString.string) {
42 | if let attributes = self.attributes {
43 | attributedString.addAttributes(attributes, range: $0.range)
44 | }
45 | if let itemAttributes = self.itemAttributes {
46 | attributedString.addAttributes(itemAttributes, range: $0.rangeAtIndex(1))
47 | }
48 | }
49 | }
50 | }
51 |
52 | private func listItemRegexWithMarkerPattern(pattern: String) -> NSRegularExpression {
53 | // From markdown.pl v1.0.1
54 | return regexFromPattern("^(?:[ ]{0,3}(?:\(pattern))[ \t]+)(.+)\n")
55 | }
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownStrikethroughHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownStrikethroughHighlighter.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Highlights ~~strikethrough~~ in Markdown text (unofficial extension)
13 | */
14 | public final class MarkdownStrikethroughHighlighter: HighlighterType {
15 | private static let StrikethroughRegex = regexFromPattern("(~~)(?=\\S)(.+?)(?<=\\S)\\1")
16 | private let attributes: TextAttributes?
17 |
18 | /**
19 | Creates a new instance of the receiver.
20 |
21 | :param: attributes Optional additional attributes to apply
22 | to strikethrough text.
23 |
24 | :returns: An initialized instance of the receiver.
25 | */
26 | public init(attributes: TextAttributes? = nil) {
27 | self.attributes = attributes
28 | }
29 |
30 | // MARK: HighlighterType
31 |
32 | public func highlightAttributedString(attributedString: NSMutableAttributedString) {
33 | enumerateMatches(self.dynamicType.StrikethroughRegex, string: attributedString.string) {
34 | var strikethroughAttributes: TextAttributes = [
35 | NSStrikethroughStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue
36 | ]
37 | if let attributes = self.attributes {
38 | for (key, value) in attributes {
39 | strikethroughAttributes[key] = value
40 | }
41 | }
42 | attributedString.addAttributes(strikethroughAttributes, range: $0.rangeAtIndex(2))
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownSuperscriptHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownSuperscriptHighlighter.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Highlights super^script in Markdown text (unofficial extension)
13 | */
14 | public final class MarkdownSuperscriptHighlighter: HighlighterType {
15 | private static let SuperscriptRegex = regexFromPattern("(\\^+)(?:(?:[^\\^\\s\\(][^\\^\\s]*)|(?:\\([^\n\r\\)]+\\)))")
16 | private let fontSizeRatio: CGFloat
17 |
18 | /**
19 | Creates a new instance of the receiver.
20 |
21 | :param: fontSizeRatio Ratio to multiply the original font
22 | size by to calculate the superscript font size.
23 |
24 | :returns: An initialized instance of the receiver.
25 | */
26 | public init(fontSizeRatio: CGFloat = 0.7) {
27 | self.fontSizeRatio = fontSizeRatio
28 | }
29 |
30 | // MARK: HighlighterType
31 |
32 | public func highlightAttributedString(attributedString: NSMutableAttributedString) {
33 | var previousRange: NSRange?
34 | var level: Int = 0
35 |
36 | enumerateMatches(self.dynamicType.SuperscriptRegex, string: attributedString.string) {
37 | level += $0.rangeAtIndex(1).length
38 | let textRange = $0.range
39 | let attributes = attributedString.attributesAtIndex(textRange.location, effectiveRange: nil)
40 |
41 | let isConsecutiveRange: Bool = {
42 | if let previousRange = previousRange where NSMaxRange(previousRange) == textRange.location {
43 | return true
44 | }
45 | return false
46 | }()
47 | if isConsecutiveRange {
48 | level++
49 | }
50 |
51 | attributedString.addAttributes(superscriptAttributes(attributes, level: level, ratio: self.fontSizeRatio), range: textRange)
52 | previousRange = textRange
53 |
54 | if !isConsecutiveRange {
55 | level = 0
56 | }
57 | }
58 | }
59 | }
60 |
61 | private func superscriptAttributes(attributes: TextAttributes, level: Int, ratio: CGFloat) -> TextAttributes {
62 | if let font = attributes[NSFontAttributeName] as? UIFont {
63 | let adjustedFont = UIFont(descriptor: font.fontDescriptor(), size: font.pointSize * ratio)
64 | return [
65 | kCTSuperscriptAttributeName as String: level,
66 | NSFontAttributeName: adjustedFont
67 | ]
68 | }
69 | return [:]
70 | }
71 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownTextStorage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownTextStorage.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/28/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Text storage with support for highlighting Markdown.
13 | */
14 | public class MarkdownTextStorage: HighlighterTextStorage {
15 | private let attributes: MarkdownAttributes
16 |
17 | // MARK: Initialization
18 |
19 | /**
20 | Creates a new instance of the receiver.
21 |
22 | :param: attributes Attributes used to style the text.
23 |
24 | :returns: An initialized instance of `MarkdownTextStorage`
25 | */
26 | public init(attributes: MarkdownAttributes = MarkdownAttributes()) {
27 | self.attributes = attributes
28 | super.init()
29 | commonInit()
30 |
31 | if let headerAttributes = attributes.headerAttributes {
32 | addHighlighter(MarkdownHeaderHighlighter(attributes: headerAttributes))
33 | }
34 | addHighlighter(MarkdownLinkHighlighter())
35 | addHighlighter(MarkdownListHighlighter(markerPattern: "[*+-]", attributes: attributes.unorderedListAttributes, itemAttributes: attributes.unorderedListItemAttributes))
36 | addHighlighter(MarkdownListHighlighter(markerPattern: "\\d+[.]", attributes: attributes.orderedListAttributes, itemAttributes: attributes.orderedListItemAttributes))
37 |
38 | // From markdown.pl v1.0.1
39 |
40 | // Code blocks
41 | addPattern("(?:\n\n|\\A)((?:(?:[ ]{4}|\t).*\n+)+)((?=^[ ]{0,4}\\S)|\\Z)", attributes.codeBlockAttributes)
42 |
43 | // Block quotes
44 | addPattern("(?:^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+", attributes.blockQuoteAttributes)
45 |
46 | // Se-text style headers
47 | // H1
48 | addPattern("^(?:.+)[ \t]*\n=+[ \t]*\n+", attributes.headerAttributes?.h1Attributes)
49 |
50 | // H2
51 | addPattern("^(?:.+)[ \t]*\n-+[ \t]*\n+", attributes.headerAttributes?.h2Attributes)
52 |
53 | // Emphasis
54 | addPattern("(\\*|_)(?=\\S)(.+?)(?<=\\S)\\1", attributesForTraits(.TraitItalic, attributes.emphasisAttributes))
55 |
56 | // Strong
57 | addPattern("(\\*\\*|__)(?=\\S)(?:.+?[*_]*)(?<=\\S)\\1", attributesForTraits(.TraitBold, attributes.strongAttributes))
58 |
59 | // Inline code
60 | addPattern("(`+)(?:.+?)(? TextAttributes? {
83 | if let defaultFont = defaultAttributes[NSFontAttributeName] as? UIFont where attributes == nil {
84 | attributes = [
85 | NSFontAttributeName: fontWithTraits(traits, font: defaultFont)
86 | ]
87 | }
88 | return attributes
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownTextView.h:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownTextView.h
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for MarkdownTextView.
12 | FOUNDATION_EXPORT double MarkdownTextViewVersionNumber;
13 |
14 | //! Project version string for MarkdownTextView.
15 | FOUNDATION_EXPORT const unsigned char MarkdownTextViewVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/MarkdownTextView/MarkdownTextView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownTextView.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Text view with support for highlighting Markdown syntax.
13 | */
14 | public class MarkdownTextView: UITextView {
15 | /**
16 | Creates a new instance of the receiver.
17 |
18 | :param: frame The view frame.
19 | :param: textStorage The text storage. This can be customized by the
20 | caller to customize text attributes and add additional highlighters
21 | if the defaults are not suitable.
22 |
23 | :returns: An initialized instance of the receiver.
24 | */
25 | public init(frame: CGRect, textStorage: MarkdownTextStorage = MarkdownTextStorage()) {
26 | let textContainer = NSTextContainer()
27 | let layoutManager = NSLayoutManager()
28 | layoutManager.addTextContainer(textContainer)
29 | textStorage.addLayoutManager(layoutManager)
30 |
31 | super.init(frame: frame, textContainer: textContainer)
32 | }
33 |
34 | required public init(coder aDecoder: NSCoder) {
35 | fatalError("init(coder:) has not been implemented")
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/MarkdownTextView/RegularExpressionHighlighter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RegularExpressionHighlighter.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | * Highlighter that uses a regular expression to match character
13 | * sequences to highlight.
14 | */
15 | public class RegularExpressionHighlighter: HighlighterType {
16 | private let regularExpression: NSRegularExpression
17 | private let attributes: TextAttributes
18 |
19 | /**
20 | Creates a new instance of the receiver.
21 |
22 | :param: regularExpression The regular expression to use for
23 | matching text to highlight.
24 | :param: attributes The attributes applied to matching
25 | text ranges.
26 |
27 | :returns: An initialized instance of the receiver.
28 | */
29 | public init(regularExpression: NSRegularExpression, attributes: TextAttributes) {
30 | self.regularExpression = regularExpression
31 | self.attributes = attributes
32 | }
33 |
34 | // MARK: HighlighterType
35 |
36 | public func highlightAttributedString(attributedString: NSMutableAttributedString) {
37 | enumerateMatches(regularExpression, string: attributedString.string) {
38 | attributedString.addAttributes(self.attributes, range: $0.range)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/MarkdownTextView/TextUtilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextUtilities.swift
3 | // MarkdownTextView
4 | //
5 | // Created by Indragie on 4/28/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public typealias TextAttributes = [String: AnyObject]
12 |
13 | internal func fontWithTraits(traits: UIFontDescriptorSymbolicTraits, font: UIFont) -> UIFont {
14 | let combinedTraits = UIFontDescriptorSymbolicTraits(rawValue: font.fontDescriptor().symbolicTraits.rawValue | (traits.rawValue & 0xFFFF))
15 | let descriptor = font.fontDescriptor().fontDescriptorWithSymbolicTraits(combinedTraits)
16 | return UIFont(descriptor: descriptor, size: font.pointSize)
17 | }
18 |
19 | internal func regexFromPattern(pattern: String) -> NSRegularExpression {
20 | do {
21 | return try NSRegularExpression(pattern: pattern, options: .AnchorsMatchLines)
22 | } catch let error {
23 | fatalError("Error constructing regular expression: \(error)")
24 | }
25 | }
26 |
27 | internal func enumerateMatches(regex: NSRegularExpression, string: String, block: NSTextCheckingResult -> Void) {
28 | let range = NSRange(location: 0, length: (string as NSString).length)
29 | regex.enumerateMatchesInString(string, options: NSMatchingOptions(rawValue: 0), range: range) { (result, _, _) in
30 | if let result = result {
31 | block(result)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/MarkdownTextViewTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/MarkdownTextViewTests/MarkdownTextViewTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MarkdownTextViewTests.swift
3 | // MarkdownTextViewTests
4 | //
5 | // Created by Indragie on 4/29/15.
6 | // Copyright (c) 2015 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import XCTest
11 |
12 | class MarkdownTextViewTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | XCTAssert(true, "Pass")
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measureBlock() {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## MarkdownTextView
2 | ### Rich Markdown Editing for iOS
3 |
4 | **MarkdownTextView** is an iOS framework for adding rich Markdown editing capabilities. Support for Markdown syntax is implemented inside an easily extensible `NSTextStorage` subclass, with a `UITextView` subclass being provided for convenience.
5 |
6 | ### Screenshot
7 |
8 |
9 |
10 | ### Example App
11 |
12 | Check out the includeded Example app to try out the text view and to see how **MarkdownTextView** is integrated into the project.
13 |
14 | ### Installation
15 |
16 | ###### With [CocoaPods](https://cocoapods.org/):
17 | ```ruby
18 | pod "MarkdownTextView"
19 | ```
20 |
21 | ###### With [Carthage](https://github.com/Carthage/Carthage):
22 | ```swift
23 | github "indragiek/MarkdownTextView"
24 | ```
25 |
26 | ### Getting Started
27 |
28 | The simplest possible usage is as follows:
29 |
30 | ```swift
31 | let textView = MarkdownTextView(frame: CGRectZero)
32 | view.addSubview(textView)
33 | ```
34 |
35 | This gives you a text view with support for most of the features defined in the original Markdown implementation (strong, emphasis, inline code, code blocks, block quotes, headers) with the default styling provided by the framework.
36 |
37 |
38 | ### Customizing Appearance
39 |
40 | All of the styling can be customized using standard `NSAttributedString` attributes. For example, if you wanted to customize bold text such that it appeared red, you would do this:
41 |
42 | ```swift
43 | var attributes = MarkdownTextAttributes()
44 | attributes.strongAttributes = [
45 | NSForegroundColorAttributeName: UIColor.redColor()
46 | ]
47 | let textStorage = MarkdownTextStorage(attributes: attributes)
48 | let textView = MarkdownTextView(frame: CGRectZero, textStorage: textStorage)
49 | view.addSubview(textView)
50 | ```
51 |
52 | ### Extensions Support
53 |
54 | Extension classes conforming to the `HighlighterType` protocol can be used to add support for unofficial Markdown extensions. The framework comes with the following extensions already implemented:
55 |
56 | From [Github Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/):
57 |
58 | * `MarkdownStrikethroughHighlighter` - Support for `~~strikethrough~~`
59 | * `MarkdownFencedCodeHighlighter` - Support for fenced code blocks
60 | * `LinkHighlighter` - Support for auto-linking
61 |
62 | Other:
63 |
64 | * `MarkdownSuperscriptHighlighter` - Support for `super^scripted^text`
65 |
66 | These extensions do not come activated by default. They must manually be added to an instance of `MarkdownTextStorage` as follows:
67 |
68 | ```swift
69 | let textStorage = MarkdownTextStorage()
70 | var error: NSError?
71 | if let linkHighlighter = LinkHighlighter(errorPtr: &error) {
72 | textStorage.addHighlighter(linkHighlighter)
73 | } else {
74 | assertionFailure("Error initializing LinkHighlighter: \(error)")
75 | }
76 | textStorage.addHighlighter(MarkdownStrikethroughHighlighter())
77 | textStorage.addHighlighter(MarkdownSuperscriptHighlighter())
78 | if let codeBlockAttributes = attributes.codeBlockAttributes {
79 | textStorage.addHighlighter(MarkdownFencedCodeHighlighter(attributes: codeBlockAttributes))
80 | }
81 |
82 | let textView = MarkdownTextView(frame: CGRectZero, textStorage: textStorage)
83 | view.addSubview(textView)
84 | ```
85 |
86 | ### Credits
87 |
88 | * John Gruber's [original Markdown implementation](http://daringfireball.net/projects/markdown/) for most of the regular expressions used in this project.
89 | * [RFMarkdownTextView](https://github.com/ruddfawcett/RFMarkdownTextView) for the idea to implement this as an `NSTextStorage` subclass
90 |
91 | ### Contact
92 |
93 | * Indragie Karunaratne
94 | * [@indragie](http://twitter.com/indragie)
95 | * [http://indragie.com](http://indragie.com)
96 |
97 | ### License
98 |
99 | MarkdownTextView is licensed under the MIT License. See `LICENSE` for more information.
100 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/MarkdownTextView/a101c31ccb4fbfa063d903dec9ff9416f632f9ab/screenshot.png
--------------------------------------------------------------------------------