[2]: Editor Rule - GeekTree0101/VEditorKit GitHub Wiki
VEditorKit Text Styling built on BonMot https://github.com/Raizlabs/BonMot It is really beautiful, easy attributed strings 3rd-party library in Swift
VEditorKit의 텍스트 스타일을 다루는 모든 기능 및 요소에 대해서는 BonMot을 기반으로 개발되었습니다.
- Input: Parse XMLString with Editor Rule
<contnet><p>hello world</p><img src="....."/><p>end</p></content>
- Output: Array of VEditorContent
[NSAttributedString, VEditorMediaContent, NSAttributedString]
VEditorKit offers 3 kind of content
- NSAttributedString
- VEditorMediaContent
- VEditorPlaceholderContent
extension NSAttributedString: VEditorContent { }
public protocol VEditorMediaContent: VEditorContent {
var xmlTag: String { get }
init(_ xmlTag: String, attributes: [String: String])
func parseAttributeToXML() -> [String: String] // parseto xml attribute from media content
}
public struct VEditorPlaceholderContent: VEditorContent {
public var xmlTag: String
public var model: Any
}
// MARK: - VEditorKit Editor Rule
public protocol VEditorRule {
var allXML: [String] { get }
var defaultStyleXMLTag: String { get }
var linkStyleXMLTag: String? { get }
func paragraphStyle(_ xmlTag: String, attributes: [String: String]) -> VEditorStyle?
func build(_ xmlTag: String, attributes: [String: String]) -> VEditorMediaContent?
func parseAttributeToXML(_ xmlTag: String, attributes: [NSAttributedString.Key: Any]) -> [String: String]?
func enableTypingXMLs(_ inActiveXML: String) -> [String]?
func disableTypingXMLs(_ activeXML: String) -> [String]?
func inactiveTypingXMLs(_ activeXML: String) -> [String]?
func activeTypingXMLs(_ inactiveXML: String) -> [String]?
}
XML Tag를 정의합니다.
- allXML: You have to return all of xml-tag cases (XML Builder와 Parser에 필요한 모든 XML Tag를 반환하세요.)
- defaultStyleXMLTag: Default text style attribute (기본 텍스트 스타일에 해당하는 XML Tag값을 반환합니다.)
- linkStyleXMLTag: Link(Article, ) XML Tag (기본 Link 스타일에 해당하는 XML Tag값을 반환합니다.)
XML을 Build하거나 Parse할 때, 텍스트를 입력할 때 필요한 모든 Attributes를 제공 및 가공합니다.
- paragraphStyle: make text text style attribute with xml-tag attributes base on xml-tag
- build: make media content object(such as video, image, opengraph and so on) with xml-tag attributes base on xml-tag
- parseAttributeToXML: Make xml-tag attributes with NSAttributedString attributes
- enableTypingXMLs: return enableTypingXMLs base on inactiveXML (TypingControl will be enabled)
- disableTypingXMLs: return disableTypingXMLs base on activeXML (TypingControl will be disabled)
- inactiveTypingXMLs: return inactiveTypingXMLs base on activeXML (TypingControl will be non-selected)
- activeTypingXMLs: return activeTypingXMLs base on inactiveXML (TypingControl will be selected)
struct EditorRule: VEditorRule {
// Convenience XML CaseIterable Enum
enum XML: String, CaseIterable {
case article = "a"
case paragraph = "p"
case bold = "b"
case italic = "i"
case heading = "h2"
case quote = "blockquote"
case image = "img"
case video = "video"
case opengraph = "og-object"
}
var defaultStyleXMLTag: String {
return XML.paragraph.rawValue
}
var linkStyleXMLTag: String? {
return XML.article.rawValue
}
var allXML: [String] {
return XML.allCases.map({ $0.rawValue })
}
func paragraphStyle(_ xmlTag: String, attributes: [String : String]) -> VEditorStyle? {
guard let xml = XML.init(rawValue: xmlTag) else { return nil }
switch xml {
case .paragraph:
return .init([.font(UIFont.systemFont(ofSize: 16)),
.minimumLineHeight(26.0),
.color(.black)])
case .bold:
return .init([.emphasis(.bold),
.font(UIFont.systemFont(ofSize: 16)),
.minimumLineHeight(26.0),
.color(.black)])
case .italic:
return .init([.emphasis(.italic),
.font(UIFont.systemFont(ofSize: 16)),
.minimumLineHeight(26.0),
.color(.black)])
case .heading:
return .init([.font(UIFont.systemFont(ofSize: 30, weight: .medium)),
.minimumLineHeight(40.0),
.color(.black)])
case .quote:
return .init([.font(UIFont.systemFont(ofSize: 20)),
.color(.gray),
.firstLineHeadIndent(19.0),
.minimumLineHeight(30.0),
.headIndent(19.0)])
case .article:
let style: VEditorStyle = .init([.font(UIFont.systemFont(ofSize: 16)),
.minimumLineHeight(26.0),
.color(.black),
.underline(.single, .black)])
if let url = URL(string: attributes["href"] ?? "") {
return style.byAdding([.link(url)])
} else {
return style
}
default:
return nil
}
}
func build(_ xmlTag: String, attributes: [String : String]) -> VEditorMediaContent? {
guard let xml = XML.init(rawValue: xmlTag) else { return nil }
switch xml {
case .image:
return VImageContent(xmlTag, attributes: attributes)
case .video:
return VVideoContent(xmlTag, attributes: attributes)
case .opengraph:
return VOpenGraphContent(xmlTag, attributes: attributes)
default:
return nil
}
}
func parseAttributeToXML(_ xmlTag: String,
attributes: [NSAttributedString.Key : Any]) -> [String : String]? {
guard let xml = XML.init(rawValue: xmlTag) else { return nil }
switch xml {
case .article:
if let url = attributes[.link] as? URL,
case let urlString = url.absoluteString {
return ["href": urlString]
} else {
return nil
}
default:
return nil
}
}
func enableTypingXMLs(_ inActiveXML: String) -> [String]? {
guard let xml = XML.init(rawValue: inActiveXML) else { return nil }
switch xml {
case .heading, .quote:
return [XML.bold.rawValue,
XML.italic.rawValue,
XML.paragraph.rawValue]
default:
return nil
}
}
func disableTypingXMLs(_ activeXML: String) -> [String]? {
guard let xml = XML.init(rawValue: activeXML) else { return nil }
switch xml {
case .heading, .quote:
return [XML.bold.rawValue,
XML.italic.rawValue,
XML.paragraph.rawValue]
default:
return nil
}
}
func inactiveTypingXMLs(_ activeXML: String) -> [String]? {
guard let xml = XML.init(rawValue: activeXML) else { return nil }
switch xml {
case .heading:
return [XML.quote.rawValue]
case .quote:
return [XML.heading.rawValue]
default:
return nil
}
}
func activeTypingXMLs(_ inactiveXML: String) -> [String]? {
return nil
}
}
<img src=\"https://rawImage.intro.png\" width=\"500\" height=\"500\"/>
Image xml has 3 kinds of attributes such as width, height, sourceURL(src) At first you have to make VEditorMediaContent inherited content for image
class VImageContent: VEditorMediaContent {
var xmlTag: String
var url: URL?
var width: CGFloat
var height: CGFloat
var ratio: CGFloat {
return height / width
}
required init(_ xmlTag: String, attributes: [String : String]) {
self.xmlTag = xmlTag
self.url = URL(string: attributes["src"] ?? "")
self.width = CGFloat(Int(attributes["width"] ?? "") ?? 1)
self.height = CGFloat(Int(attributes["height"] ?? "") ?? 1)
}
func parseAttributeToXML() -> [String : String] {
return ["src": url?.absoluteString ?? "",
"width": "\(Int(width))",
"height": "\(Int(height))"]
}
}
And you just define it on Editor Rule build methods
func build(_ xmlTag: String, attributes: [String : String]) -> VEditorMediaContent? {
switch xmlTag {
case "img":
return VImageContent(xmlTag, attributes: attributes)
default: return nil
}
}