• Stars
    star
    1,200
  • Rank 39,023 (Top 0.8 %)
  • Language
    Objective-C
  • License
    MIT License
  • Created almost 10 years ago
  • Updated almost 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Markdown parsing and rendering for iOS and OS X

CocoaMarkdown

Markdown parsing and rendering for iOS and macOS

CocoaMarkdown is a cross-platform framework for parsing and rendering Markdown, built on top of the C reference implementation of CommonMark.

Why?

CocoaMarkdown aims to solve two primary problems better than existing libraries:

  1. More flexibility. CocoaMarkdown allows you to define custom parsing hooks or even traverse the Markdown AST using the low-level API.
  2. Efficient NSAttributedString creation for easy rendering on iOS and macOS. Most existing libraries just generate HTML from the Markdown, which is not a convenient representation to work with in native apps.

Example app macOS

Example app iOS

Installation

First you will want to add this project as a submodule to your project:

git submodule add https://github.com/indragiek/CocoaMarkdown.git

Then, you need to pull down all of its dependencies.

cd CocoaMarkdown
git submodule update --init --recursive

Next, drag the .xcodeproj file from within CocoaMarkdown into your project. After that, click on the General tab of your target. Select the plus button under "Embedded Binaries" and select the CocoaMarkdown.framework.

API

Traversing the Markdown AST

CMNode and CMIterator wrap CommonMark's C types with an object-oriented interface for traversal of the Markdown AST.

let document = CMDocument(contentsOfFile: path, options: [])
document.rootNode.iterator().enumerateUsingBlock { (node, _, _) in
    print("String value: \(node.stringValue)")
}

Building Custom Renderers

The CMParser class isn't really a parser (it just traverses the AST), but it defines an NSXMLParser-style delegate API that provides handy callbacks for building your own renderers:

@protocol CMParserDelegate <NSObject>
@optional
- (void)parserDidStartDocument:(CMParser *)parser;
- (void)parserDidEndDocument:(CMParser *)parser;
...
- (void)parser:(CMParser *)parser foundText:(NSString *)text;
- (void)parserFoundHRule:(CMParser *)parser;
...
@end

CMAttributedStringRenderer is an example of a custom renderer that is built using this API.

Rendering Attributed Strings

CMAttributedStringRenderer is the high level API that will be useful to most apps. It creates an NSAttributedString directly from Markdown, skipping the step of converting it to HTML altogether.

Going from a Markdown document to rendering it on screen is as easy as:

let document = CMDocument(contentsOfFile: path, options: [])
let renderer = CMAttributedStringRenderer(document: document, attributes: CMTextAttributes())
textView.attributedText = renderer.render()

Or, using the convenience method on CMDocument:

textView.attributedText = CMDocument(contentsOfFile: path, options: []).attributedStringWithAttributes(CMTextAttributes())

HTML elements can be supported by implementing CMHTMLElementTransformer. The framework includes several transformers for commonly used tags:

Transformers can be registered with the renderer to use them:

let document = CMDocument(contentsOfFile: path, options: [])
let renderer = CMAttributedStringRenderer(document: document, attributes: CMTextAttributes())
renderer.registerHTMLElementTransformer(CMHTMLStrikethroughTransformer())
renderer.registerHTMLElementTransformer(CMHTMLSuperscriptTransformer())
textView.attributedText = renderer.render()

Customizing Attributed strings rendering

All attributes used to style the text are customizable using the CMTextAttributes class.

Every Markdown element type can be customized using the corresponding CMStyleAttributes property in CMTextAttributes, defining 3 different kinds of attributes:

  • String attributes, i.e. regular NSAttributedString attributes
  • Font attributes, for easy font setting
  • Paragraph attributes, relevant only for block elements

Attributes for any Markdown element kind can be directly set:

let textAttributes = CMTextAttributes()
textAttributes.linkAttributes.stringAttributes[NSAttributedString.Key.backgroundColor] = UIColor.yellow

A probably better alternative for style customization is to use grouped attributes setting methods available in CMTextAttributes:

let textAttributes = CMTextAttributes()

// Set the text color for all headers
textAttributes.addStringAttributes([ .foregroundColor: UIColor(red: 0.0, green: 0.446, blue: 0.657, alpha: 1.0)], 
                                   forElementWithKinds: .anyHeader)

// Set a specific font + font-traits for all headers
let boldItalicTrait: UIFontDescriptor.SymbolicTraits = [.traitBold, .traitItalic]
textAttributes.addFontAttributes([ .family: "Avenir Next" ,
                                   .traits: [ UIFontDescriptor.TraitKey.symbolic: boldItalicTrait.rawValue]], 
                                 forElementWithKinds: .anyHeader)
// Set specific font traits for header1 and header2
textAttributes.setFontTraits([.weight: UIFont.Weight.heavy], 
                             forElementWithKinds: [.header1, .header2])

// Center block-quote paragraphs        
textAttributes.addParagraphStyleAttributes([ .alignment: NSTextAlignment.center.rawValue], 
                                           forElementWithKinds: .blockQuote)

// Set a background color for code elements        
textAttributes.addStringAttributes([ .backgroundColor: UIColor(white: 0.9, alpha: 0.5)], 
                                   forElementWithKinds: [.inlineCode, .codeBlock])

List styles can be customized using dedicated paragraph style attributes:

// Customize the list bullets
textAttributes.addParagraphStyleAttributes([ .listItemBulletString: "🍏" ], 
                                           forElementWithKinds: .unorderedList)
textAttributes.addParagraphStyleAttributes([ .listItemBulletString: "🌼" ], 
                                           forElementWithKinds: .unorderedSublist)

// Customize numbered list item labels format and distance between label and paragraph
textAttributes.addParagraphStyleAttributes([ .listItemNumberFormat: "(%02ld)", 
                                             .listItemLabelIndent: 30 ],    
                                           forElementWithKinds: .orderedList)

Font and paragraph attributes are incremental, meaning that they allow to modify only specific aspects of the default rendering styles.

Additionally on iOS, Markdown elements styled using the font attributes API get automatic Dynamic-Type compliance in the generated attributed string, just like default rendering styles.

Rendering HTML

CMHTMLRenderer provides the ability to render HTML from Markdown:

let document = CMDocument(contentsOfFile: path, options: [])
let renderer = CMHTMLRenderer(document: document)
let HTML = renderer.render()

Or, using the convenience method on CMDocument:

let HTML = CMDocument(contentsOfFile: path).HTMLString()

Example Apps

The project includes example apps for iOS and macOS to demonstrate rendering attributed strings.

Contact

License

CocoaMarkdown is licensed under the MIT License. See LICENSE for more information.

More Repositories

1

InAppViewDebugger

A UIView debugger (like Reveal or Xcode) that can be embedded in an app for on-device view debugging
Swift
1,871
star
2

INAppStoreWindow

NSWindow subclass with a highly customizable title bar and traffic lights
Objective-C
1,064
star
3

DominantColor

Finding dominant colors of an image using k-means clustering
Swift
959
star
4

MarkdownTextView

Rich Markdown editing control for iOS
Swift
686
star
5

SwiftAutoLayout

Tiny Swift DSL for Autolayout
Swift
655
star
6

SNRHUDKit

Code drawn AppKit HUD interface elements
Objective-C
326
star
7

swiftrsrc

Resource code generation tool for Swift
Swift
290
star
8

INDANCSClient

Objective-C Apple Notification Center Service Client
Objective-C
252
star
9

INPopoverController

A customizable popover controller for OS X
Objective-C
196
star
10

SNRSearchIndex

SearchKit backed search for Core Data
Objective-C
190
star
11

Unzip

iOS 8 Action Extension for browsing ZIP files
Objective-C
165
star
12

NSUserNotificationPrivate

Private API showcase for NSUserNotification on OS X
Objective-C
152
star
13

SNRMusicKit

All-in-one framework for browsing and playing music from various sources on iOS and OS X
Objective-C
150
star
14

WWDC-2014

Scholarship submission for WWDC 2014
Objective-C
146
star
15

Ares

Zero-setup P2P file transfer between Macs and iOS devices
Swift
133
star
16

INDockableWindow

A window to which other views can be "docked" to and separated into their own windows
Objective-C
114
star
17

INDLinkLabel

A simple, no frills UILabel subclass with support for links
Swift
82
star
18

SNRFetchedResultsController

Automatic Core Data change tracking for OS X (NSFetchedResultsController port)
Objective-C
81
star
19

INDSequentialTextSelectionManager

Sequential text selection for NSTextViews
Objective-C
74
star
20

OEGridView

High performance Core Animation-based grid view, originally from OpenEmu.
Objective-C
72
star
21

INDGIFPreviewDownloader

[iOS] Retrieves preview images for GIFs by downloading only the first frame
Objective-C
60
star
22

Chip8

CHIP-8 emulator and disassembler written in Swift
Swift
53
star
23

pdfcat

OS X utility for concatenating PDF files
Swift
49
star
24

ReactiveXPC

Signals across process boundaries
Swift
36
star
25

ObjectiveKVDB

Objective-C wrapper for kvdb (https://github.com/dinhviethoa/kvdb)
Objective-C
33
star
26

SwiftTableViews

Type-safe Table Views with Swift
Swift
33
star
27

AlamofireRACExtensions

ReactiveCocoa Swift extensions for Alamofire
Swift
32
star
28

Dial

The beginnings of a replacement Contacts app for iOS.
Objective-C
30
star
29

ReactiveBLE

ReactiveCocoa wrapper for communicating with BLE devices using CoreBluetooth
Objective-C
29
star
30

INSOCKSServer

SOCKS5 proxy server implementation in Objective-C
Objective-C
27
star
31

LiveWebPreview

Web development tool for automatically refreshing a page when the content changes.
Objective-C
20
star
32

SNRLastFMEngine

[DEPRECATED] A modern block-based Objective-C interface to the Last.fm API
Objective-C
19
star
33

AttributedString.swift

Swift library that adds type safety and string interpolation support to NSAttributedString
Swift
18
star
34

INKeychainAccess

[DEPRECATED] Objective-C Keychain Services Wrapper for OS X and iOS
Objective-C
17
star
35

tecs

Projects for The Elements of Computing Systems by Nisan and Schocken
Assembly
15
star
36

INTrafficLightsDisabler

SIMBL plugin to hide the traffic lights on Mac OS X
Objective-C
14
star
37

pebble-lifx

Pebble controller for LIFX bulbs. UAlberta CompE Club Hackathon 2014 project.
Objective-C
11
star
38

xcode-themes

Color themes for Xcode 4 and 5
6
star
39

radars

Apple Radars filed for OS X and iOS.
Objective-C
5
star
40

indragiek.github.com

http://indragie.com
HTML
3
star
41

arduino-copter

Copter game for Arduino + Adafruit TFT
C++
2
star
42

writing

Things that I write
2
star
43

advent-of-code-2020

My Rust solutions for https://adventofcode.com
Rust
1
star
44

bootstrap-no-responsive

Compiled Bootstrap with responsive features disabled
1
star