Swiftpack.co - Package - M2mobi/Marky-Mark

Marky Mark

build status codecov Marky Mark is a parser written in Swift that converts markdown into native views. The way it looks it highly customizable and the supported markdown syntax is easy to extend.

Screenshot Screenshot Screenshot

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

  • iOS 8.0+
  • Xcode 8.0+

Installation

CocoaPods 1.0.0+ is required to build MarkyMark

To integrate MarkyMark into your Xcode project using CocoaPods, specify it in your Podfile:

pod "markymark"

Alternatively, add MarkyMark to your project using Swift Package Manager using:

https://github.com/M2Mobi/Marky-Mark

Simple usage

View with default styling

let markDownView = MarkDownTextView()
markDownView.text = "# Header\nParagraph"

View with modified styling

Markymark has many styling options, please check the examples in the styling section of this readme. A simple example:

let markDownView = MarkDownTextView()
markDownView.styling.headingStyling.textColorsForLevels = [
	.orange, //H1 (i.e. # Title)
	.black,  //H2, ... (i.e. ## Subtitle, ### Sub subtitle)
]

markDownView.styling.linkStyling.textColor = .blue
markDownView.styling.paragraphStyling.baseFont = .systemFont(ofSize: 14)
markDownView.text = "# Header\nParagraph"

Supported tags in the Default Flavor

Note: Different tags can be supported by either extending the ContentfulFlavor (default) or by implementing a class that comforms to Flavor and implement the required Rule's

Headings
# H1
## H2
### H3
#### H4
##### H5
###### H6

Lists
- item
	- item
* item
	* item
+ item
	+ item
a. item
b. item
1. item
2. item

Emphasis
*Em*
_Em_
**Strong**
__Strong__
~~Strike through~~

Images
![Alternative text](https://raw.github.com/M2mobi/Marky-Mark/blob/master/image.png)

Links
[Link text](https://www.example.net)

Code 
`code`
\```code```

Customizing default style

Default Styling instance

var styling = DefaultStyling()

Paragraphs (regular text)

Markdown example: Some text

styling.paragraphStyling.baseFont = .systemFont(ofSize: 14)
styling.paragraphStyling.textColor = .black
styling.paragraphStyling.contentInsets = UIEdgeInsets(top:0, left: 0, bottom: 5, right: 0)  
styling.paragraphStyling.lineHeight = 4
styling.paragraphStyling.isBold = false
styling.paragraphStyling.isItalic = false
styling.paragraphStyling.textAlignment = .left

Headings

Markdown example: # Title or ## Subtitle etc.

styling.headingStyling.fontsForLevels = [
	UIFont.boldSystemFontOfSize(24), //H1
	UIFont.systemFontOfSize(18),     //H2
	UIFont.systemFontOfSize(16)      //H3, ... (last item will be next levels as well)
]

styling.headingStyling.colorsForLevels = [
	.red, //H1
	.black, //H2, ... (last item will be next levels as well)
]

// Margins
styling.headingStyling.contentInsetsForLevels = [
	UIEdgeInsets(top: 5, left: 0, bottom: 15, right: 10), // H1
	UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 10) //H2, ... (last item will be next levels as well)
]

styling.headingStyling.isBold = false
styling.headingStyling.isItalic = false
styling.headingStyling.isUnderlined = false
styling.headingStyling.textAlignment = .left

linkStyling

Markdown Example [Google](http://www.google.com)

styling.linkStyling.textColor = .black
styling.linkStyling.baseFont = nil // Default: nil. Setting baseFont to nil will inherit font from paragraphStyling

styling.linkStyling.isBold = false
styling.linkStyling.isItalic = false
styling.linkStyling.isUnderlined = true

List styling

Markdown Example:

- List item 1
- List item 2
- Nested List item
// By default a font will be used with the bullet character `•`. Use the follow properties to configure it's size and color:
styling.listStyling.bulletFont = .systemFont(ofSize: 14)
styling.listStyling.bulletColor = .black

// Bullets can also be images for more complex styling. When setting images, bullet font and color won't be used anymore
// Array of images used as bullet for each level of nested list items
styling.listStyling.bulletImages = [
	UIImage(named: "circle"),
	UIImage(named: "emptyCircle"),
	UIImage(named: "line"),
	UIImage(named: "square")
]

// Size of the images
styling.listStyling.bulletViewSize = CGSize(width: 16, height: 16)

styling.listStyling.baseFont = .systemFont(ofSize: 14)
styling.listStyling.contentInsets = UIEdgeInsets(top: 0, left:  0, bottom: 10, right: 10)

//Amount of space underneath each list item
styling.listStyling.bottomListItemSpacing = 5

// Number of pixels to indent for each nested list level
styling.listStyling.listIdentSpace = 15

styling.listStyling.textColor = .black

Styling is also possible for:

styling.paragraphStyling
styling.italicStyling
styling.boldStyling
styling.strikeThroughStyling
styling.imageStyling
styling.linkStyling
styling.horizontalLineStyling
styling.codeBlockStyling
styling.inlineCodeBlockStyling
styling.quoteStyling

Please check the DefaultStyling class for more information

Advanced usage

Advanced usage is only needed for very specific cases. Making subsets of styling, making different styling combinations, supporting different Markdown rules (syntax) or modifying certain views after that have been generated.

Custom styling objects

struct CustomMarkyMarkStyling: Styling {
	var headerStyling = CustomHeaderStyling()
	var paragraphStyling = ParagraphStyling()
	var linkStyling = ListStyling()

	var itemStylingRules: [ItemStyling] {
		return [headerStyling, paragraphStyling, linkStyling]  
	}
}

You can implement CustomHeaderStyling by checking how other Styling objects have been implemented, like ``HeaderStyling. Make sure yourCustomHeaderStyling` comforms to all styling rules you'd like your custom styling to support. i.e. comform to `TextColorStylingRule` to support textStyle of your custom styling.

Each styling rule can be applied to a markDownItem by comforming to ItemStyling and implement the required method like this:

public func isApplicableOn(_ markDownItem: MarkDownItem) -> Bool {
	return markDownItem is HeaderMarkDownItem
}

This will let the mechanism know it should apply your styling to a HeaderMarkDownItem

You can inject your new styling object by passing it to the constructor of the MarkdownTextView

MarkDownTextView(styling: CustomMarkyMarkStyling())

Adding your own rules

Adding a new rule requires three new classes of based on the following protocol:

  • Rule that can recoginizes the desired markdown syntax
  • MarkDownItem for your new element that will be created by your new rule
  • LayoutBlockBuilder that can convert your MarkDownItem to layout

Add the rule to MarkyMark

markyMark.addRule(MyCustomRule())

Add the block builder to your layout converter

converter.addLayoutBlockBuilder(MyCustomLayoutBlockBuilder())

If needed you can also add a custom styling class to the default styling

styling.addStyling(MyCustomStyling())

Converter hook

The converter has a callback method which is called every time a MarkDownItem is converted to layout.

converter.didConvertElement = {
	markDownItem, view in
	// Do something with markDownItem and / or view here
}

When using the MarkdownTextView

markDownTextView.onDidConvertMarkDownItemToView = {
    markDownItem, view in

}

Link behavior

By default Markymark opens URL's using UIApplication.shared.delegate.open(_:open:options). links will only be openened when this method is implemented. Markymark allows changing this behavior by passing a custom URLOpener, an object that conforms to the URLOpener protocol.

let markDownView = MarkDownTextView()
markDownTextView?.urlOpener = MyCustomerURLOpener()

Using Markymark in Extensions

Markymark also supports usage the a Today extension. By default tapping url's is not working, since Extensions don't have access to UIApplication.shared, in order to support links you can pass a different url opener to a MarkyDownTextView. See the Example project for a working example:

markDownTextView?.urlOpener = ExtensionContextURLOpener(extensionContext: self.extensionContext)

Author

M2mobi, info@m2mobi.com

License

MarkyMark is available under the MIT license. See the LICENSE file for more info.

Github

link
Stars: 224

Dependencies

Used By

Total: 0

Releases

Swift Package Manager support - 2020-02-05 08:49:59

Resolves

  • #97

Breaking changes

  • In order for URL handling to keep working (in some cases), func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool has to be implemented in AppDelegate

Notes

  • Since this release is only beneficial for SPM users, there's currently no CocoaPods version of this code (yet).

MarkDownTextView expose UITextView creation - 2019-09-20 14:57:38

Resolves #92

Allow subclassing MarkDownTextView - 2019-07-25 12:05:57

Changed access of MarkdownTextView from public to open to allow subclassing

Optional minimum height rule for paragraph's - 2019-03-29 15:03:30

Allow add converter hook on MarkdownTextview - 2019-03-29 12:28:57

Allow adding rules to MarkdownTextView - 2019-03-28 09:41:42

Add https scheme to images URL's when no scheme is specified - 2019-01-25 14:36:21

When images are added with an url like:

//www.mywebsite.com/myimage.jpg

it will automatically be changed to:

https://www.mywebsite.com/myimage.jpg

This is implemented for users who sync markdown from a CMS which may use implicit url protocols that are supported on websites.

Extension support and customise open url behavior - 2018-11-22 20:26:30

Customise url opening by providing an instance of the URLOpener protocol and provide it to MarkdownTextView or MarkdownToViewConverterConfiguration.

Example: markDownTextView?.urlOpener = MyCustomerURLOpener()

Support markdown links in extensions by providing a different kind of url opener: markDownTextView?.urlOpener = ExtensionContextURLOpener(extensionContext: self.extensionContext)

Swift 4.2 support and minor improvements - 2018-10-26 09:04:27

Added public accessors for more customizations - 2018-06-18 10:13:04

Remove public CanConfigureViews - 2018-06-11 13:57:47

Removed protocol that was not needed to be public and was also causing conflicts in some cases.

Letterspacing support - 2018-06-11 13:45:07

  • Support letterspacing
  • Fix bug in textAlignment and line-height

MarkdownAttributedLabel - 2018-05-29 11:40:33

Implement MarkdownAttributedLabel that supports *italic* **bold**, ~~strikethrough~~ and [Links](https//www.link.com). Can also be used to have labels with custom line-height.

Convenient Marky Mark - 2018-05-28 08:17:21

Implemented convenient MarkDownTextView, updated Example project and updated README

In short MarkyMark 7.0 implements convenient syntax for creating simple MarkDownViews. Just instantiate MarkDownTextView(), set the text property and configure by reading the possibilities in the README

Bugfix a crash caused by not setting capitilizationForLevels - 2018-05-19 19:41:44

Allow capitilization of headers - 2018-05-15 15:10:51

Made DefaultStyling an open class instead of a struct to allow subclassing - 2018-05-15 08:24:18

Bullet images for the example project - 2018-04-04 11:29:04

Different content inset for each heading level (h1, h2, ...) - 2018-03-23 15:48:12

Bullet list image per level - 2018-03-21 11:00:13

Bugfixes - 2018-02-13 14:19:28

Contains bugfix for complex statements. Pull-request: https://github.com/M2Mobi/Marky-Mark/pull/46

Bugfixes - 2018-02-06 10:57:17

  • Bugfixes for ImageRule
  • Added more public accessors
  • Resolved warnings

Swift 4.0.0 - 2017-09-23 16:07:52

Support for Swift 4.0.0 / Xcode 9

Inline image optimisation swift 3.0 - 2017-01-13 15:30:01

In this version there is an update for inline image support. The image were scaled wrongly if they were smaller than the width of the markdown view.

Inline image optimisation swift 2.3 - 2017-01-13 15:29:09

In this version there is an update for inline image support. The image were scaled wrongly if they were smaller than the width of the markdown view.

- 2016-10-03 10:24:11

Swift 3.0 support. Xcode 8+

Deprecates version 0.1.5

- 2016-10-03 10:19:10

Swift 2.3 support. Xcode 8+

Deprecates version 1.4.x

- 2016-10-03 10:10:07

Swift 2.2 Deprecates version 0.1.3

- 2016-09-30 09:51:36

Deprecated. Use Version 2.0.0 instead

- 2016-09-30 09:51:25

Deprecated. Use Version 3.0.0 instead