diff --git a/README.md b/README.md
index a31c3321..913af8e4 100755
--- a/README.md
+++ b/README.md
@@ -141,6 +141,202 @@ let clearAllItem = RichEditorOptionItem(image: UIImage(named: "clear"), title: "
toolbar.options = [clearAllItem]
```
+Using RichEditorView in SwiftUI
+--------------------
+
+`RichEditorView` can be easily integrated into your SwiftUI projects using the `SwiftUIRichEditor` and `SwiftUIRichEditorToolbar` wrappers.
+
+Start by creating a state object to manage the editor's content and properties.
+
+
+
+```swift
+import SwiftUI
+import RichEditorView
+
+class RichEditorState: ObservableObject {
+ @Published var htmlContent: String = ""
+ @Published var isEditing: Bool = false
+ @Published var editorHeight: CGFloat = 0
+
+ weak var editor: RichEditorView?
+}
+```
+
+Then, in your SwiftUI `View`, you can use `SwiftUIRichEditor` and `SwiftUIRichEditorToolbar` as follows:
+
+```swift
+import SwiftUI
+import RichEditorView
+
+struct ContentView: View {
+
+ // Rich text editor state management
+ @StateObject private var editorState = RichEditorState(
+ htmlContent: """
+
+ - This is the first line for testing styles.
+ - Bold this line.
+ - Make this line italic.
+ - Underline this one.
+ Apply a strikethrough here.
+ - This is for subscript test: H2O.
+ - And this is for superscript test: E=mc2.
+ - Let's test text color on this sentence.
+ - Now, let's try a background color.
+ Heading 1 will be applied here.
+ This should be Heading 2.
+ And here comes Heading 3.
+ Indent this paragraph.
+ - This is a standard line.
+ - This is the fifteenth item in our list.
+ - A slightly longer sentence to check wrapping and alignment.
+ Left align this text.
+ Center align this text.
+ Right align this text.
+ - Let's see how a hyperlink looks.
+ - This line contains some special characters: @#$%^&*().
+ - A sentence with multiple formats.
+ - The twenty-third line to test scrolling behavior.
+ - Almost there, just one more line to go.
+ - This is the final line for testing!
+
+ """
+ )
+
+ // State variables for ColorPicker
+ @State private var foregroundColor: Color = .black
+ @State private var highlightColor: Color = .white
+
+ // State variables to control color picker display
+ @State private var showTextColorPicker: Bool = false
+ @State private var showBackgroundColorPicker: Bool = false
+
+ // Toolbar options configuration - using all available options
+ private let toolbarOptions: [RichEditorDefaultOption] = RichEditorDefaultOption.all
+
+ var body: some View {
+ VStack(spacing: 0) {
+ // Toolbar section wrapped in VStack
+ VStack(spacing: 0) {
+ SwiftUIRichEditorToolbar(
+ options: toolbarOptions,
+ barTintColor: .systemBackground,
+ editor: editorState.editor,
+ onTextColorChange: {
+ // Show text color picker
+ showTextColorPicker = true
+ },
+ onBackgroundColorChange: {
+ // Show background color picker
+ showBackgroundColorPicker = true
+ },
+ onImageInsert: {
+ // Handle image insertion
+ print("Image insertion")
+ },
+ onLinkInsert: {
+ // Handle link insertion
+ print("Link insertion")
+ }
+ )
+ .frame(height: 44)
+ .background(
+ Rectangle()
+ .fill(Color(.systemBackground))
+ .overlay(
+ Rectangle()
+ .stroke(Color(.separator), lineWidth: 1)
+ )
+ )
+ }
+ .padding(.horizontal, 16)
+ .padding(.top, 8)
+ .zIndex(1) // Ensure toolbar is always on top
+
+ // Divider between toolbar and editor
+ Divider()
+ .padding(.horizontal, 16)
+
+ // Editor section wrapped in VStack
+ VStack(spacing: 0) {
+ SwiftUIRichEditor(
+ htmlContent: $editorState.htmlContent,
+ isEditing: $editorState.isEditing,
+ placeholder: "Enter content...",
+ isScrollEnabled: true,
+ editingEnabled: true,
+ backgroundColor: .systemGray6,
+ onContentChange: { content in
+ print("Content changed: \(content)")
+ },
+ onHeightChange: { height in
+ editorState.editorHeight = CGFloat(height)
+ },
+ onEditorReady: { richEditor in
+ editorState.editor = richEditor
+ }
+ )
+ .background(Color(.systemGray6))
+ .clipped() // Prevent content from overflowing its bounds
+ }
+ .padding(.horizontal, 16)
+ .padding(.bottom, 16)
+ .zIndex(0) // Ensure editor is below the toolbar
+ }
+ .ignoresSafeArea(.keyboard, edges: .bottom)
+ .onChange(of: foregroundColor) { newColor in
+ // Update editor text color when foregroundColor changes
+ editorState.editor?.setTextColor(UIColor(newColor))
+ }
+ .onChange(of: highlightColor) { newColor in
+ // Update editor text background color when highlightColor changes
+ editorState.editor?.setTextBackgroundColor(UIColor(newColor))
+ }
+ .sheet(isPresented: $showTextColorPicker) {
+ NavigationView {
+ VStack {
+ ColorPicker("Select Text Color", selection: $foregroundColor)
+ .padding()
+ Spacer()
+ }
+ .navigationTitle("Text Color")
+ .navigationBarTitleDisplayMode(.inline)
+ .toolbar {
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button("Done") {
+ showTextColorPicker = false
+ }
+ }
+ }
+ }
+ }
+ .sheet(isPresented: $showBackgroundColorPicker) {
+ NavigationView {
+ VStack {
+ ColorPicker("Select Background Color", selection: $highlightColor)
+ .padding()
+ Spacer()
+ }
+ .navigationTitle("Background Color")
+ .navigationBarTitleDisplayMode(.inline)
+ .toolbar {
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button("Done") {
+ showBackgroundColorPicker = false
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#Preview {
+ ContentView()
+}
+```
+
Acknowledgements
----------------
diff --git a/RichEditorView/Sources/RichEditorState.swift b/RichEditorView/Sources/RichEditorState.swift
new file mode 100644
index 00000000..bb3a9118
--- /dev/null
+++ b/RichEditorView/Sources/RichEditorState.swift
@@ -0,0 +1,138 @@
+//
+// RichEditorState.swift
+// RichEditorView
+//
+// Created by yunning you on 2025/8/25.
+//
+
+import SwiftUI
+
+
+/// A state management class for the rich text editor, designed for use in SwiftUI environments.
+@available(iOS 13.0, *)
+public class RichEditorState: ObservableObject {
+
+ // MARK: - Published Properties
+
+ /// The HTML content of the editor.
+ @Published public var htmlContent: String
+
+ /// A boolean value indicating whether the editor is currently focused.
+ @Published public var isEditing: Bool = false
+
+ /// The dynamic height of the editor content.
+ @Published public var editorHeight: CGFloat = 200
+
+ // MARK: - Properties
+
+ /// A weak reference to the underlying RichEditorView instance.
+ public var editor: RichEditorView?
+
+ // MARK: - Initialization
+
+ /// Initializes the state with optional initial HTML content.
+ /// - Parameter htmlContent: The initial HTML content to load into the editor.
+ public init(htmlContent: String = "") {
+ self.htmlContent = htmlContent
+ }
+
+ // MARK: - Public Methods
+
+ /// Clears the content of the editor.
+ public func clearContent() {
+ htmlContent = ""
+ editor?.html = ""
+ }
+
+ /// Inserts sample HTML content into the editor for demonstration purposes.
+ public func insertSampleContent() {
+ let sampleHTML = """
+ Welcome to the Rich Editor
+ This is a powerful rich text editor that supports:
+
+ - Bold and italic text
+ - Underline and
strikethrough
+ - Multiple heading levels
+ - Ordered and unordered lists
+ - Text alignment
+ - Text and background colors
+
+ Start editing now!
+ """
+ htmlContent = sampleHTML
+ editor?.html = sampleHTML
+ }
+
+ /// Asynchronously retrieves the plain text content of the editor.
+ /// - Parameter completion: A closure that receives the plain text string.
+ public func getPlainText(completion: @escaping (String) -> Void) {
+ guard let editor = editor else {
+ completion("")
+ return
+ }
+ editor.getText { text in
+ completion(text)
+ }
+ }
+
+ /// Asynchronously retrieves content statistics, such as character and word counts.
+ /// - Parameter completion: A closure that receives the character count and word count.
+ public func getContentStats(completion: @escaping (Int, Int) -> Void) {
+ getPlainText { plainText in
+ let characters = plainText.count
+ let words = plainText.components(separatedBy: .whitespacesAndNewlines)
+ .filter { !$0.isEmpty }.count
+ completion(characters, words)
+ }
+ }
+
+ /// Saves the current HTML content to UserDefaults.
+ public func autoSave() {
+ UserDefaults.standard.set(htmlContent, forKey: "RichEditorAutoSave")
+ }
+
+ /// Restores the HTML content from UserDefaults if available.
+ public func restoreFromAutoSave() {
+ if let savedContent = UserDefaults.standard.string(forKey: "RichEditorAutoSave"),
+ !savedContent.isEmpty {
+ htmlContent = savedContent
+ editor?.html = savedContent
+ }
+ }
+
+ /// Saves the current HTML content to a file in the documents directory.
+ /// - Parameter filename: The name of the file to save the content to.
+ public func saveToFile(filename: String = "rich_editor_content.html") {
+ guard let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
+ print("Failed to access documents directory.")
+ return
+ }
+ let fileURL = documentsPath.appendingPathComponent(filename)
+
+ do {
+ try htmlContent.write(to: fileURL, atomically: true, encoding: .utf8)
+ print("Content successfully saved to: \(fileURL.path)")
+ } catch {
+ print("Failed to save content: \(error)")
+ }
+ }
+
+ /// Loads HTML content from a file in the documents directory.
+ /// - Parameter filename: The name of the file to load the content from.
+ public func loadFromFile(filename: String = "rich_editor_content.html") {
+ guard let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
+ print("Failed to access documents directory.")
+ return
+ }
+ let fileURL = documentsPath.appendingPathComponent(filename)
+
+ do {
+ let content = try String(contentsOf: fileURL, encoding: .utf8)
+ htmlContent = content
+ editor?.html = content
+ print("Content successfully loaded from file.")
+ } catch {
+ print("Failed to load content: \(error)")
+ }
+ }
+}
diff --git a/RichEditorView/Sources/SwiftUIRichEditor.swift b/RichEditorView/Sources/SwiftUIRichEditor.swift
new file mode 100644
index 00000000..fa1468be
--- /dev/null
+++ b/RichEditorView/Sources/SwiftUIRichEditor.swift
@@ -0,0 +1,272 @@
+//
+// SwiftUIRichEditor.swift
+// RichEditorView
+//
+// Created by SwiftUI Integration
+//
+
+import SwiftUI
+import UIKit
+
+// MARK: - SwiftUI包装器
+
+/// SwiftUI版本的富文本编辑器
+/// 支持独立使用或与工具栏配合使用
+@available(iOS 13.0, *)
+public struct SwiftUIRichEditor: UIViewRepresentable {
+
+ // MARK: - 绑定属性
+ @Binding private var htmlContent: String
+ @Binding private var isEditing: Bool
+
+ // MARK: - 配置属性
+ private let placeholder: String
+ private let isScrollEnabled: Bool
+ private let editingEnabled: Bool
+ private let backgroundColor: UIColor
+ private let onContentChange: ((String) -> Void)?
+ private let onHeightChange: ((Int) -> Void)?
+ private let onFocusChange: ((Bool) -> Void)?
+ private let onEditorReady: ((RichEditorView) -> Void)?
+
+ // MARK: - 初始化器
+
+ /// 基本初始化器
+ /// - Parameters:
+ /// - htmlContent: HTML内容的绑定
+ /// - isEditing: 编辑状态的绑定
+ /// - placeholder: 占位符文本
+ /// - isScrollEnabled: 是否允许滚动
+ /// - editingEnabled: 是否允许编辑
+ /// - backgroundColor: 背景颜色
+ /// - onContentChange: 内容变化回调
+ /// - onHeightChange: 高度变化回调
+ /// - onFocusChange: 焦点变化回调
+ /// - onEditorReady: 编辑器准备就绪回调
+ public init(
+ htmlContent: Binding,
+ isEditing: Binding = .constant(false),
+ placeholder: String = "请输入内容...",
+ isScrollEnabled: Bool = true,
+ editingEnabled: Bool = true,
+ backgroundColor: UIColor = .systemBackground,
+ onContentChange: ((String) -> Void)? = nil,
+ onHeightChange: ((Int) -> Void)? = nil,
+ onFocusChange: ((Bool) -> Void)? = nil,
+ onEditorReady: ((RichEditorView) -> Void)? = nil
+ ) {
+ self._htmlContent = htmlContent
+ self._isEditing = isEditing
+ self.placeholder = placeholder
+ self.isScrollEnabled = isScrollEnabled
+ self.editingEnabled = editingEnabled
+ self.backgroundColor = backgroundColor
+ self.onContentChange = onContentChange
+ self.onHeightChange = onHeightChange
+ self.onFocusChange = onFocusChange
+ self.onEditorReady = onEditorReady
+ }
+
+ // MARK: - UIViewRepresentable实现
+
+ public func makeCoordinator() -> Coordinator {
+ Coordinator(self)
+ }
+
+ public func makeUIView(context: Context) -> RichEditorView {
+ let editor = RichEditorView()
+
+ // 基本配置
+ editor.delegate = context.coordinator
+ editor.placeholder = placeholder
+ editor.isScrollEnabled = isScrollEnabled
+ editor.editingEnabled = editingEnabled
+ editor.setEditorBackgroundColor(backgroundColor)
+
+ // 设置初始内容
+ if !htmlContent.isEmpty {
+ editor.html = htmlContent
+ }
+
+ // 通知编辑器已准备就绪
+ onEditorReady?(editor)
+
+ return editor
+ }
+
+ public func updateUIView(_ uiView: RichEditorView, context: Context) {
+ // 更新内容(避免循环更新)
+ if uiView.contentHTML != htmlContent {
+ uiView.html = htmlContent
+ }
+
+ // 更新编辑状态
+ if uiView.editingEnabled != editingEnabled {
+ uiView.editingEnabled = editingEnabled
+ }
+
+ // 更新占位符
+ if uiView.placeholder != placeholder {
+ uiView.placeholder = placeholder
+ }
+ }
+}
+
+// MARK: - Coordinator
+@available(iOS 13.0, *)
+extension SwiftUIRichEditor {
+ public class Coordinator: NSObject, RichEditorDelegate {
+ var parent: SwiftUIRichEditor
+
+ init(_ parent: SwiftUIRichEditor) {
+ self.parent = parent
+ }
+
+ // MARK: - RichEditorDelegate方法
+
+ public func richEditor(_ editor: RichEditorView, contentDidChange content: String) {
+ // 更新绑定的HTML内容
+ DispatchQueue.main.async {
+ self.parent.htmlContent = content
+ self.parent.onContentChange?(content)
+ }
+ }
+
+ public func richEditor(_ editor: RichEditorView, heightDidChange height: Int) {
+ DispatchQueue.main.async {
+ self.parent.onHeightChange?(height)
+ }
+ }
+
+ public func richEditorTookFocus(_ editor: RichEditorView) {
+ DispatchQueue.main.async {
+ self.parent.isEditing = true
+ self.parent.onFocusChange?(true)
+ }
+ }
+
+ public func richEditorLostFocus(_ editor: RichEditorView) {
+ DispatchQueue.main.async {
+ self.parent.isEditing = false
+ self.parent.onFocusChange?(false)
+ }
+ }
+
+ public func richEditorDidLoad(_ editor: RichEditorView) {
+ // 编辑器加载完成
+ }
+
+ public func richEditor(_ editor: RichEditorView, shouldInteractWith url: URL) -> Bool {
+ return true
+ }
+
+ public func richEditor(_ editor: RichEditorView, handle action: String) {
+ // 处理自定义操作
+ }
+ }
+}
+
+// MARK: - SwiftUI工具栏包装器
+
+/// SwiftUI版本的富文本编辑器工具栏
+@available(iOS 13.0, *)
+public struct SwiftUIRichEditorToolbar: UIViewRepresentable {
+
+ // MARK: - 属性
+ private let options: [RichEditorOption]
+ private let barTintColor: UIColor?
+ private weak var editor: RichEditorView?
+ private let onTextColorChange: (() -> Void)?
+ private let onBackgroundColorChange: (() -> Void)?
+ private let onImageInsert: (() -> Void)?
+ private let onLinkInsert: (() -> Void)?
+
+ // MARK: - 初始化器
+
+ /// 初始化工具栏
+ /// - Parameters:
+ /// - options: 工具栏选项数组
+ /// - barTintColor: 工具栏背景色
+ /// - editor: 关联的编辑器
+ /// - onTextColorChange: 文字颜色变化回调
+ /// - onBackgroundColorChange: 背景颜色变化回调
+ /// - onImageInsert: 图片插入回调
+ /// - onLinkInsert: 链接插入回调
+ public init(
+ options: [RichEditorOption] = RichEditorDefaultOption.all,
+ barTintColor: UIColor? = nil,
+ editor: RichEditorView? = nil,
+ onTextColorChange: (() -> Void)? = nil,
+ onBackgroundColorChange: (() -> Void)? = nil,
+ onImageInsert: (() -> Void)? = nil,
+ onLinkInsert: (() -> Void)? = nil
+ ) {
+ self.options = options
+ self.barTintColor = barTintColor
+ self.editor = editor
+ self.onTextColorChange = onTextColorChange
+ self.onBackgroundColorChange = onBackgroundColorChange
+ self.onImageInsert = onImageInsert
+ self.onLinkInsert = onLinkInsert
+ }
+
+ // MARK: - UIViewRepresentable实现
+
+ public func makeCoordinator() -> ToolbarCoordinator {
+ ToolbarCoordinator(self)
+ }
+
+ public func makeUIView(context: Context) -> RichEditorToolbar {
+ let toolbar = RichEditorToolbar()
+ toolbar.options = options
+ toolbar.delegate = context.coordinator
+ toolbar.editor = editor
+
+ if let barTintColor = barTintColor {
+ toolbar.barTintColor = barTintColor
+ }
+
+ return toolbar
+ }
+
+ public func updateUIView(_ uiView: RichEditorToolbar, context: Context) {
+ uiView.options = options
+ uiView.editor = editor
+
+ if let barTintColor = barTintColor {
+ uiView.barTintColor = barTintColor
+ }
+ }
+}
+
+// MARK: - 工具栏Coordinator
+
+@available(iOS 13.0, *)
+extension SwiftUIRichEditorToolbar {
+ public class ToolbarCoordinator: NSObject, RichEditorToolbarDelegate {
+ var parent: SwiftUIRichEditorToolbar
+
+ init(_ parent: SwiftUIRichEditorToolbar) {
+ self.parent = parent
+ }
+
+ // MARK: - RichEditorToolbarDelegate方法
+
+ public func richEditorToolbarChangeTextColor(_ toolbar: RichEditorToolbar) {
+ parent.onTextColorChange?()
+ }
+
+ public func richEditorToolbarChangeBackgroundColor(_ toolbar: RichEditorToolbar) {
+ parent.onBackgroundColorChange?()
+ }
+
+ public func richEditorToolbarInsertImage(_ toolbar: RichEditorToolbar) {
+ parent.onImageInsert?()
+ }
+
+ public func richEditorToolbarInsertLink(_ toolbar: RichEditorToolbar) {
+ parent.onLinkInsert?()
+ }
+ }
+}
+
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUI.xcodeproj/project.pbxproj b/RichEditorViewSwiftUI/RichEditorViewSwiftUI.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..7205b9d8
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUI.xcodeproj/project.pbxproj
@@ -0,0 +1,600 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 77;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 7F7CAD2E2E61FEEA000492B0 /* RichEditorView in Frameworks */ = {isa = PBXBuildFile; productRef = 7F7CAD2D2E61FEEA000492B0 /* RichEditorView */; };
+ 7F7CAD892E620B47000492B0 /* RichEditorView in Frameworks */ = {isa = PBXBuildFile; productRef = 7F7CAD882E620B47000492B0 /* RichEditorView */; };
+ 7F9E9BAB2E5B802B004AD074 /* RichEditorView in Frameworks */ = {isa = PBXBuildFile; productRef = 7F9E9BAA2E5B802B004AD074 /* RichEditorView */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 7F9E9B8D2E5B7FCF004AD074 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 7F9E9B772E5B7FCE004AD074 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 7F9E9B7E2E5B7FCE004AD074;
+ remoteInfo = RichEditorViewSwiftUI;
+ };
+ 7F9E9B972E5B7FCF004AD074 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 7F9E9B772E5B7FCE004AD074 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 7F9E9B7E2E5B7FCE004AD074;
+ remoteInfo = RichEditorViewSwiftUI;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 7F9E9B7F2E5B7FCE004AD074 /* RichEditorViewSwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RichEditorViewSwiftUI.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7F9E9B8C2E5B7FCF004AD074 /* RichEditorViewSwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RichEditorViewSwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7F9E9B962E5B7FCF004AD074 /* RichEditorViewSwiftUIUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RichEditorViewSwiftUIUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+ 7F9E9B812E5B7FCE004AD074 /* RichEditorViewSwiftUI */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ path = RichEditorViewSwiftUI;
+ sourceTree = "";
+ };
+ 7F9E9B8F2E5B7FCF004AD074 /* RichEditorViewSwiftUITests */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ path = RichEditorViewSwiftUITests;
+ sourceTree = "";
+ };
+ 7F9E9B992E5B7FCF004AD074 /* RichEditorViewSwiftUIUITests */ = {
+ isa = PBXFileSystemSynchronizedRootGroup;
+ path = RichEditorViewSwiftUIUITests;
+ sourceTree = "";
+ };
+/* End PBXFileSystemSynchronizedRootGroup section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 7F9E9B7C2E5B7FCE004AD074 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7F7CAD2E2E61FEEA000492B0 /* RichEditorView in Frameworks */,
+ 7F7CAD892E620B47000492B0 /* RichEditorView in Frameworks */,
+ 7F9E9BAB2E5B802B004AD074 /* RichEditorView in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7F9E9B892E5B7FCF004AD074 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7F9E9B932E5B7FCF004AD074 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 7F9E9B762E5B7FCE004AD074 = {
+ isa = PBXGroup;
+ children = (
+ 7F9E9B812E5B7FCE004AD074 /* RichEditorViewSwiftUI */,
+ 7F9E9B8F2E5B7FCF004AD074 /* RichEditorViewSwiftUITests */,
+ 7F9E9B992E5B7FCF004AD074 /* RichEditorViewSwiftUIUITests */,
+ 7F9E9B802E5B7FCE004AD074 /* Products */,
+ );
+ sourceTree = "";
+ };
+ 7F9E9B802E5B7FCE004AD074 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7F9E9B7F2E5B7FCE004AD074 /* RichEditorViewSwiftUI.app */,
+ 7F9E9B8C2E5B7FCF004AD074 /* RichEditorViewSwiftUITests.xctest */,
+ 7F9E9B962E5B7FCF004AD074 /* RichEditorViewSwiftUIUITests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 7F9E9B7E2E5B7FCE004AD074 /* RichEditorViewSwiftUI */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7F9E9BA02E5B7FCF004AD074 /* Build configuration list for PBXNativeTarget "RichEditorViewSwiftUI" */;
+ buildPhases = (
+ 7F9E9B7B2E5B7FCE004AD074 /* Sources */,
+ 7F9E9B7C2E5B7FCE004AD074 /* Frameworks */,
+ 7F9E9B7D2E5B7FCE004AD074 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ fileSystemSynchronizedGroups = (
+ 7F9E9B812E5B7FCE004AD074 /* RichEditorViewSwiftUI */,
+ );
+ name = RichEditorViewSwiftUI;
+ packageProductDependencies = (
+ 7F9E9BAA2E5B802B004AD074 /* RichEditorView */,
+ 7F7CAD2D2E61FEEA000492B0 /* RichEditorView */,
+ 7F7CAD882E620B47000492B0 /* RichEditorView */,
+ );
+ productName = RichEditorViewSwiftUI;
+ productReference = 7F9E9B7F2E5B7FCE004AD074 /* RichEditorViewSwiftUI.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 7F9E9B8B2E5B7FCF004AD074 /* RichEditorViewSwiftUITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7F9E9BA32E5B7FCF004AD074 /* Build configuration list for PBXNativeTarget "RichEditorViewSwiftUITests" */;
+ buildPhases = (
+ 7F9E9B882E5B7FCF004AD074 /* Sources */,
+ 7F9E9B892E5B7FCF004AD074 /* Frameworks */,
+ 7F9E9B8A2E5B7FCF004AD074 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 7F9E9B8E2E5B7FCF004AD074 /* PBXTargetDependency */,
+ );
+ fileSystemSynchronizedGroups = (
+ 7F9E9B8F2E5B7FCF004AD074 /* RichEditorViewSwiftUITests */,
+ );
+ name = RichEditorViewSwiftUITests;
+ packageProductDependencies = (
+ );
+ productName = RichEditorViewSwiftUITests;
+ productReference = 7F9E9B8C2E5B7FCF004AD074 /* RichEditorViewSwiftUITests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 7F9E9B952E5B7FCF004AD074 /* RichEditorViewSwiftUIUITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7F9E9BA62E5B7FCF004AD074 /* Build configuration list for PBXNativeTarget "RichEditorViewSwiftUIUITests" */;
+ buildPhases = (
+ 7F9E9B922E5B7FCF004AD074 /* Sources */,
+ 7F9E9B932E5B7FCF004AD074 /* Frameworks */,
+ 7F9E9B942E5B7FCF004AD074 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 7F9E9B982E5B7FCF004AD074 /* PBXTargetDependency */,
+ );
+ fileSystemSynchronizedGroups = (
+ 7F9E9B992E5B7FCF004AD074 /* RichEditorViewSwiftUIUITests */,
+ );
+ name = RichEditorViewSwiftUIUITests;
+ packageProductDependencies = (
+ );
+ productName = RichEditorViewSwiftUIUITests;
+ productReference = 7F9E9B962E5B7FCF004AD074 /* RichEditorViewSwiftUIUITests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 7F9E9B772E5B7FCE004AD074 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1640;
+ LastUpgradeCheck = 1640;
+ TargetAttributes = {
+ 7F9E9B7E2E5B7FCE004AD074 = {
+ CreatedOnToolsVersion = 16.4;
+ };
+ 7F9E9B8B2E5B7FCF004AD074 = {
+ CreatedOnToolsVersion = 16.4;
+ TestTargetID = 7F9E9B7E2E5B7FCE004AD074;
+ };
+ 7F9E9B952E5B7FCF004AD074 = {
+ CreatedOnToolsVersion = 16.4;
+ TestTargetID = 7F9E9B7E2E5B7FCE004AD074;
+ };
+ };
+ };
+ buildConfigurationList = 7F9E9B7A2E5B7FCE004AD074 /* Build configuration list for PBXProject "RichEditorViewSwiftUI" */;
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 7F9E9B762E5B7FCE004AD074;
+ minimizedProjectReferenceProxies = 1;
+ packageReferences = (
+ 7F7CAD872E620B47000492B0 /* XCLocalSwiftPackageReference "../../RichEditorView" */,
+ );
+ preferredProjectObjectVersion = 77;
+ productRefGroup = 7F9E9B802E5B7FCE004AD074 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 7F9E9B7E2E5B7FCE004AD074 /* RichEditorViewSwiftUI */,
+ 7F9E9B8B2E5B7FCF004AD074 /* RichEditorViewSwiftUITests */,
+ 7F9E9B952E5B7FCF004AD074 /* RichEditorViewSwiftUIUITests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 7F9E9B7D2E5B7FCE004AD074 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7F9E9B8A2E5B7FCF004AD074 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7F9E9B942E5B7FCF004AD074 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 7F9E9B7B2E5B7FCE004AD074 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7F9E9B882E5B7FCF004AD074 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7F9E9B922E5B7FCF004AD074 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 7F9E9B8E2E5B7FCF004AD074 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 7F9E9B7E2E5B7FCE004AD074 /* RichEditorViewSwiftUI */;
+ targetProxy = 7F9E9B8D2E5B7FCF004AD074 /* PBXContainerItemProxy */;
+ };
+ 7F9E9B982E5B7FCF004AD074 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 7F9E9B7E2E5B7FCE004AD074 /* RichEditorViewSwiftUI */;
+ targetProxy = 7F9E9B972E5B7FCF004AD074 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 7F9E9B9E2E5B7FCF004AD074 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ DEVELOPMENT_TEAM = T5WWP2Z4XN;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 18.5;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 7F9E9B9F2E5B7FCF004AD074 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DEVELOPMENT_TEAM = T5WWP2Z4XN;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 18.5;
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 7F9E9BA12E5B7FCF004AD074 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = T5WWP2Z4XN;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ites.easumerebuild4.RichEditorViewSwiftUI;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 7F9E9BA22E5B7FCF004AD074 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = T5WWP2Z4XN;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ites.easumerebuild4.RichEditorViewSwiftUI;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 7F9E9BA42E5B7FCF004AD074 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = T5WWP2Z4XN;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 18.5;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ites.easumerebuild4.RichEditorViewSwiftUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RichEditorViewSwiftUI.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/RichEditorViewSwiftUI";
+ };
+ name = Debug;
+ };
+ 7F9E9BA52E5B7FCF004AD074 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = T5WWP2Z4XN;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 18.5;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ites.easumerebuild4.RichEditorViewSwiftUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RichEditorViewSwiftUI.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/RichEditorViewSwiftUI";
+ };
+ name = Release;
+ };
+ 7F9E9BA72E5B7FCF004AD074 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = T5WWP2Z4XN;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ites.easumerebuild4.RichEditorViewSwiftUIUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = RichEditorViewSwiftUI;
+ };
+ name = Debug;
+ };
+ 7F9E9BA82E5B7FCF004AD074 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = T5WWP2Z4XN;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ites.easumerebuild4.RichEditorViewSwiftUIUITests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = RichEditorViewSwiftUI;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 7F9E9B7A2E5B7FCE004AD074 /* Build configuration list for PBXProject "RichEditorViewSwiftUI" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7F9E9B9E2E5B7FCF004AD074 /* Debug */,
+ 7F9E9B9F2E5B7FCF004AD074 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 7F9E9BA02E5B7FCF004AD074 /* Build configuration list for PBXNativeTarget "RichEditorViewSwiftUI" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7F9E9BA12E5B7FCF004AD074 /* Debug */,
+ 7F9E9BA22E5B7FCF004AD074 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 7F9E9BA32E5B7FCF004AD074 /* Build configuration list for PBXNativeTarget "RichEditorViewSwiftUITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7F9E9BA42E5B7FCF004AD074 /* Debug */,
+ 7F9E9BA52E5B7FCF004AD074 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 7F9E9BA62E5B7FCF004AD074 /* Build configuration list for PBXNativeTarget "RichEditorViewSwiftUIUITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7F9E9BA72E5B7FCF004AD074 /* Debug */,
+ 7F9E9BA82E5B7FCF004AD074 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCLocalSwiftPackageReference section */
+ 7F7CAD872E620B47000492B0 /* XCLocalSwiftPackageReference "../../RichEditorView" */ = {
+ isa = XCLocalSwiftPackageReference;
+ relativePath = ../../RichEditorView;
+ };
+/* End XCLocalSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 7F7CAD2D2E61FEEA000492B0 /* RichEditorView */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = RichEditorView;
+ };
+ 7F7CAD882E620B47000492B0 /* RichEditorView */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = RichEditorView;
+ };
+ 7F9E9BAA2E5B802B004AD074 /* RichEditorView */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = RichEditorView;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 7F9E9B772E5B7FCE004AD074 /* Project object */;
+}
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/RichEditorViewSwiftUI/RichEditorViewSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000..eb878970
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..23058801
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,35 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "tinted"
+ }
+ ],
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/Contents.json b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUI/ContentView.swift b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/ContentView.swift
new file mode 100644
index 00000000..8d67ec41
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/ContentView.swift
@@ -0,0 +1,176 @@
+//
+// ContentView.swift
+// RichEditorViewSwiftUI
+//
+// Created by yunning you on 2025/8/25.
+//
+
+import SwiftUI
+import RichEditorView
+
+struct ContentView: View {
+
+ // Rich text editor state management
+ @StateObject private var editorState = RichEditorState(
+ htmlContent: """
+
+ - This is the first line for testing styles.
+ - Bold this line.
+ - Make this line italic.
+ - Underline this one.
+ Apply a strikethrough here.
+ - This is for subscript test: H2O.
+ - And this is for superscript test: E=mc2.
+ - Let's test text color on this sentence.
+ - Now, let's try a background color.
+ Heading 1 will be applied here.
+ This should be Heading 2.
+ And here comes Heading 3.
+ Indent this paragraph.
+ - This is a standard line.
+ - This is the fifteenth item in our list.
+ - A slightly longer sentence to check wrapping and alignment.
+ Left align this text.
+ Center align this text.
+ Right align this text.
+ - Let's see how a hyperlink looks.
+ - This line contains some special characters: @#$%^&*().
+ - A sentence with multiple formats.
+ - The twenty-third line to test scrolling behavior.
+ - Almost there, just one more line to go.
+ - This is the final line for testing!
+
+ """
+ )
+
+ // State variables for ColorPicker
+ @State private var foregroundColor: Color = .black
+ @State private var highlightColor: Color = .white
+
+ // State variables to control color picker display
+ @State private var showTextColorPicker: Bool = false
+ @State private var showBackgroundColorPicker: Bool = false
+
+ // Toolbar options configuration - using all available options
+ private let toolbarOptions: [RichEditorDefaultOption] = RichEditorDefaultOption.all
+
+ var body: some View {
+ VStack(spacing: 0) {
+ // Toolbar section wrapped in VStack
+ VStack(spacing: 0) {
+ SwiftUIRichEditorToolbar(
+ options: toolbarOptions,
+ barTintColor: .systemBackground,
+ editor: editorState.editor,
+ onTextColorChange: {
+ // Show text color picker
+ showTextColorPicker = true
+ },
+ onBackgroundColorChange: {
+ // Show background color picker
+ showBackgroundColorPicker = true
+ },
+ onImageInsert: {
+ // Handle image insertion
+ print("Image insertion")
+ },
+ onLinkInsert: {
+ // Handle link insertion
+ print("Link insertion")
+ }
+ )
+ .frame(height: 44)
+ .background(
+ Rectangle()
+ .fill(Color(.systemBackground))
+ .overlay(
+ Rectangle()
+ .stroke(Color(.separator), lineWidth: 1)
+ )
+ )
+ }
+ .padding(.horizontal, 16)
+ .padding(.top, 8)
+ .zIndex(1) // Ensure toolbar is always on top
+
+ // Divider between toolbar and editor
+ Divider()
+ .padding(.horizontal, 16)
+
+ // Editor section wrapped in VStack
+ VStack(spacing: 0) {
+ SwiftUIRichEditor(
+ htmlContent: $editorState.htmlContent,
+ isEditing: $editorState.isEditing,
+ placeholder: "Enter content...",
+ isScrollEnabled: true,
+ editingEnabled: true,
+ backgroundColor: .systemGray6,
+ onContentChange: { content in
+ print("Content changed: \(content)")
+ },
+ onHeightChange: { height in
+ editorState.editorHeight = CGFloat(height)
+ },
+ onEditorReady: { richEditor in
+ editorState.editor = richEditor
+ }
+ )
+ .background(Color(.systemGray6))
+ .clipped() // Prevent content from overflowing its bounds
+ }
+ .padding(.horizontal, 16)
+ .padding(.bottom, 16)
+ .zIndex(0) // Ensure editor is below the toolbar
+ }
+ .ignoresSafeArea(.keyboard, edges: .bottom)
+ .onChange(of: foregroundColor) { newColor in
+ // Update editor text color when foregroundColor changes
+ editorState.editor?.setTextColor(UIColor(newColor))
+ }
+ .onChange(of: highlightColor) { newColor in
+ // Update editor text background color when highlightColor changes
+ editorState.editor?.setTextBackgroundColor(UIColor(newColor))
+ }
+ .sheet(isPresented: $showTextColorPicker) {
+ NavigationView {
+ VStack {
+ ColorPicker("Select Text Color", selection: $foregroundColor)
+ .padding()
+ Spacer()
+ }
+ .navigationTitle("Text Color")
+ .navigationBarTitleDisplayMode(.inline)
+ .toolbar {
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button("Done") {
+ showTextColorPicker = false
+ }
+ }
+ }
+ }
+ }
+ .sheet(isPresented: $showBackgroundColorPicker) {
+ NavigationView {
+ VStack {
+ ColorPicker("Select Background Color", selection: $highlightColor)
+ .padding()
+ Spacer()
+ }
+ .navigationTitle("Background Color")
+ .navigationBarTitleDisplayMode(.inline)
+ .toolbar {
+ ToolbarItem(placement: .navigationBarTrailing) {
+ Button("Done") {
+ showBackgroundColorPicker = false
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#Preview {
+ ContentView()
+}
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUI/RichEditorViewSwiftUIApp.swift b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/RichEditorViewSwiftUIApp.swift
new file mode 100644
index 00000000..6dd797aa
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUI/RichEditorViewSwiftUIApp.swift
@@ -0,0 +1,17 @@
+//
+// RichEditorViewSwiftUIApp.swift
+// RichEditorViewSwiftUI
+//
+// Created by yunning you on 2025/8/25.
+//
+
+import SwiftUI
+
+@main
+struct RichEditorViewSwiftUIApp: App {
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUITests/RichEditorViewSwiftUITests.swift b/RichEditorViewSwiftUI/RichEditorViewSwiftUITests/RichEditorViewSwiftUITests.swift
new file mode 100644
index 00000000..a9a8b01f
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUITests/RichEditorViewSwiftUITests.swift
@@ -0,0 +1,36 @@
+//
+// RichEditorViewSwiftUITests.swift
+// RichEditorViewSwiftUITests
+//
+// Created by yunning you on 2025/8/25.
+//
+
+import XCTest
+@testable import RichEditorViewSwiftUI
+
+final class RichEditorViewSwiftUITests: XCTestCase {
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+ func testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ // Any test you write for XCTest can be annotated as throws and async.
+ // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+ // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+}
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUIUITests/RichEditorViewSwiftUIUITests.swift b/RichEditorViewSwiftUI/RichEditorViewSwiftUIUITests/RichEditorViewSwiftUIUITests.swift
new file mode 100644
index 00000000..f71da156
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUIUITests/RichEditorViewSwiftUIUITests.swift
@@ -0,0 +1,41 @@
+//
+// RichEditorViewSwiftUIUITests.swift
+// RichEditorViewSwiftUIUITests
+//
+// Created by yunning you on 2025/8/25.
+//
+
+import XCTest
+
+final class RichEditorViewSwiftUIUITests: XCTestCase {
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+
+ // In UI tests it is usually best to stop immediately when a failure occurs.
+ continueAfterFailure = false
+
+ // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+ @MainActor
+ func testExample() throws {
+ // UI tests must launch the application that they test.
+ let app = XCUIApplication()
+ app.launch()
+
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ }
+
+ @MainActor
+ func testLaunchPerformance() throws {
+ // This measures how long it takes to launch your application.
+ measure(metrics: [XCTApplicationLaunchMetric()]) {
+ XCUIApplication().launch()
+ }
+ }
+}
diff --git a/RichEditorViewSwiftUI/RichEditorViewSwiftUIUITests/RichEditorViewSwiftUIUITestsLaunchTests.swift b/RichEditorViewSwiftUI/RichEditorViewSwiftUIUITests/RichEditorViewSwiftUIUITestsLaunchTests.swift
new file mode 100644
index 00000000..6d31ff2c
--- /dev/null
+++ b/RichEditorViewSwiftUI/RichEditorViewSwiftUIUITests/RichEditorViewSwiftUIUITestsLaunchTests.swift
@@ -0,0 +1,33 @@
+//
+// RichEditorViewSwiftUIUITestsLaunchTests.swift
+// RichEditorViewSwiftUIUITests
+//
+// Created by yunning you on 2025/8/25.
+//
+
+import XCTest
+
+final class RichEditorViewSwiftUIUITestsLaunchTests: XCTestCase {
+
+ override class var runsForEachTargetApplicationUIConfiguration: Bool {
+ true
+ }
+
+ override func setUpWithError() throws {
+ continueAfterFailure = false
+ }
+
+ @MainActor
+ func testLaunch() throws {
+ let app = XCUIApplication()
+ app.launch()
+
+ // Insert steps here to perform after app launch but before taking a screenshot,
+ // such as logging into a test account or navigating somewhere in the app
+
+ let attachment = XCTAttachment(screenshot: app.screenshot())
+ attachment.name = "Launch Screen"
+ attachment.lifetime = .keepAlways
+ add(attachment)
+ }
+}
diff --git a/art/SwiftUI.jpg b/art/SwiftUI.jpg
new file mode 100644
index 00000000..11174fb3
Binary files /dev/null and b/art/SwiftUI.jpg differ