A SwiftUI view for displaying Markdown with customizable appearances.

Overview

Parma

Display Markdown using pure SwiftUI components. Taking advantages of ViewBuilder to make custom appearances for Text and View.

Example

import Parma

struct ContentView: View {
    var markdown = "I'm **Strong**."
    
    var body: some View {
        Parma(markdown)
    }
}

For more examples, please refer to demo app.

Markdown Support

Already Supported

  • Heading level 1-6
  • Paragraph
  • Multi-level bullet list
  • Multi-level ordered list
    • Period delimiter
    • Parenthesis delimiter
  • Image (Needs extra configurations)
  • Inline text
    • Strong
    • Emphasis
    • Code

Possibly Support in Future Versions

  • Divider
  • Block quote
  • Code block

Unsupported

  • Inline hyperlink

Installation

Requirement

  • Xcode 11.0 or later
  • Swift 5 or later
  • iOS 13.0 / macOS 10.15 or later deployment targets

Swift Package Manager

Swift Package Manager is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies on all platforms.

Adding Parma as a dependency by using Xcode’s GUI, the package url is https://github.com/dasautoooo/Parma .

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Parma into your Xcode project using CocoaPods, specify it in your Podfile:

pod 'Parma'

Appearance Customization

To customize Text styles and Views, create a new render which conform to protocol ParmaRenderable, and only reimplement those that fit your purposes. Finally, assign the customized render as a new render when create Parma view.

import Parma

struct ContentView: View {
    var markdown = "I'm **Strong**."
    
    var body: some View {
        Parma(markdown, render: MyRender())
    }
}

struct MyRender: ParmaRenderable {
    ...
}

There's a DemoApp that modified some of these delegate methods below for everyone to take as a reference.

/// Define the heading text style.
/// - Parameters:
///   - level: The level of heading.
///   - textView: The textView generated from captured heading string.
func heading(level: HeadingLevel?, textView: Text) -> Text

/// Define the paragraph text style.
/// - Parameter text: The text string captured from paragraph.
func paragraph(text: String) -> Text

/// Define the text style for plain text. Do NOT recommend to alter this if there's no special purpose.
/// - Parameter text: The text string captured from markdown.
func plainText(_ text: String) -> Text

/// Define the strong text style.
/// - Parameter textView: The textView generated from captured strong string.
func strong(textView: Text) -> Text

/// Define the emphasis text style.
/// - Parameter textView: The textView generated from captured emphasis string.
func emphasis(textView: Text) -> Text

/// Define the link text style.
/// - Parameters:
///   - textView: The textView generated from captured link string.
///   - destination: The destination of the link.
func link(textView: Text, destination: String?) -> Text

/// Define the code text style.
/// - Parameter text: The text string captured from code.
func code(_ text: String) -> Text

/// Define the style of heading view.
/// - Parameters:
///   - level: The level of heading.
///   - view: The view contains heading text.
func headingBlock(level: HeadingLevel?, view: AnyView) -> AnyView

/// Define the style of paragraph view.
/// - Parameter view: The view contains view(s) which belong(s) to this paragraph.
func paragraphBlock(view: AnyView) -> AnyView

/// Define the style of list item.
/// - Parameter attributes: Attributes of the list containing the item. Those must be considered for proper item rendering.
/// - Parameter index: Normalized index of the list item. For exemple, the index of the third item of a one level list would be `[2]` and the second item of a sublist appearing fourth in it's parent list would be `[3, 1]`.
/// - Parameter view: The view contains view(s) which belong(s) to this item.
func listItem(attributes: ListAttributes, index: [Int], view: AnyView) -> AnyView

/// Define the style of image view.
/// - Parameter urlString: The url string for this image view.
/// - Parameter altTextView: The view contains alt text.
func imageView(with urlString: String, altTextView: AnyView?) -> AnyView

Name Origin

Parma is a city in the northern Italy, which is famous for its architecture, music and art. The reason of choosing this city name as the project name is Giambattista Bodoni, a famous typographer, who spent most his lifetime living and working in this city.

Bodoni was an Italian typographer, type-designer in Parma. During his lifespan, he designed many typefaces that known as Bodoni nowadays. Each Mac has Bodoni font installed, and free to use.

Credit

The package is built upon Down, which is a markdown parser in Swift.

Comments
  • Add support for multi-level ordered lists

    Add support for multi-level ordered lists

    Add support for multi-level ordered lists supporting two types of delimiters: parenthesis and periods.

    It also support mix-and-matching delimiters and list styles. As an exemple, the following markdown:

    1) Item 1
    1) Item 2
    1) Item 3
       1. Item 3a
          * Item 3a1
          * Item 3a2
       1. Item 3b
    1) Item 4
    1) Item 5
       1. Item 5a
       1. Item 5b
    

    Would produce the following result with the default ParmaRenderable: Screen Shot 2021-05-13 at 5 38 48 PM

    Notes

    • The default bullet list style has been updated to switch between and for better readability Screen Shot 2021-05-13 at 5 53 43 PM

    • Fixes the additional spacing for multi-level lists when entering a deeper level Screen Shot 2021-05-13 at 5 52 35 PM

    opened by antoinelamy 7
  • Escape < and > characters

    Escape < and > characters

    Hello,

    Thanks for Parma, it's terrific!

    I did discover that because XML is used under the hood, content with < and > causes problems with the output, essentially truncating anything before the first <.

    This pull adds a function to escape these characters. An enum is provided to allow easy addition of future escaped characters:

    enum EscapedCharacters: String, CaseIterable {
                
                case leftAngleBracket = "<",
                     rightAngleBracket = ">"
                
                func replacement() -> String {
                    switch self {
                    case .leftAngleBracket:
                        return "&lt;"
                    case .rightAngleBracket:
                        return "&gt;"
                    }
                }
                
                static func escapeString(_ string: String) -> String {
                    var escapedValue = string
                    
                    self.allCases.forEach {
                        escapedValue = escapedValue.replacingOccurrences(of: $0.rawValue, with: $0.replacement())
                    }
                    
                    return escapedValue
                }
            }
    

    I hope you will find this addition useful, and open to any feedback if you'd prefer this to live elsewhere in the project than ParmaCore.

    opened by daniloc 3
  • imageView fails

    imageView fails

    Hello and thanks for ur great work!

    I've run into one issue related to images. I've derived from ParmaRenderable as discribed as follows:

    struct AlculateRender: ParmaRenderable {
      func imageView(with urlString: String) -> AnyView {
    	AnyView(Text(urlString))
      }
    }
    

    and initialize Parma with it. But the imageView function never got executed. So started a bit of debugging and the Image ElementComposer never called the imageView function.

    struct ImageElementComposer: BlockElementComposer {
        func view(in context: ComposingContext, render: ParmaRenderable) -> AnyView {
            guard let urlString = context.attributes["destination"] else {
                return AnyView(EmptyView())
            }
            return render.imageView(with: urlString)
        }
    }
    

    The guard statement fails and it returns the EmptyView. The context.attributes contains one key/value element = ["xml:space", "preserve"], which is weired.

    I tried the following markdown:

    ![Image](http://url/a.png)
    
    ![Image][1]
    
    [1]: http://url/b.jpg 
    

    Before i start further debugging, i may ask if u have any idea about that.

    Thanks in advance

    opened by npetri13 3
  • How to modify Text inside listItem?

    How to modify Text inside listItem?

    Hi @dasautoooo! Parma is working really fine on what I was doing, but there is just one thing I can't achieve yet:

    Is there a way to modify the Text view inside the listItem method without using the plainText one?

    In my case I have both a heading and a bullet list, so If I modify the Text view inside the plainText method the Text view inside heading is also being modified, and I don't want that. I already tried with paragraph but nothing happens.

    Thanks in advance, Francisco.

    opened by franciscorosso 2
  • As a user, I'd like to have .leading alignment by default, not .center

    As a user, I'd like to have .leading alignment by default, not .center

    This is fantastic, thank you so much!

    I noticed that ,by default, the render is centered, which is uncommon, I believe. How can I change that to leading?

    Ideally, .leading should be the default render without the need for any configuration. Once again, thanks for this fantastic work and building such a simple SwiftUI component.

    opened by sashamitrovich 2
  • Added watchOS 7.0 support

    Added watchOS 7.0 support

    Hello. First of all thanks for this useful package. I was trying to use this for my watchOS app but ran into errors since the package's several internal parts required a watchOS minimum support of 7.0. I have added watchOS 7.0 as a platform to Package.swift and now I can use this package.

    Simulator Screen Shot - Apple Watch Series 6 - 40mm - 2021-02-23 at 15 01 32

    opened by iberatkaya 1
  • plainText(_ text: String) overrides heading settings. Is that working the way its meant to?

    plainText(_ text: String) overrides heading settings. Is that working the way its meant to?

    I have some markdown text like this:

    This is my title

    And this is my body

    And when I use the plaintText(_ text: String) to provide a font for the plain body text, it overrides the header font changes value and forces all text from the entire string an displays it with the same font

    opened by bryan1anderson 3
  • Line Limit modifier produces very unexpected results

    Line Limit modifier produces very unexpected results

        Parma(markdown, render: MyRender())
            .lineLimit(1)
    

    I would expect this to work similarly to Text or not at all.. but it does work. Just not predictably.

    It clips each line. Looks like it might be applying the limit to literally each line individually.

    Any workaround suggestions would be greatly appreciated

    opened by bryan1anderson 2
  • Links embedded in bold text are rendered incorrectly.

    Links embedded in bold text are rendered incorrectly.

    Consider the following examples and their respective expected results: | | Markdown | Expected Result | Actual Result | Is Correct? | |-|---|---|---|---| | 1 | **[Bold-Link]()** | Bold-Link | Bold-Link | Yes | | 2 | **Bold [Link]()** | Bold Link | Link | No | | 3 | **[Link]() Bold** | Link Bold | Bold (this actually should have a space before the word | No |

    Any tips on how to fix this would be highly appreciated

    opened by fjcaetano 2
  • [wip] Directly deal with the Down AST, skip detour via XML

    [wip] Directly deal with the Down AST, skip detour via XML

    Makes it faster. Rendering the example text goes from 3.3ms to 2.6ms.

    Requires a newer version of Down which includes this PR: https://github.com/johnxnguyen/Down/pull/275

    opened by 5sw 0
Releases(v0.3.0)
Owner
DasAuto
💻 Programmer | 🕹 Lifetime Gamer
DasAuto
Render Markdown text in SwiftUI

Render Markdown text in SwiftUI. It is a preview based on the Marked implementation

小弟调调™ 26 Dec 20, 2022
Transition from any SwiftUI Text view into an inline navigation bar title when the view is scrolled off-screen, as seen in Apple's TV & TestFlight iOS apps.

SwiftUI Matched Inline Title Transition from any SwiftUI Text view into an inline navigation bar title when the view is scrolled off-screen, as seen i

Seb Jachec 19 Oct 9, 2022
SwiftUI TextEdit View - A proof-of-concept text edit component in SwiftUI & CoreText.

A proof-of-concept text edit component in SwiftUI & CoreText. No UIKit, No AppKit, no UITextView/NSTextView/UITextField involved.

Marcin Krzyzanowski 80 Dec 1, 2022
A simple and easily customizable InputAccessoryView for making powerful input bars with autocomplete and attachments

InputBarAccessoryView Features Autocomplete text with @mention, #hashtag or any other prefix A self-sizing UITextView with an optional fixed height (c

Nathan Tannar 968 Jan 2, 2023
Floating-textfield-swiftui - Floating textfield swiftui: Floating field with multiple icons

floating_textfield-swiftui Hey, Guys welcome to this tutorial. In this complete

Patrick 0 Jan 2, 2022
Floating-textfield-swiftui - Floating textfield With SwiftUI

floating_textfield-swiftui Hey, Guys welcome to this tutorial. In this complete

null 1 Feb 11, 2022
A customisable view for entering arbitrary length pins, codes or passwords in iOS. Supports iOS 12 one time codes.

CBPinEntryView CBPinEntryView is a view written in Swift to allow easy and slick entry of pins, codes or passwords. It allows backspacing, dismissal o

Chris Byatt 183 Dec 5, 2022
Flutter plugin to display VGS card info using TextView or View

VGS Card Info Flutter plugin to display VGS Card info using TextView or View Installation Add the dependency in your pubspec.yaml vgscardinfo: git

Djamo Côte d'Ivoire 0 Mar 7, 2022
A text view that supports selection and expansion

The Problem UILabel and UITextView offer unsatisfying support for text selection. Existing solutions like TTTAttributedLabel are great but offer a som

Jeff Hurray 636 Dec 20, 2022
A Float Input View with smooth animation and supporting icon and seperator written with Swift

RSFloatInputView Features Smooth animation using CoreText Support optional left icon Support optional seperator Configurable padding, size, fonts and

null 103 Nov 13, 2022
Floating Label TextField for SwiftUI. FloatingLabelTextFieldSwiftUI

FloatingLabelTextFieldSwiftUI FloatingLabelTextFieldSwiftUI is a small and lightweight SwiftUI framework written in completely swiftUI (not using UIVi

Kishan Raja 337 Jan 2, 2023
Fully-wrapped UITextField made to work entirely in SwiftUI

iTextField ⌨️ A fully-wrapped `UITextField` that works entirely in SwiftUI. ?? Get Started | Examples | Customize | Install | Get Started Install iTex

Benjamin Sage 89 Jan 2, 2023
A SwiftUI TextField with a prompt (or placeholder) that floats above the text field when active or not empty. Requires iOS 15.

FloatingPromptTextField A prompt is the label in a text field that informs the user about the kind of content the text field expects. In a default Tex

Emilio Peláez 43 Nov 3, 2022
A Credit Amount and EMI User Interface build in Swift and SwiftUI

App Usage An iPhone application build in swift . Overview Supported on All iPhone Screen Sizes Dynamic Data following MVVM Design Pattern Used Transit

Mohammad Yasir 4 Apr 20, 2022
A floating label style for SwiftUI's TextField.

FloatingLabelTextFieldStyle A floating label style for TextField with support for displaying error messages. Requirements iOS 15.0+ macOS 12.0+ Instal

Red Davis 13 Aug 22, 2022
Focus text field in SwiftUI dynamically and progress through form using iOS keyboard.

Focuser Focuser allows to focus SwiftUI text fields dynamically and implements ability move go through the form using Keyboard for iOS 13 and iOS 14.

Art Technologies 118 Dec 25, 2022
Provides a SwiftUI multi-line TextView implementation including support for auto-sizing. (iOS)

TextView Also available as a part of my SwiftUI+ Collection – just add it to Xcode 13+ Provides a SwiftUI multi-line TextView implementation with supp

SwiftUI+ 51 Jan 3, 2023
Autocomplete for a text field in SwiftUI using async/await

Autocomplete for a text field in SwiftUI using async/await

Dmytro Anokhin 13 Oct 21, 2022
Currency text field formatter available for UIKit and SwiftUI 💶✏️

CurrencyText provides lightweight libraries for formating text field text as currency, available for both UIKit and SwiftUI. Its main core, the Curren

Felipe Lefèvre Marino 183 Dec 15, 2022