-
Notifications
You must be signed in to change notification settings - Fork 31
[1]: Quick Start
Ha Hyun soo edited this page Jan 10, 2019
·
3 revisions
struct EditorRule: VEditorRule {
enum XML: String, CaseIterable {
case paragraph = "p"
case bold = "b"
case image = "img"
}
var defaultStyleXMLTag: String {
return XML.paragraph.rawValue
}
var linkStyleXMLTag: String? {
return nil
}
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)])
default:
return nil
}
}
func build(_ xmlTag: String, attributes: [String : String]) -> VEditorMediaContent? {
return nil
}
func parseAttributeToXML(_ xmlTag: String,
attributes: [NSAttributedString.Key : Any]) -> [String : String]? {
return nil
}
func enableTypingXMLs(_ inActiveXML: String) -> [String]? {
return nil
}
func disableTypingXMLs(_ activeXML: String) -> [String]? {
return nil
}
func inactiveTypingXMLs(_ activeXML: String) -> [String]? {
return nil
}
func activeTypingXMLs(_ inactiveXML: String) -> [String]? {
return nil
}
}
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 define on build methods
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)
default:
return nil
}
}
example image tag xml
<img src=\"https://imageurl.png\" width=\"500\" height=\"500\"/>
class EditorNodeController: ASViewController<VEditorNode> {
override init() {
let rule = EditorRule()
self.controlAreaNode = EditorControlAreaNode(rule: rule)
super.init(node: .init(editorRule: rule, controlAreaNode: nil))
}
- STEP4: Make a TypingController
import Foundation
import AsyncDisplayKit
import VEditorKit
class EditorControlAreaNode: ASDisplayNode {
struct Const {
static let insets: UIEdgeInsets = .init(top: 10.0, left: 5.0, bottom: 5.0, right: 10.0)
static let controlSize: CGSize = .init(width: 44.0, height: 44.0)
}
lazy var boldNode: VEditorTypingControlNode = {
let node = VEditorTypingControlNode(EditorRule.XML.bold.rawValue,
rule: rule)
node.style.preferredSize = Const.controlSize
node.setTitle("B", with: UIFont.systemFont(ofSize: 20.0), with: .black, for: .normal)
node.setTitle("B", with: UIFont.systemFont(ofSize: 20.0), with: .lightGray, for: .disabled)
node.setTitle("B", with: UIFont.systemFont(ofSize: 20.0, weight: .bold), with: .blue, for: .selected)
return node
}()
lazy var seperateLineNode: ASDisplayNode = {
let node = ASDisplayNode()
node.backgroundColor = .lightGray
return node
}()
lazy var scrollNode: ASScrollNode = {
let node = ASScrollNode()
node.automaticallyManagesContentSize = true
node.automaticallyManagesSubnodes = true
node.scrollableDirections = [.left, .right]
return node
}()
lazy var photoLoadControlNode: ASButtonNode = {
let node = ASButtonNode()
node.setImage(#imageLiteral(resourceName: "image.png"), for: .normal)
node.style.preferredSize = Const.controlSize
return node
}()
lazy var dismissNode: ASButtonNode = {
let node = ASButtonNode()
node.setImage(#imageLiteral(resourceName: "keyboard.png"), for: .normal)
node.style.preferredSize = Const.controlSize
return node
}()
var typingControlNodes: [VEditorTypingControlNode] {
return [boldNode]
}
let rule: EditorRule
init(rule: EditorRule) {
self.rule = rule
super.init()
self.automaticallyManagesSubnodes = true
self.backgroundColor = .white
self.scrollNode.layoutSpecBlock = { [weak self] (_, _) -> ASLayoutSpec in
return self?.controlButtonsGroupLayoutSpec() ?? ASLayoutSpec()
}
}
override func didLoad() {
super.didLoad()
self.scrollNode.view.showsVerticalScrollIndicator = false
self.scrollNode.view.showsHorizontalScrollIndicator = false
}
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
scrollNode.style.flexShrink = 1.0
scrollNode.style.flexGrow = 0.0
dismissNode.style.flexShrink = 1.0
dismissNode.style.flexGrow = 0.0
let stackLayout = ASStackLayoutSpec(direction: .horizontal,
spacing: 5.0,
justifyContent: .spaceBetween,
alignItems: .stretch,
children: [scrollNode, dismissNode])
let controlAreaLayout = ASInsetLayoutSpec(insets: Const.insets, child: stackLayout)
seperateLineNode.style.height = .init(unit: .points, value: 0.5)
return ASStackLayoutSpec(direction: .vertical,
spacing: 0.0,
justifyContent: .start,
alignItems: .stretch,
children: [seperateLineNode,
controlAreaLayout])
}
private func controlButtonsGroupLayoutSpec() -> ASLayoutSpec {
return ASStackLayoutSpec(direction: .horizontal,
spacing: 10.0,
justifyContent: .start,
alignItems: .start,
children: [photoLoadControlNode] + typingControlNodes)
}
}
and define on viewController
class EditorNodeController: ASViewController<VEditorNode> {
let controlAreaNode: EditorControlAreaNode
init(isEditMode: Bool = true, xmlString: String? = nil) {
let rule = EditorRule()
self.controlAreaNode = EditorControlAreaNode(rule: rule)
super.init(node: .init(editorRule: rule, controlAreaNode: controlAreaNode))
}
class EditorNodeController: ASViewController<VEditorNode> {
let controlAreaNode: EditorControlAreaNode
init(isEditMode: Bool = true, xmlString: String? = nil) {
let rule = EditorRule()
self.controlAreaNode = EditorControlAreaNode(rule: rule)
super.init(node: .init(editorRule: rule, controlAreaNode: controlAreaNode))
self.node.delegate = self <---- @HERE
}
extension EditorNodeController: VEditorNodeDelegate {
func getRegisterTypingControls() -> [VEditorTypingControlNode]? {
return controlAreaNode.typingControlNodes
}
func dismissKeyboardNode() -> ASControlNode? {
return controlAreaNode.dismissNode
}
func placeholderCellNode(_ content: VEditorPlaceholderContent, indexPath: IndexPath) -> VEditorMediaPlaceholderNode? {
return nil
}
func contentCellNode(_ content: VEditorContent, indexPath: IndexPath) -> ASCellNode? {
switch content {
case let text as NSAttributedString:
return VEditorTextCellNode(isEdit: true,
placeholderText: nil,
attributedText: text,
rule: self.node.editorRule,
regexDelegate: self,
automaticallyGenerateLinkPreview: true)
.setContentInsets(.zero)
case let imageNode as VImageContent:
return VEditorImageNode(isEdit: true)
.setContentInsets(.zero)
.setTextInsertionHeight(16.0)
.setURL(imageNode.url)
.setMediaRatio(imageNode.ratio)
.setPlaceholderColor(.lightGray)
.setBackgroundColor(.lightGray)
default:
return nil
}
}
}