{"id":2051,"url":"https://github.com/m2mobi/Marky-Mark","last_synced_at":"2025-08-02T23:31:22.692Z","repository":{"id":8607158,"uuid":"59031766","full_name":"M2mobi/Marky-Mark","owner":"M2mobi","description":"Markdown parser for iOS","archived":false,"fork":false,"pushed_at":"2023-01-24T11:19:42.000Z","size":1897,"stargazers_count":301,"open_issues_count":19,"forks_count":66,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-10-29T22:40:40.896Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/M2mobi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-05-17T14:52:46.000Z","updated_at":"2024-10-10T17:49:15.000Z","dependencies_parsed_at":"2023-02-13T20:46:18.008Z","dependency_job_id":null,"html_url":"https://github.com/M2mobi/Marky-Mark","commit_stats":null,"previous_names":[],"tags_count":48,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M2mobi%2FMarky-Mark","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M2mobi%2FMarky-Mark/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M2mobi%2FMarky-Mark/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/M2mobi%2FMarky-Mark/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/M2mobi","download_url":"https://codeload.github.com/M2mobi/Marky-Mark/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228503092,"owners_count":17930509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-01-05T20:16:02.211Z","updated_at":"2024-12-06T17:30:31.994Z","avatar_url":"https://github.com/M2mobi.png","language":"Swift","funding_links":[],"categories":["Text"],"sub_categories":["Other free courses","Other Testing","Keychain"],"readme":"# Marky Mark\n[![build status](https://api.travis-ci.org/M2Mobi/Marky-Mark.svg)](https://travis-ci.org/M2Mobi/Marky-Mark) [![codecov](https://codecov.io/gh/M2Mobi/Marky-Mark/branch/master/graph/badge.svg)](https://codecov.io/gh/M2Mobi/Marky-Mark)\nMarky 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.\n\n[![Screenshot](Readme_Assets/example1-thumb.png)](Readme_Assets/example1.png)\n[![Screenshot](Readme_Assets/example2-thumb.png)](Readme_Assets/example2.png)\n[![Screenshot](Readme_Assets/example3-thumb.png)](Readme_Assets/example3.png)\n\n## Example\n\nTo run the example project, clone the repo, and run `pod install` from the Example directory first.\n\n## Requirements\n- iOS 8.0+ \n- Xcode 8.0+\n\n## Installation\n\nCocoaPods 1.0.0+ is required to build MarkyMark\n\nTo integrate MarkyMark into your Xcode project using CocoaPods, specify it in your `Podfile`:\n\n```ruby\npod \"markymark\"\n```\n\nAlternatively, add MarkyMark to your project using Swift Package Manager using:\n\n```link\nhttps://github.com/M2Mobi/Marky-Mark\n```\n\n## Simple usage\n\n### View with default styling\n```swift\nlet markDownView = MarkDownTextView()\nmarkDownView.text = \"# Header\\nParagraph\"\n```\n\n### View with modified styling\n\nMarkymark has many styling options, please check the examples in the styling section of this readme. A simple example:\n\n```swift\nlet markDownView = MarkDownTextView()\nmarkDownView.styling.headingStyling.textColorsForLevels = [\n\t.orange, //H1 (i.e. # Title)\n\t.black,  //H2, ... (i.e. ## Subtitle, ### Sub subtitle)\n]\n\nmarkDownView.styling.linkStyling.textColor = .blue\nmarkDownView.styling.paragraphStyling.baseFont = .systemFont(ofSize: 14)\nmarkDownView.text = \"# Header\\nParagraph\"\n```\n\n\n## Supported tags in the Default Flavor\nNote: 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\n\n```\nHeadings\n# H1\n## H2\n### H3\n#### H4\n##### H5\n###### H6\n\nLists\n- item\n\t- item\n* item\n\t* item\n+ item\n\t+ item\na. item\nb. item\n1. item\n2. item\n\nEmphasis\n*Em*\n_Em_\n**Strong**\n__Strong__\n~~Strike through~~\n\nImages\n![Alternative text](image.png)\n\nLinks\n[Link text](https://www.example.net)\n\nCode \n`code`\n```code```\n```\n\n\n### Customizing default style\n\nDefault Styling instance\n\n```swift\nvar styling = DefaultStyling()\n```\n#### Paragraphs (regular text)\nMarkdown example: `Some text`\n\n```swift\nstyling.paragraphStyling.baseFont = .systemFont(ofSize: 14)\nstyling.paragraphStyling.textColor = .black\nstyling.paragraphStyling.contentInsets = UIEdgeInsets(top:0, left: 0, bottom: 5, right: 0)  \nstyling.paragraphStyling.lineHeight = 4\nstyling.paragraphStyling.isBold = false\nstyling.paragraphStyling.isItalic = false\nstyling.paragraphStyling.textAlignment = .left\n```\n\n#### Headings\n\nMarkdown example: `# Title` or `## Subtitle` etc.\n\n```swift\nstyling.headingStyling.fontsForLevels = [\n\tUIFont.boldSystemFontOfSize(24), //H1\n\tUIFont.systemFontOfSize(18),     //H2\n\tUIFont.systemFontOfSize(16)      //H3, ... (last item will be next levels as well)\n]\n\nstyling.headingStyling.colorsForLevels = [\n\t.red, //H1\n\t.black, //H2, ... (last item will be next levels as well)\n]\n\n// Margins\nstyling.headingStyling.contentInsetsForLevels = [\n\tUIEdgeInsets(top: 5, left: 0, bottom: 15, right: 10), // H1\n\tUIEdgeInsets(top: 5, left: 0, bottom: 5, right: 10) //H2, ... (last item will be next levels as well)\n]\n\nstyling.headingStyling.isBold = false\nstyling.headingStyling.isItalic = false\nstyling.headingStyling.isUnderlined = false\nstyling.headingStyling.textAlignment = .left\n```\n\n#### linkStyling\nMarkdown Example `[Google](http://www.google.com)`\n\n```swift\nstyling.linkStyling.textColor = .black\nstyling.linkStyling.baseFont = nil // Default: nil. Setting baseFont to nil will inherit font from paragraphStyling\n\nstyling.linkStyling.isBold = false\nstyling.linkStyling.isItalic = false\nstyling.linkStyling.isUnderlined = true\n```\n\n#### List styling\nMarkdown Example:\n\n```\n- List item 1\n- List item 2\n- Nested List item\n```\n\n```swift\n// By default a font will be used with the bullet character `•`. Use the follow properties to configure it's size and color:\nstyling.listStyling.bulletFont = .systemFont(ofSize: 14)\nstyling.listStyling.bulletColor = .black\n\n// Bullets can also be images for more complex styling. When setting images, bullet font and color won't be used anymore\n// Array of images used as bullet for each level of nested list items\nstyling.listStyling.bulletImages = [\n\tUIImage(named: \"circle\"),\n\tUIImage(named: \"emptyCircle\"),\n\tUIImage(named: \"line\"),\n\tUIImage(named: \"square\")\n]\n\n// Size of the images\nstyling.listStyling.bulletViewSize = CGSize(width: 16, height: 16)\n\nstyling.listStyling.baseFont = .systemFont(ofSize: 14)\nstyling.listStyling.contentInsets = UIEdgeInsets(top: 0, left:  0, bottom: 10, right: 10)\n\n//Amount of space underneath each list item\nstyling.listStyling.bottomListItemSpacing = 5\n\n// Number of pixels to indent for each nested list level\nstyling.listStyling.listIdentSpace = 15\n\nstyling.listStyling.textColor = .black\n```\n\nStyling is also possible for:\n\n```\nstyling.paragraphStyling\nstyling.italicStyling\nstyling.boldStyling\nstyling.strikeThroughStyling\nstyling.imageStyling\nstyling.linkStyling\nstyling.horizontalLineStyling\nstyling.codeBlockStyling\nstyling.inlineCodeBlockStyling\nstyling.quoteStyling\n```\n_Please check the `DefaultStyling` class for more information_\n\n### Accessibility: Dynamic Type (font scaling)\n\nTo enable Dynamic Type on MarkDownTextView:\n\n```\n    markDownView.hasScalableFonts = true\n```\n\nIn some cases you may want to configure a maximum size for each font (default is `nil`).\nAn example on how to limit the font-size for paragraphs\n\n```\n    markDownView.styling.paragraphStyling.maximumPointSize = 26\n```\n\nTo set maximumPointSize for headings you can set 'maximumPointSizeForLevels' on headingStyling. The following example sets the maximum size for heading1, heading2, heading3:\n\n```\n    markDownView.styling.headingStyling.maximumPointSizeForLevels = [36, 20, 18]\n```\n\n## Advanced usage\nAdvanced 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.\n\n### Custom styling objects\n\n```swift\nstruct CustomMarkyMarkStyling: Styling {\n\tvar headerStyling = CustomHeaderStyling()\n\tvar paragraphStyling = ParagraphStyling()\n\tvar linkStyling = ListStyling()\n\n\tvar itemStylingRules: [ItemStyling] {\n\t\treturn [headerStyling, paragraphStyling, linkStyling]  \n\t}\n}\n```\n\nYou can implement `CustomHeaderStyling` by checking how other `Styling` objects have been implemented, like `HeaderStyling`. \nMake sure your `CustomHeaderStyling` 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.\n\nEach styling rule can be applied to a markDownItem by comforming to `ItemStyling` and implement the required method like this:\n\n```\npublic func isApplicableOn(_ markDownItem: MarkDownItem) -\u003e Bool {\n\treturn markDownItem is HeaderMarkDownItem\n}\n\n```\nThis will let the mechanism know it should apply your styling to a HeaderMarkDownItem\n\nYou can inject your new styling object by passing it to the constructor of the `MarkdownTextView`\n\n```\nMarkDownTextView(styling: CustomMarkyMarkStyling())\n```\n\n### Adding your own rules\nAdding a new rule requires three new classes of based on the following protocol:\n\n* `Rule` that can recoginizes the desired markdown syntax\n* `MarkDownItem` for your new element that will be created by your new rule\n* `LayoutBlockBuilder` that can convert your MarkDownItem to layout\n\nAdd the rule to MarkyMark\n\n```swift\nmarkyMark.addRule(MyCustomRule())\n```\n\nOr when using the MarkdownTextView:\n\n```swift\nmarkdownTextView.add(rule: MyCustomRule())\n```\n\nAdd the block builder to your layout converter\n\n```swift\nconverter.addLayoutBlockBuilder(MyCustomLayoutBlockBuilder())\n```\n\nOr when using the MarkdownTextView use either of these options (depending on the configuration view or attributedString):\n\n```swift\nmarkdownTextView.addViewLayoutBlockBuilder(MyCustomLayoutBlockBuilder())\n```\n\n```swift\nmarkdownTextView.addAttributedStringLayoutBlockBuilder(MyCustomLayoutBlockBuilder())\n```\n\nIf needed you can also add a custom styling class to the default styling\n\n```swift\nstyling.addStyling(MyCustomStyling())\n```\n\n### Converter hook\nThe converter has a callback method which is called every time a `MarkDownItem` is converted to layout. \n\n```swift\nconverter.didConvertElement = {\n\tmarkDownItem, view in\n\t// Do something with markDownItem and / or view here\n}\n```\n\nWhen using the MarkdownTextView\n\n```swift\nmarkDownTextView.onDidConvertMarkDownItemToView = {\n    markDownItem, view in\n\n}\n```\n\n### Link behavior\nBy 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.\n\n```swift\nlet markDownView = MarkDownTextView()\nmarkDownTextView?.urlOpener = MyCustomerURLOpener()\n```\n\n### Using Markymark in Extensions\n\nMarkymark 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:\n\n```swift\nmarkDownTextView?.urlOpener = ExtensionContextURLOpener(extensionContext: self.extensionContext)\n```\n\n## Author\n\nM2mobi, info@m2mobi.com\n\n## License\n\nMarkyMark is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm2mobi%2FMarky-Mark","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fm2mobi%2FMarky-Mark","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fm2mobi%2FMarky-Mark/lists"}