123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- //
- // RichTextView.swift
- // RichTextView
- //
- // Created by Ahmed Elkady on 2018-11-08.
- // Copyright © 2018 Top Hat. All rights reserved.
- //
- import SnapKit
- import WebKit
- public class RichTextView: UIView {
- // MARK: - Properties
- public private(set) var input: String
- private(set) var richTextParser: RichTextParser
- private(set) var textColor: UIColor
- private(set) var isSelectable: Bool
- private(set) var isEditable: Bool
- public weak var textViewDelegate: RichTextViewDelegate?
- private(set) var errors: [ParsingError]?
- // MARK: - Constants
- private enum VideoProperties {
- static let defaultAspectRatio = 9.0/16.0
- }
- // MARK: - Init
- public init(input: String = "",
- latexParser: LatexParserProtocol = LatexParser(),
- font: UIFont = UIFont.systemFont(ofSize: UIFont.systemFontSize),
- textColor: UIColor = UIColor.black,
- isSelectable: Bool = true,
- isEditable: Bool = false,
- latexTextBaselineOffset: CGFloat = 0,
- interactiveTextColor: UIColor = UIColor.blue,
- textViewDelegate: RichTextViewDelegate? = nil,
- customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil,
- frame: CGRect,
- completion: (([ParsingError]?) -> Void)? = nil) {
- self.input = input
- self.isSelectable = isSelectable
- self.isEditable = isEditable
- self.richTextParser = RichTextParser(
- latexParser: latexParser,
- font: font,
- textColor: textColor,
- latexTextBaselineOffset: latexTextBaselineOffset,
- interactiveTextColor: interactiveTextColor,
- customAdditionalAttributes: customAdditionalAttributes
- )
- self.textColor = textColor
- self.textViewDelegate = textViewDelegate
- super.init(frame: frame)
- self.setupSubviews()
- completion?(self.errors)
- }
- required init?(coder aDecoder: NSCoder) {
- self.input = ""
- self.richTextParser = RichTextParser()
- self.textColor = UIColor.black
- self.isSelectable = true
- self.isEditable = false
- super.init(coder: aDecoder)
- self.setupSubviews()
- }
- // MARK: - Helpers
- public func update(input: String? = nil,
- latexParser: LatexParserProtocol? = nil,
- font: UIFont? = nil,
- textColor: UIColor? = nil,
- latexTextBaselineOffset: CGFloat? = nil,
- interactiveTextColor: UIColor? = nil,
- customAdditionalAttributes: [String: [NSAttributedString.Key: Any]]? = nil,
- completion: (([ParsingError]?) -> Void)? = nil) {
- self.input = input ?? self.input
- self.richTextParser = RichTextParser(
- latexParser: latexParser ?? self.richTextParser.latexParser,
- font: font ?? self.richTextParser.font,
- textColor: textColor ?? self.textColor,
- latexTextBaselineOffset: latexTextBaselineOffset ?? self.richTextParser.latexTextBaselineOffset,
- interactiveTextColor: interactiveTextColor ?? self.richTextParser.interactiveTextColor,
- customAdditionalAttributes: customAdditionalAttributes ?? self.richTextParser.customAdditionalAttributes
- )
- self.textColor = textColor ?? self.textColor
- self.subviews.forEach { $0.removeFromSuperview() }
- self.setupSubviews()
- completion?(self.errors)
- }
- private func setupSubviews() {
- let subviews = self.generateViews()
- for (index, subview) in subviews.enumerated() {
- self.addSubview(subview)
- subview.snp.makeConstraints { make in
- if index == 0 {
- make.top.equalTo(self)
- } else {
- make.top.equalTo(subviews[index - 1].snp.bottom)
- }
- make.width.equalTo(self)
- make.centerX.equalTo(self)
- if subview is WKWebView {
- make.height.equalTo(self.snp.width).multipliedBy(VideoProperties.defaultAspectRatio)
- }
- if index == subviews.count - 1 {
- make.bottom.equalTo(self)
- }
- }
- subview.backgroundColor = UIColor.clear
- }
- self.enableAccessibility()
- }
- func generateViews() -> [UIView] {
- return self.richTextParser.getRichDataTypes(from: self.input).compactMap { (richDataType: RichDataType) -> UIView? in
- switch richDataType {
- case .video(let tag, let error):
- self.appendError(error)
- let webView = WKWebViewGenerator.getWebView(from: tag)
- if webView == nil {
- self.appendError(ParsingError.webViewGeneration(link: tag))
- }
- return webView
- case .text(let richText, let font, let errors):
- self.appendErrors(errors)
- return UITextViewGenerator.getTextView(
- from: richText,
- font: font,
- textColor: self.textColor,
- isSelectable: self.isSelectable,
- isEditable: self.isEditable,
- textViewDelegate: self.textViewDelegate
- )
- }
- }
- }
- private func appendErrors(_ errors: [ParsingError]?) {
- if let errors = errors {
- if self.errors == nil {
- self.errors = [ParsingError]()
- }
- self.errors?.append(contentsOf: errors)
- }
- }
- private func appendError(_ error: ParsingError?) {
- if let error = error {
- if self.errors == nil {
- self.errors = [ParsingError]()
- }
- self.errors?.append(error)
- }
- }
- private func enableAccessibility() {
- self.isAccessibilityElement = true
- self.accessibilityValue = nil
- for view in self.subviews {
- if let accessibilityValue = view.accessibilityValue, !accessibilityValue.isEmpty {
- if self.accessibilityValue == nil {
- self.accessibilityValue = accessibilityValue
- } else {
- self.accessibilityValue?.append(accessibilityValue)
- }
- }
- }
- }
- }
|