From 86458684a09193956e1b9bb23ac11a7d10aa2a88 Mon Sep 17 00:00:00 2001 From: Pedro Aim Date: Mon, 11 Jan 2021 12:09:20 -0500 Subject: [PATCH] initial commit --- .../xcschemes/TheRichTextEditor.xcscheme | 105 ++++++ Package.swift | 6 +- README.md | 2 + .../Assets.xcassets/Contents.json | 6 + .../alignCenter.imageset/Contents.json | 21 ++ .../alignCenter.imageset/align-center.png | Bin 0 -> 4063 bytes .../alignLeft.imageset/Contents.json | 21 ++ .../alignLeft.imageset/align-left.png | Bin 0 -> 4068 bytes .../alignRight.imageset/Contents.json | 21 ++ .../alignRight.imageset/align-right.png | Bin 0 -> 4090 bytes .../bold.imageset/Contents.json | 21 ++ .../Assets.xcassets/bold.imageset/bold.png | Bin 0 -> 5339 bytes .../clear.imageset/Contents.json | 21 ++ .../clear.imageset/paragraph.png | Bin 0 -> 8709 bytes .../indent.imageset/Contents.json | 21 ++ .../indent.imageset/indent.png | Bin 0 -> 7169 bytes .../italic.imageset/Contents.json | 21 ++ .../italic.imageset/italic.png | Bin 0 -> 5419 bytes .../outdent.imageset/Contents.json | 21 ++ .../outdent.imageset/right-indent.png | Bin 0 -> 5881 bytes .../redo.imageset/Contents.json | 21 ++ .../Assets.xcassets/redo.imageset/redo.png | Bin 0 -> 6686 bytes .../undo.imageset/Contents.json | 21 ++ .../Assets.xcassets/undo.imageset/undo.png | Bin 0 -> 6722 bytes .../TheRichTextEditor/TheRichTextEditor.swift | 352 +++++++++++++++++- .../TheRichTextEditor/Utils/Extensions.swift | 21 ++ .../Views/AccessoryUICollectionViewCell.swift | 20 + .../Views/AccessoryUICollectionViewCell.xib | 38 ++ .../Views/CustomWebview.swift | 169 +++++++++ .../TheRichTextEditor/Web Resources/main.html | 69 ++++ .../TheRichTextEditor/Web Resources/main.js | 81 ++++ .../TheRichTextEditorTests.swift | 2 +- 32 files changed, 1077 insertions(+), 4 deletions(-) create mode 100644 .swiftpm/xcode/xcshareddata/xcschemes/TheRichTextEditor.xcscheme create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/alignCenter.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/alignCenter.imageset/align-center.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/alignLeft.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/alignLeft.imageset/align-left.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/alignRight.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/alignRight.imageset/align-right.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/bold.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/bold.imageset/bold.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/clear.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/clear.imageset/paragraph.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/indent.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/indent.imageset/indent.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/italic.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/italic.imageset/italic.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/outdent.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/outdent.imageset/right-indent.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/redo.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/redo.imageset/redo.png create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/undo.imageset/Contents.json create mode 100644 Sources/TheRichTextEditor/Assets.xcassets/undo.imageset/undo.png create mode 100644 Sources/TheRichTextEditor/Utils/Extensions.swift create mode 100644 Sources/TheRichTextEditor/Views/AccessoryUICollectionViewCell.swift create mode 100644 Sources/TheRichTextEditor/Views/AccessoryUICollectionViewCell.xib create mode 100644 Sources/TheRichTextEditor/Views/CustomWebview.swift create mode 100644 Sources/TheRichTextEditor/Web Resources/main.html create mode 100644 Sources/TheRichTextEditor/Web Resources/main.js diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/TheRichTextEditor.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/TheRichTextEditor.xcscheme new file mode 100644 index 0000000..3a6c63a --- /dev/null +++ b/.swiftpm/xcode/xcshareddata/xcschemes/TheRichTextEditor.xcscheme @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Package.swift b/Package.swift index 5b0d0e5..8d1feda 100644 --- a/Package.swift +++ b/Package.swift @@ -5,6 +5,9 @@ import PackageDescription let package = Package( name: "TheRichTextEditor", + platforms: [ + .iOS(.v11) + ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( @@ -20,7 +23,8 @@ let package = Package( // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "TheRichTextEditor", - dependencies: []), + dependencies: [], + resources: [.process("main.js"), .process("main.html")]), .testTarget( name: "TheRichTextEditorTests", dependencies: ["TheRichTextEditor"]), diff --git a/README.md b/README.md index 9737bae..6aedefd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # TheRichTextEditor A description of this package. + +
Icons made by bqlqn from www.flaticon.com
diff --git a/Sources/TheRichTextEditor/Assets.xcassets/Contents.json b/Sources/TheRichTextEditor/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Sources/TheRichTextEditor/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/TheRichTextEditor/Assets.xcassets/alignCenter.imageset/Contents.json b/Sources/TheRichTextEditor/Assets.xcassets/alignCenter.imageset/Contents.json new file mode 100644 index 0000000..74944a6 --- /dev/null +++ b/Sources/TheRichTextEditor/Assets.xcassets/alignCenter.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "align-center.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/TheRichTextEditor/Assets.xcassets/alignCenter.imageset/align-center.png b/Sources/TheRichTextEditor/Assets.xcassets/alignCenter.imageset/align-center.png new file mode 100644 index 0000000000000000000000000000000000000000..682c46c09bfe7e9c53adba798ec1d63348c734b9 GIT binary patch literal 4063 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 V^#l7c2mDQT?%1=vcq`+F{{ecvRObKy literal 0 HcmV?d00001 diff --git a/Sources/TheRichTextEditor/Assets.xcassets/bold.imageset/Contents.json b/Sources/TheRichTextEditor/Assets.xcassets/bold.imageset/Contents.json new file mode 100644 index 0000000..0acc732 --- /dev/null +++ b/Sources/TheRichTextEditor/Assets.xcassets/bold.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "bold.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/TheRichTextEditor/Assets.xcassets/bold.imageset/bold.png b/Sources/TheRichTextEditor/Assets.xcassets/bold.imageset/bold.png new file mode 100644 index 0000000000000000000000000000000000000000..5c9617f9d76dfd1ff0f84143e0358c4e0cac04d2 GIT binary patch literal 5339 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 WnQe&hEf)SB0(Sy`yyO`1#{U7cVm{9R literal 0 HcmV?d00001 diff --git a/Sources/TheRichTextEditor/Assets.xcassets/indent.imageset/Contents.json b/Sources/TheRichTextEditor/Assets.xcassets/indent.imageset/Contents.json new file mode 100644 index 0000000..6e0c680 --- /dev/null +++ b/Sources/TheRichTextEditor/Assets.xcassets/indent.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "indent.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/TheRichTextEditor/Assets.xcassets/indent.imageset/indent.png b/Sources/TheRichTextEditor/Assets.xcassets/indent.imageset/indent.png new file mode 100644 index 0000000000000000000000000000000000000000..bb9e21087d9d9b4e3e62d8dd80c9072dfdb25138 GIT binary patch literal 7169 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 literal 0 HcmV?d00001 diff --git a/Sources/TheRichTextEditor/Assets.xcassets/italic.imageset/Contents.json b/Sources/TheRichTextEditor/Assets.xcassets/italic.imageset/Contents.json new file mode 100644 index 0000000..bd31029 --- /dev/null +++ b/Sources/TheRichTextEditor/Assets.xcassets/italic.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "italic.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/TheRichTextEditor/Assets.xcassets/italic.imageset/italic.png b/Sources/TheRichTextEditor/Assets.xcassets/italic.imageset/italic.png new file mode 100644 index 0000000000000000000000000000000000000000..670b10d56162799fc02714ea0e034913112b737f GIT binary patch literal 5419 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 literal 0 HcmV?d00001 diff --git a/Sources/TheRichTextEditor/Assets.xcassets/redo.imageset/Contents.json b/Sources/TheRichTextEditor/Assets.xcassets/redo.imageset/Contents.json new file mode 100644 index 0000000..0cd92b1 --- /dev/null +++ b/Sources/TheRichTextEditor/Assets.xcassets/redo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "redo.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/TheRichTextEditor/Assets.xcassets/redo.imageset/redo.png b/Sources/TheRichTextEditor/Assets.xcassets/redo.imageset/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..3e982da454191804c0493155df11f1b43d9b1459 GIT binary patch literal 6686 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 literal 0 HcmV?d00001 diff --git a/Sources/TheRichTextEditor/Assets.xcassets/undo.imageset/Contents.json b/Sources/TheRichTextEditor/Assets.xcassets/undo.imageset/Contents.json new file mode 100644 index 0000000..ad7cac4 --- /dev/null +++ b/Sources/TheRichTextEditor/Assets.xcassets/undo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "undo.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Sources/TheRichTextEditor/Assets.xcassets/undo.imageset/undo.png b/Sources/TheRichTextEditor/Assets.xcassets/undo.imageset/undo.png new file mode 100644 index 0000000000000000000000000000000000000000..462a986115e0bfb483febcb7bd12621f8a8f0df1 GIT binary patch literal 6722 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 Tay#_n{|V{1!*36dQZD}w+gzS0 literal 0 HcmV?d00001 diff --git a/Sources/TheRichTextEditor/TheRichTextEditor.swift b/Sources/TheRichTextEditor/TheRichTextEditor.swift index 0ebcc00..b7017b7 100644 --- a/Sources/TheRichTextEditor/TheRichTextEditor.swift +++ b/Sources/TheRichTextEditor/TheRichTextEditor.swift @@ -1,3 +1,351 @@ -struct TheRichTextEditor { - var text = "Hello, World!" +// +// TheRichTextEditor.swift +// iOS-Email-Client +// +// Created by Pedro Iniguez on 12/17/20. +// Copyright © 2020 Criptext Inc. All rights reserved. +// + +import Foundation +import UIKit +import WebKit + +public protocol TheRichTextEditorDelegate: class { + func textDidChange(content: String) + func heightDidChange() + func editorDidLoad() + func scrollOffset(verticalOffset: CGFloat) +} + +fileprivate class WeakScriptMessageHandler: NSObject, WKScriptMessageHandler { + weak var delegate: WKScriptMessageHandler? + + init(delegate: WKScriptMessageHandler) { + self.delegate = delegate + } + + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + self.delegate?.userContentController(userContentController, didReceive: message) + } +} + +public class TheRichTextEditor: UIView, WKScriptMessageHandler, WKNavigationDelegate, UIScrollViewDelegate { + private static let textDidChange = "textDidChange" + private static let heightDidChange = "heightDidChange" + private static let previewDidChange = "previewDidChange" + private static let documentHasLoaded = "documentHasLoaded" + + private static let defaultHeight: CGFloat = 60 + + public weak var delegate: TheRichTextEditorDelegate? + public var height: CGFloat = TheRichTextEditor.defaultHeight + + public var placeholder: String? { + didSet { + webView.evaluateJavaScript("richeditor.setPlaceholderText('\(placeholder ?? "")')") + } + } + + private var textToLoad: String? + + public var html: String = "" { + didSet { + if webView.isLoading { + textToLoad = html + } else { + webView.evaluateJavaScript("richeditor.insertText(\"\(html.htmlEscapeQuotes)\");") + body = html + } + } + } + + public var preview: String = "" + public var body: String = "" + + var webView: WKWebView! + + public override init(frame: CGRect = .zero) { + super.init(frame: frame) + setup() + } + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setup() + } + + var enableAccessoryView: Bool { + set { + (webView as? CustomWebview)?.enableAccessoryView = newValue + } + + get { + return (webView as? CustomWebview)?.enableAccessoryView ?? false + } + } + + func setup() { + guard let scriptPath = Bundle.main.path(forResource: "main", ofType: "js"), + let scriptContent = try? String(contentsOfFile: scriptPath, encoding: String.Encoding.utf8), + let htmlPath = Bundle.main.path(forResource: "main", ofType: "html"), + let html = try? String(contentsOfFile: htmlPath, encoding: String.Encoding.utf8) + else { + fatalError("Unable to find javscript/html for text editor") + } + + let configuration = WKWebViewConfiguration() + configuration.userContentController.addUserScript( + WKUserScript(source: scriptContent, + injectionTime: .atDocumentEnd, + forMainFrameOnly: true + ) + ) + + webView = CustomWebview(frame: .zero, configuration: configuration) + (webView as? CustomWebview)?.toolbarDelegate = self + + [TheRichTextEditor.textDidChange, TheRichTextEditor.heightDidChange, TheRichTextEditor.previewDidChange, TheRichTextEditor.documentHasLoaded].forEach { + configuration.userContentController.add(WeakScriptMessageHandler(delegate: self), name: $0) + } + + webView.keyboardDisplayRequiresUserAction = false + webView.navigationDelegate = self + webView.isOpaque = false + webView.backgroundColor = .clear + webView.scrollView.maximumZoomScale = 1 + webView.scrollView.minimumZoomScale = 1 + webView.scrollView.showsHorizontalScrollIndicator = false + webView.scrollView.showsVerticalScrollIndicator = false + webView.scrollView.bounces = false + webView.scrollView.isScrollEnabled = false + webView.scrollView.delegate = self + + addSubview(webView) + webView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + webView.leadingAnchor.constraint(equalTo: leadingAnchor), + webView.topAnchor.constraint(equalTo: topAnchor, constant: 10), + webView.trailingAnchor.constraint(equalTo: trailingAnchor), + webView.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + + webView.loadHTMLString(html, baseURL: Bundle.main.bundleURL) + } + + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { + switch message.name { + case TheRichTextEditor.textDidChange: + guard let body = message.body as? String else { return } + self.body = body + delegate?.textDidChange(content: body) + case TheRichTextEditor.heightDidChange: + guard let height = message.body as? CGFloat else { return } + if (height + 20 != self.height) { + print(self.height) + self.height = height + 20 + delegate?.heightDidChange() + } + case TheRichTextEditor.previewDidChange: + guard let preview = message.body as? String else { return } + self.preview = preview + case TheRichTextEditor.documentHasLoaded: + delegate?.editorDidLoad() + default: + break + } + } + + public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + if let textToLoad = textToLoad { + self.textToLoad = nil + html = textToLoad + } + } + + public func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) { + scrollView.pinchGestureRecognizer?.isEnabled = false + } + + public func scrollViewDidScroll(_ scrollView: UIScrollView) { + if (scrollView.contentOffset.y != 0) { + delegate?.scrollOffset(verticalOffset: scrollView.contentOffset.y) + } + scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false) + } + + public func viewForZooming(in: UIScrollView) -> UIView? { + return nil + } + + public override func endEditing(_ force: Bool) -> Bool { + webView.endEditing(force) + return super.endEditing(force) + } + + public func setEditorFontColor(_ color: UIColor) { + webView.evaluateJavaScript("richeditor.setBaseTextColor('#\(color.toHexString())');", completionHandler: nil) + } + + public func setEditorBackgroundColor(_ color: UIColor) { + webView.evaluateJavaScript("richeditor.setBackgroundColor('#\(color.toHexString())');", completionHandler: nil) + } + + public func focus() { + webView.evaluateJavaScript("richeditor.focus();", completionHandler: nil) + } + + public func focus(at: CGPoint) { + webView.evaluateJavaScript("richeditor.focusAtPoint(\(at.x), \(at.y));", completionHandler: nil) + } + + public func runCommand(_ command: String) { + webView.evaluateJavaScript("document.execCommand('\(command)', false, null);", completionHandler: nil) + } +} + +extension TheRichTextEditor: WebviewToolbarDelegate { + func onUndoPress() { + self.runCommand("undo") + } + + func onRedoPress() { + self.runCommand("redo") + } + + func onTextAlignCenter() { + self.runCommand("justifyCenter") + } + + func onIndentPress() { + self.runCommand("indent") + } + + func onOutdentPress() { + self.runCommand("outdent") + } + + func onClearPress() { + self.runCommand("removeFormat") + } + + func onItalicPress() { + self.runCommand("italic") + } + + func onTextAlignLeft() { + self.runCommand("justifyLeft") + } + + func onTextAlignRight() { + self.runCommand("justifyRight") + } + + func onBoldPress() { + self.runCommand("bold") + } +} + +fileprivate extension String { + + var htmlToPlainText: String { + return [ + ("(<[^>]*>)|(&\\w+;)", " "), + ("[ ]+", " ") + ].reduce(self) { + try! $0.replacing(pattern: $1.0, with: $1.1) + }.resolvedHTMLEntities + } + + var resolvedHTMLEntities: String { + return self + .replacingOccurrences(of: "'", with: "'") + .replacingOccurrences(of: "'", with: "'") + .replacingOccurrences(of: "&", with: "&") + .replacingOccurrences(of: " ", with: " ") + } + + func replacing(pattern: String, with template: String) throws -> String { + let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive) + return regex.stringByReplacingMatches(in: self, options: [], range: NSRange(0.. Void +typealias NewClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void + +extension WKWebView{ + var keyboardDisplayRequiresUserAction: Bool? { + get { + return self.keyboardDisplayRequiresUserAction + } + set { + self.setKeyboardRequiresUserInteraction(newValue ?? true) + } + } + + func setKeyboardRequiresUserInteraction( _ value: Bool) { + guard let WKContentView: AnyClass = NSClassFromString("WKContentView") else { + print("keyboardDisplayRequiresUserAction extension: Cannot find the WKContentView class") + return + } + // For iOS 10, * + let sel_10: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:") + // For iOS 11.3, * + let sel_11_3: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:") + // For iOS 12.2, * + let sel_12_2: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:") + // For iOS 13.0, * + let sel_13_0: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:") + + if let method = class_getInstanceMethod(WKContentView, sel_10) { + let originalImp: IMP = method_getImplementation(method) + let original: OldClosureType = unsafeBitCast(originalImp, to: OldClosureType.self) + let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in + original(me, sel_10, arg0, !value, arg2, arg3) + } + let imp: IMP = imp_implementationWithBlock(block) + method_setImplementation(method, imp) + } + + if let method = class_getInstanceMethod(WKContentView, sel_11_3) { + let originalImp: IMP = method_getImplementation(method) + let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self) + let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in + original(me, sel_11_3, arg0, !value, arg2, arg3, arg4) + } + let imp: IMP = imp_implementationWithBlock(block) + method_setImplementation(method, imp) + } + + if let method = class_getInstanceMethod(WKContentView, sel_12_2) { + let originalImp: IMP = method_getImplementation(method) + let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self) + let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in + original(me, sel_12_2, arg0, !value, arg2, arg3, arg4) + } + let imp: IMP = imp_implementationWithBlock(block) + method_setImplementation(method, imp) + } + + if let method = class_getInstanceMethod(WKContentView, sel_13_0) { + let originalImp: IMP = method_getImplementation(method) + let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self) + let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in + original(me, sel_13_0, arg0, !value, arg2, arg3, arg4) + } + let imp: IMP = imp_implementationWithBlock(block) + method_setImplementation(method, imp) + } + } } diff --git a/Sources/TheRichTextEditor/Utils/Extensions.swift b/Sources/TheRichTextEditor/Utils/Extensions.swift new file mode 100644 index 0000000..6072b78 --- /dev/null +++ b/Sources/TheRichTextEditor/Utils/Extensions.swift @@ -0,0 +1,21 @@ +// +// File.swift +// +// +// Created by Pedro Iniguez on 12/29/20. +// + +import Foundation +import UIKit + +extension UIColor { + func toHexString() -> String { + var r:CGFloat = 0 + var g:CGFloat = 0 + var b:CGFloat = 0 + var a:CGFloat = 0 + getRed(&r, green: &g, blue: &b, alpha: &a) + let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0 + return String(format:"%06x", rgb) + } +} diff --git a/Sources/TheRichTextEditor/Views/AccessoryUICollectionViewCell.swift b/Sources/TheRichTextEditor/Views/AccessoryUICollectionViewCell.swift new file mode 100644 index 0000000..8552274 --- /dev/null +++ b/Sources/TheRichTextEditor/Views/AccessoryUICollectionViewCell.swift @@ -0,0 +1,20 @@ +// +// AccessoryUICollectionViewCell.swift +// iOS-Email-Client +// +// Created by Pedro Iniguez on 12/28/20. +// Copyright © 2020 Criptext Inc. All rights reserved. +// + +import Foundation +import UIKit + +class AccessoryUICollectionViewCell: UICollectionViewCell { + @IBOutlet weak var iconImageView: UIImageView! + + override func awakeFromNib() { + super.awakeFromNib() + + backgroundColor = .clear + } +} diff --git a/Sources/TheRichTextEditor/Views/AccessoryUICollectionViewCell.xib b/Sources/TheRichTextEditor/Views/AccessoryUICollectionViewCell.xib new file mode 100644 index 0000000..6890ec9 --- /dev/null +++ b/Sources/TheRichTextEditor/Views/AccessoryUICollectionViewCell.xib @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/TheRichTextEditor/Views/CustomWebview.swift b/Sources/TheRichTextEditor/Views/CustomWebview.swift new file mode 100644 index 0000000..4c1101c --- /dev/null +++ b/Sources/TheRichTextEditor/Views/CustomWebview.swift @@ -0,0 +1,169 @@ +// +// CustomWebview.swift +// iOS-Email-Client +// +// Created by Pedro Iniguez on 12/28/20. +// Copyright © 2020 Criptext Inc. All rights reserved. +// + +import Foundation +import UIKit +import WebKit + +protocol WebviewToolbarDelegate: class { + func onBoldPress() + func onItalicPress() + func onTextAlignLeft() + func onTextAlignRight() + func onTextAlignCenter() + func onIndentPress() + func onOutdentPress() + func onClearPress() + func onUndoPress() + func onRedoPress() +} + +class CustomWebview: WKWebView { + + enum Modifier { + case bold + case italic + case textAlignLeft + case textAlignCenter + case textAlignRight + case indent + case outdent + case clear + case undo + case redo + + var desc: String { + switch(self) { + case .bold: + return "Bold" + case .italic: + return "Italic" + case .textAlignLeft: + return "Left" + case .textAlignCenter: + return "Center" + case .textAlignRight: + return "Right" + case .indent: + return "Indent" + case .outdent: + return "Outdent" + case .clear: + return "Clear" + case .undo: + return "Undo" + case .redo: + return "Redo" + } + } + + var image: UIImage { + switch(self) { + case .bold: + return UIImage(named: "bold")! + case .italic: + return UIImage(named: "italic")! + case .textAlignLeft: + return UIImage(named: "alignLeft")! + case .textAlignCenter: + return UIImage(named: "alignCenter")! + case .textAlignRight: + return UIImage(named: "alignRight")! + case .indent: + return UIImage(named: "indent")! + case .outdent: + return UIImage(named: "outdent")! + case .clear: + return UIImage(named: "clear")! + case .undo: + return UIImage(named: "undo")! + case .redo: + return UIImage(named: "redo")! + } + } + } + weak var toolbarDelegate: WebviewToolbarDelegate? = nil + var enableAccessoryView = true + var accessoryView: UIView? = nil + var modifiers: [Modifier] = [.bold, .italic, .textAlignRight, .textAlignCenter, .textAlignLeft, .indent, .outdent, .clear, .undo, .redo] + + override var inputAccessoryView: UIView? { + get { + if enableAccessoryView, + accessoryView == nil { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + + let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 100, height: 50), collectionViewLayout: layout) + //collectionView.backgroundColor = theme.background + collectionView.dataSource = self + collectionView.delegate = self + collectionView.isScrollEnabled = true + collectionView.bounces = false + collectionView.showsHorizontalScrollIndicator = false + + let accessoryNib = UINib(nibName: "AccessoryUICollectionViewCell", bundle: nil) + collectionView.register(accessoryNib, forCellWithReuseIdentifier: "accessoryCell") + + accessoryView = collectionView + } + return accessoryView + } + set { + accessoryView = newValue + } + } +} + +extension CustomWebview: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return modifiers.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let modifier = modifiers[indexPath.item] + + let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "accessoryCell", for: indexPath) as! AccessoryUICollectionViewCell + collectionCell.iconImageView.image = modifier.image + return collectionCell + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: 40, height: 50) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5) + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let modifier = modifiers[indexPath.item] + switch modifier { + case .bold: + self.toolbarDelegate?.onBoldPress() + case .italic: + self.toolbarDelegate?.onItalicPress() + case .textAlignLeft: + self.toolbarDelegate?.onTextAlignLeft() + case .textAlignRight: + self.toolbarDelegate?.onTextAlignRight() + case .textAlignCenter: + self.toolbarDelegate?.onTextAlignCenter() + case .indent: + self.toolbarDelegate?.onIndentPress() + case .outdent: + self.toolbarDelegate?.onOutdentPress() + case .clear: + self.toolbarDelegate?.onClearPress() + case .undo: + self.toolbarDelegate?.onUndoPress() + case .redo: + self.toolbarDelegate?.onRedoPress() + } + } +} diff --git a/Sources/TheRichTextEditor/Web Resources/main.html b/Sources/TheRichTextEditor/Web Resources/main.html new file mode 100644 index 0000000..48e4c99 --- /dev/null +++ b/Sources/TheRichTextEditor/Web Resources/main.html @@ -0,0 +1,69 @@ + + + + + + + + +
+
+ + diff --git a/Sources/TheRichTextEditor/Web Resources/main.js b/Sources/TheRichTextEditor/Web Resources/main.js new file mode 100644 index 0000000..81c9393 --- /dev/null +++ b/Sources/TheRichTextEditor/Web Resources/main.js @@ -0,0 +1,81 @@ +var richeditor = {}; +var editor = document.getElementById("editor"); + +window.onload = function() { + window.webkit.messageHandlers.documentHasLoaded.postMessage("ready"); +}; + +richeditor.updatePlaceholder = function() { + if (editor.innerHTML.indexOf('img') !== -1 || (editor.textContent.length > 0 && editor.innerHTML.length > 0)) { + editor.classList.remove("placeholder"); + } else { + editor.classList.add("placeholder"); + } +} + +richeditor.insertText = function(text) { + editor.innerHTML = text; + richeditor.updatePlaceholder(); + window.webkit.messageHandlers.heightDidChange.postMessage(document.body.offsetHeight); +} + +richeditor.setBaseTextColor = function(color) { + editor.style.color = color; +} + +richeditor.setBaseTextColor = function(color) { + editor.style.color = color; +} + +richeditor.setBackgroundColor = function(color) { + editor.style.backgroundColor = color; +} + +richeditor.focus = function() { + var range = document.createRange(); + range.selectNodeContents(editor); + range.collapse(false); + var selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + editor.focus(); +} + +richeditor.focusAtPoint = function(x, y) { + var range = document.caretRangeFromPoint(x, y) || document.createRange(); + var selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + editor.focus(); +}; + +richeditor.setPlaceholderText = function(text) { + editor.setAttribute("placeholder", text); +}; + +editor.addEventListener("input", function() { + window.webkit.messageHandlers.textDidChange.postMessage(editor.innerHTML); + window.webkit.messageHandlers.previewDidChange.postMessage(editor.innerText); + richeditor.updatePlaceholder(); +}, false) + +document.addEventListener("selectionchange", function() { + window.webkit.messageHandlers.heightDidChange.postMessage(editor.clientHeight); +}, false); + +document.getElementById("not-editor").addEventListener("click", () => { + if (editor == document.activeElement) { + editor.blur(); + } else { + editor.focus(); + document.execCommand('selectAll', false, null); + document.getSelection().collapseToEnd(); + } +}) + +document.addEventListener('paste', e => { + var items = (event.clipboardData || event.originalEvent.clipboardData).items; + if (items[0] && items[0].kind === 'file') { + e.preventDefault(); + } +}); diff --git a/Tests/TheRichTextEditorTests/TheRichTextEditorTests.swift b/Tests/TheRichTextEditorTests/TheRichTextEditorTests.swift index 7071781..cba4bb8 100644 --- a/Tests/TheRichTextEditorTests/TheRichTextEditorTests.swift +++ b/Tests/TheRichTextEditorTests/TheRichTextEditorTests.swift @@ -6,7 +6,7 @@ final class TheRichTextEditorTests: XCTestCase { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct // results. - XCTAssertEqual(TheRichTextEditor().text, "Hello, World!") + //XCTAssertEqual(TheRichTextEditor(), "Hello, World!") } static var allTests = [