[3]: VEditorNode - GeekTree0101/VEditorKit GitHub Wiki

Intro

1. Create ViewController

Case 1: Texture(AsyncDisplayKit)

class VC: ASViewController<VEditorNode> {
   init() {
         super.init(node: .init(editorRule: Rule(), controlAreaNode: node))
   }

Case 2: UIKit

class VC: UIViewController {
    lazy var node = VEditorNode.init(editorRule: Rule(), controlAreaNode: node)

    override func viewDidLoad() {
         super.viewDidLoad()
         node.view.snp <--- SnapKit or constraints else
    }
}

2. Inherit VEditorNodeDelegate

// MARK: VEditotKit EditorNodeDelegate
public protocol VEditorNodeDelegate: class {
    
    func getRegisterTypingControls() -> [VEditorTypingControlNode]?
    func dismissKeyboardNode() -> ASControlNode?
    func placeholderCellNode(_ content: VEditorPlaceholderContent,
                             indexPath: IndexPath) -> VEditorMediaPlaceholderNode?
    func contentCellNode(_ content: VEditorContent,
                         indexPath: IndexPath) -> ASCellNode?
}
  • getRegisterTypingControls: You have to return Array of VEditorTypingControlNode subclass or class

VEditorTypingControlNode의 subclass또는 객체의 버튼들을 반환합니다.

  • dismissKeyboardNode: Keyboard dismiss controlNode

키보드를 Dismiss하는 버튼을 반환합니다.

  • placeholderCellNode: return placeholderNode (VEditorMediaPlaceholderNode subclass)

서버단에서 네트워크콜이 필요한 Content에 대한 Placeholder를 반환합니다. 서버호출 성공 및 실패시 내부적으로 교체 및 삭제됩니다.

  • contentCellNode: return content cell object such as attributedString, image, video etc

AttributedString, Image, Video 등등의 Cell을 반환합니다.

Example

extension VC: VEditorNodeDelegate {
    
    func getRegisterTypingControls() -> [VEditorTypingControlNode]? {
        return controlAreaNode.typingControlNodes
    }
    
    func dismissKeyboardNode() -> ASControlNode? {
        return controlAreaNode.dismissNode
    }
    
    func placeholderCellNode(_ content: VEditorPlaceholderContent, indexPath: IndexPath) -> VEditorMediaPlaceholderNode? {
        guard let xml = EditorRule.XML.init(rawValue: content.xmlTag) else { return nil }
    
        switch xml {
        case .article:
            guard let url = content.model as? URL else { return nil }
            return EditorOpenGraphPlaceholder(xmlTag: EditorRule.XML.opengraph.rawValue,
                                              url: url)
        default:
            break
        }
        return nil
    }
    
    func contentCellNode(_ content: VEditorContent, indexPath: IndexPath) -> ASCellNode? {
        switch content {
        case let text as NSAttributedString:
            return VEditorTextCellNode(isEdit: isEditMode)
        case let imageNode as VImageContent:
            return VEditorImageNode(isEdit: isEditMode)
        case let videoNode as VVideoContent:
            return VEditorVideoNode(isEdit: isEditMode)
        case let ogObjectNode as VOpenGraphContent:
            return VEditorOpenGraphNode(isEdit: isEditMode)
        default:
            return nil
        }
    }
}

placholder example screenshot

3. Convenience editor manage methods

fetchNewContent or fetchNewContents

    public enum MediaAppendScope {
        case automatic
        case last
        case first
        case insert(IndexPath)
    }

You don't need IndexPath calculate, just put MediaAppendScope
IndexPath를 신경쓰지않고 원하는 MediaAppendScope값만 넣으면 MediaContent가 알아서 정확한 위치에 들어갑니다. More see about media content

  • automatic: will append at last or insert onto ActiveTextView cursor location

마지막에 붙거나, ActiveTextView가 있으면 cursor의 location에 삽입됩니다.

  • last: append
  • first: insert at zero
  • insert: Customized IndexPath Insertion
    open func fetchNewContent(_ content: VEditorContent,
                              scope: MeidaAppendScope,
                              section: Int = 0,
                              scrollPosition: UITableView.ScrollPosition = .bottom,
                              animated: Bool = true) {
    }


    open func fetchNewContents(_ contents: [VEditorContent],
                               scope: MeidaAppendScope,
                               section: Int = 0,
                               scrollPosition: UITableView.ScrollPosition = .bottom,
                               animated: Bool = true) {
    }

loadActiveTextCellNode()

You can get first responder UITextView cell

let node = VEditorNode.init(...)
let cell = node.loadActiveTextCellNode()

insertLinkOnActiveTextSelectedRange(_ url: URL)

You can insert url on activeTextView selectedRange
활성화된 텍스트뷰의 선택된 영역에 URL를 삽입합니다.

fetchNewActiveTextNode(_ node: VEditorTextCellNode)

Will become first responder with resign before TextView
이전에 활성화된 텍스트뷰를 비활성화시키고 자동으로 새로운 텍스트뷰를 활성화시킵니다.

resignActiveTextNode()

Resign before activeTextNode
활성화된 텍스트뷰를 비활성화시킵니다.

parseXMLString(_ xmlString: String)

Parse XMLString with render Editor UI
XMLString을 파싱하고 Editor UI를 랜더링합니다.

synchronizeFetchContents

Synchronize all of contents
에디터에 기록된 모든 속성과 Contents를 동기화합니다.

buildXML(_ customRule: VEditorRule? = nil, packageTag: String)

Building XMLString from Editor Contents with EditorRule. you can make capsuled xmlString with packageTag 에디터의 모든 컨텐츠를 룰에 따라서 XMLString으로 빌드합니다. 또한 packageTag를 정의해서 캡슐화할 수 있습니다.

4. Build XML Tip

let node: VEditorNode = ....

// STEP 1: synchronize contents
self.node.synchronizeFetchContents { [weak self] () in
            
            // STEP 2: build XML
            guard let output = self?.node.buildXML(packageTag: "content") else {
                return
            }
            let vc = XMLViewController.init(output)
            self?.navigationController?.pushViewController(vc, animated: true)
        }
⚠️ **GitHub.com Fallback** ⚠️