Created
September 2, 2022 09:39
-
-
Save jayesh15111988/ec13ecf2f87035239e6a5d4c6a046958 to your computer and use it in GitHub Desktop.
A source code to demo applying attributed strings attribute from markdown styles
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CustomAttributedStringTextView.swift | |
// SwiftUIBlogPosts | |
// | |
// Created by Jayesh Kawli on 8/31/22. | |
// | |
import SwiftUI | |
struct CustomAttributedStringTextViewModel { | |
let customAttributedInputString: AttributedString | |
init() { | |
let inputString = "^[Amsterdam](cityStyle: 'amsterdamStyle', cityColor: 'amsterdamColor') is a great city and ^[regular rainbow style](rainbowStyle: 'regular') awesome city to live in ^[first half rainbow style](rainbowStyle: 'firstHalf') or ^[second half rainbow style](rainbowStyle: 'secondHalf')" | |
self.customAttributedInputString = (try? AttributedString( | |
markdown: inputString, | |
including: AttributeScopes.CustomCityAttributes.self, | |
options: AttributedString.MarkdownParsingOptions(allowsExtendedAttributes: true))) ?? AttributedString(inputString) | |
} | |
} | |
struct CustomAttributedStringTextView: View { | |
let customAttributedStringTextViewModel: CustomAttributedStringTextViewModel | |
init() { | |
self.customAttributedStringTextViewModel = CustomAttributedStringTextViewModel() | |
} | |
var body: some View { | |
Text(customStyledAttributedString(input: customAttributedStringTextViewModel.customAttributedInputString)) | |
.padding() | |
} | |
private func customStyledAttributedString(input: AttributedString) -> AttributedString { | |
var output = input | |
for run in output.runs { | |
let customCityAttributes = run.customCityAttributes | |
guard customCityAttributes.customCityStyle != nil || customCityAttributes.customColorStyle != nil || customCityAttributes.customRainbowStyle != nil else { | |
continue | |
} | |
let range = run.range | |
if let customCityStyle = customCityAttributes.customCityStyle { | |
if customCityStyle == .amsterdamStyle { | |
output[range].font = .largeTitle | |
} | |
} | |
if let customColorStyle = customCityAttributes.customColorStyle { | |
if customColorStyle == .amsterdamColor { | |
output[range].foregroundColor = .orange | |
} | |
} | |
if let customRainbowStyle = customCityAttributes.customRainbowStyle { | |
let rainbowColorsCollection: [Color] | |
switch customRainbowStyle { | |
case .regular: | |
rainbowColorsCollection = rainbowColorsCollectionFull | |
case .firstHalf: | |
rainbowColorsCollection = rainbowColorsCollectionFirstHalf | |
case .secondHalf: | |
rainbowColorsCollection = rainbowColorsCollectionSecondHalf | |
} | |
var integerIndex = 0 | |
for i in output[range].characters.indices { | |
output[i..<output[range].characters.index(after: i)].foregroundColor = rainbowColorsCollection[integerIndex % rainbowColorsCollection.count] | |
integerIndex += 1 | |
} | |
} | |
} | |
return output | |
} | |
private var rainbowColorsCollectionFull: [Color] { | |
[.red, .orange, .yellow, .green, .blue, .indigo, .purple] | |
} | |
private var rainbowColorsCollectionFirstHalf: [Color] { | |
[.red, .orange, .yellow] | |
} | |
private var rainbowColorsCollectionSecondHalf: [Color] { | |
[.green, .blue, .indigo, .purple] | |
} | |
} | |
public enum CustomCityStyleAttributes: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey { | |
public enum Value: String, Codable { | |
case amsterdamStyle | |
} | |
public static var name = "cityStyle" | |
} | |
public enum CustomCityColorAttributes: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey { | |
public enum Value: String, Codable { | |
case amsterdamColor | |
} | |
public static var name = "cityColor" | |
} | |
public enum CustomRainbowStyleAttributes: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey { | |
public enum Value: String, Codable { | |
case regular | |
case firstHalf | |
case secondHalf | |
} | |
public static var name = "rainbowStyle" | |
} | |
public extension AttributeScopes { | |
struct CustomCityAttributes: AttributeScope { | |
let customCityStyle: CustomCityStyleAttributes | |
let customColorStyle: CustomCityColorAttributes | |
let customRainbowStyle: CustomRainbowStyleAttributes | |
let swiftUI: SwiftUIAttributes | |
} | |
var customCityAttributes: CustomCityAttributes.Type { CustomCityAttributes.self } | |
} |
This (in gist) is far more easy to write than @-ing you in TT. 😃
^[string_to_decorate](attribute_key_1: attribute_value_1, attribute_key_2: attribute_value_2)
, this piece can be reinterpreted as ^[concept](semantic_this : … , style_that : … , annotation_for_classification: …)
- think in CSS’s element’s class
and style
attributes. That will make the purpose of annotation not only for styling but also anything else (and potentially later re-styling)
This Apple’s markdown extension, supporting dictionary and more, is far more expressive than CSS’s class
attribute’s merely flat string list.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
customStyledAttributedString
can be refactored to support any new attribute-style definition ,on-demand , in plug-in fashion (vs currently modify the method’s content)Good job!