From 4dd8f4446a2eda50a2f622317251864348ea82a3 Mon Sep 17 00:00:00 2001 From: Divyesh Canopas <83937721+cp-divyesh-v@users.noreply.github.com> Date: Tue, 16 Jan 2024 18:12:48 +0530 Subject: [PATCH 1/2] create workflow (#16) * create workflow --- .github/workflows/build_package.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/build_package.yml diff --git a/.github/workflows/build_package.yml b/.github/workflows/build_package.yml new file mode 100644 index 0000000..14ca170 --- /dev/null +++ b/.github/workflows/build_package.yml @@ -0,0 +1,20 @@ +name: build_package + +run-name: build + +on: [push] + +jobs: + build: + name: Swift ${{ matrix.swift }} on ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: swift-actions/setup-swift@65540b95f51493d65f5e59e97dcef9629ddf11bf + - uses: actions/checkout@v4 + - name: Build + run: swift build + - name: Run tests + run: swift test From 6963ca5f5f441ca6cf419653f50a6efcd7cd5b73 Mon Sep 17 00:00:00 2001 From: Divyesh Canopas Date: Wed, 17 Jan 2024 10:04:27 +0530 Subject: [PATCH 2/2] Update font with representable --- .../RichTextAttributeWriter+Style.swift | 6 +-- .../Fonts/FontMetricsRepresentable.swift | 34 +++++++++++++ .../UI/Editor/RichEditorState.swift | 2 +- .../UI/Editor/TextSpanStyle.swift | 6 +-- ...wift => FontRepresentable+Extension.swift} | 50 +++++++++---------- .../UI/Extensions/String+Extension.swift | 4 +- .../UIFontDescriptor+Extension.swift | 20 -------- .../UI/Utils/TextViewWrapper.swift | 4 +- 8 files changed, 68 insertions(+), 58 deletions(-) create mode 100644 Sources/RichEditorSwiftUI/Fonts/FontMetricsRepresentable.swift rename Sources/RichEditorSwiftUI/UI/Extensions/{UIFont+Extension.swift => FontRepresentable+Extension.swift} (66%) delete mode 100644 Sources/RichEditorSwiftUI/UI/Extensions/UIFontDescriptor+Extension.swift diff --git a/Sources/RichEditorSwiftUI/Attributes/RichTextAttributeWriter+Style.swift b/Sources/RichEditorSwiftUI/Attributes/RichTextAttributeWriter+Style.swift index d4f00d8..f78d089 100644 --- a/Sources/RichEditorSwiftUI/Attributes/RichTextAttributeWriter+Style.swift +++ b/Sources/RichEditorSwiftUI/Attributes/RichTextAttributeWriter+Style.swift @@ -6,7 +6,6 @@ // import Foundation -import UIKit public extension NSMutableAttributedString { @@ -49,13 +48,10 @@ public extension NSMutableAttributedString { /** This will reset font size befor multiplying new size */ - private func byTogglingFontSizeFor(style: TextSpanStyle, font: UIFont, shouldAdd: Bool) -> CGFloat { + private func byTogglingFontSizeFor(style: TextSpanStyle, font: FontRepresentable, shouldAdd: Bool) -> CGFloat { guard style.isHeaderStyle || style.isDefault else { return font.pointSize } let cleanFont = style.getFontAfterRemovingStyle(font: font) -// if style.isDefault { -// return cleanFont.pointSize -// } if shouldAdd { return cleanFont.pointSize * style.fontSizeMultiplier } else { diff --git a/Sources/RichEditorSwiftUI/Fonts/FontMetricsRepresentable.swift b/Sources/RichEditorSwiftUI/Fonts/FontMetricsRepresentable.swift new file mode 100644 index 0000000..ee27c3e --- /dev/null +++ b/Sources/RichEditorSwiftUI/Fonts/FontMetricsRepresentable.swift @@ -0,0 +1,34 @@ +// +// File.swift +// +// +// Created by Divyesh Vekariya on 17/01/24. +// + +import Foundation + +#if canImport(AppKit) && !targetEnvironment(macCatalyst) +import AppKit + +/** + This typealias bridges platform-specific font matrics to + simplify multi-platform support. + + The typealias also defines additional functionality as type + extensions for the platform-specific types. + */ +public typealias FontMetricsRepresentable = NSFont.NSFontMetrics +#endif + +#if canImport(UIKit) +import UIKit + +/** + This typealias bridges platform-specific font matrics to + simplify multi-platform support. + + The typealias also defines additional functionality as type + extensions for the platform-specific types. + */ +public typealias FontMetricsRepresentable = UIFontMetrics +#endif diff --git a/Sources/RichEditorSwiftUI/UI/Editor/RichEditorState.swift b/Sources/RichEditorSwiftUI/UI/Editor/RichEditorState.swift index 208c615..7ed572e 100644 --- a/Sources/RichEditorSwiftUI/UI/Editor/RichEditorState.swift +++ b/Sources/RichEditorSwiftUI/UI/Editor/RichEditorState.swift @@ -16,7 +16,7 @@ public class RichEditorState: ObservableObject { @Published internal var editableText: NSMutableAttributedString @Published internal var activeStyles: Set = [] @Published internal var activeAttributes: [NSAttributedString.Key: Any]? = [:] - internal var curretFont: UIFont = .systemFont(ofSize: .standardRichTextFontSize) + internal var curretFont: FontRepresentable = .systemFont(ofSize: .standardRichTextFontSize) @Published internal var attributesToApply: ((spans: [(span:RichTextSpan, shouldApply: Bool)], onCompletion: () -> Void))? = nil diff --git a/Sources/RichEditorSwiftUI/UI/Editor/TextSpanStyle.swift b/Sources/RichEditorSwiftUI/UI/Editor/TextSpanStyle.swift index ec6ee10..e6ed837 100644 --- a/Sources/RichEditorSwiftUI/UI/Editor/TextSpanStyle.swift +++ b/Sources/RichEditorSwiftUI/UI/Editor/TextSpanStyle.swift @@ -25,7 +25,7 @@ public enum TextSpanStyle: String, Equatable, Codable, CaseIterable { return self.rawValue } - func defaultAttributeValue(font: UIFont? = nil) -> Any { + func defaultAttributeValue(font: FontRepresentable? = nil) -> Any { let font = font ?? .systemFont(ofSize: .standardRichTextFontSize) switch self { case .underline: @@ -90,7 +90,7 @@ public enum TextSpanStyle: String, Equatable, Codable, CaseIterable { } } - func getFontWithUpdating(font: UIFont) -> UIFont { + func getFontWithUpdating(font: FontRepresentable) -> FontRepresentable { switch self { case .default: return font @@ -130,7 +130,7 @@ public enum TextSpanStyle: String, Equatable, Codable, CaseIterable { } } - func getFontAfterRemovingStyle(font: UIFont) -> UIFont { + func getFontAfterRemovingStyle(font: FontRepresentable) -> FontRepresentable { switch self { case .bold, .italic: return font.removeFontStyle(self) diff --git a/Sources/RichEditorSwiftUI/UI/Extensions/UIFont+Extension.swift b/Sources/RichEditorSwiftUI/UI/Extensions/FontRepresentable+Extension.swift similarity index 66% rename from Sources/RichEditorSwiftUI/UI/Extensions/UIFont+Extension.swift rename to Sources/RichEditorSwiftUI/UI/Extensions/FontRepresentable+Extension.swift index 095ad0c..3196286 100644 --- a/Sources/RichEditorSwiftUI/UI/Extensions/UIFont+Extension.swift +++ b/Sources/RichEditorSwiftUI/UI/Extensions/FontRepresentable+Extension.swift @@ -1,5 +1,5 @@ // -// UIFont+Extension.swift +// FontRepresentable+Extension.swift // // // Created by Divyesh Vekariya on 26/12/23. @@ -7,7 +7,7 @@ import SwiftUI -extension UIFont { +extension FontRepresentable { /// Check if font is bold var isBold: Bool { return fontDescriptor.symbolicTraits.contains(.traitBold) @@ -19,61 +19,61 @@ extension UIFont { } /// Make font **Bold** - func makeBold() -> UIFont { + func makeBold() -> FontRepresentable { if isBold { return self } else { let fontDesc = fontDescriptor.byTogglingStyle(.bold) fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: pointSize) + return FontRepresentable(descriptor: fontDesc, size: pointSize) } } /// Make font **Italic** - func makeItalic() -> UIFont { + func makeItalic() -> FontRepresentable { if isItalic { return self } else { let fontDesc = fontDescriptor.byTogglingStyle(.italic) fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: pointSize) + return FontRepresentable(descriptor: fontDesc, size: pointSize) } } /// Make font **Bold** and **Italic** - func setBoldItalicStyles() -> UIFont { + func setBoldItalicStyles() -> FontRepresentable { return makeBold().makeItalic() } /// Remove **Bold** style from font - func removeBoldStyle() -> UIFont { + func removeBoldStyle() -> FontRepresentable { if !isBold { return self } else { let fontDesc = fontDescriptor.byTogglingStyle(.bold) fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: pointSize) + return FontRepresentable(descriptor: fontDesc, size: pointSize) } } /// Remove **Italic** style from font - func removeItalicStyle() -> UIFont { + func removeItalicStyle() -> FontRepresentable { if !isItalic { return self } else { let fontDesc = fontDescriptor.byTogglingStyle(.italic) fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: pointSize) + return FontRepresentable(descriptor: fontDesc, size: pointSize) } } /// Remove **Bold** and **Italic** style from font - func makeNormal() -> UIFont { + func makeNormal() -> FontRepresentable { return removeBoldStyle().removeItalicStyle() } /// Toggle **Bold** style of font - func toggleBoldTrait() -> UIFont { + func toggleBoldTrait() -> FontRepresentable { if isBold { return removeBoldStyle() } else { @@ -82,7 +82,7 @@ extension UIFont { } /// Toggle **Italic** style of font - func toggleItalicStyle() -> UIFont { + func toggleItalicStyle() -> FontRepresentable { if isItalic { return removeItalicStyle() } else { @@ -91,49 +91,49 @@ extension UIFont { } /// Get a new font with updated font size by **size** - func updateFontSize(size: CGFloat) -> UIFont { + func updateFontSize(size: CGFloat) -> FontRepresentable { if pointSize != size { let fontDesc = fontDescriptor fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: size) + return FontRepresentable(descriptor: fontDesc, size: size) } else { return self } } - func updateFontSize(multiple: CGFloat) -> UIFont { + func updateFontSize(multiple: CGFloat) -> FontRepresentable { if pointSize != multiple * pointSize { let size = multiple * pointSize let fontDesc = fontDescriptor fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: size) + return FontRepresentable(descriptor: fontDesc, size: size) } else { return self } } } -public extension UIFont { +public extension FontRepresentable { /// Get a new font by adding a text style. - func addFontStyle(_ style: TextSpanStyle) -> UIFont { + func addFontStyle(_ style: TextSpanStyle) -> FontRepresentable { guard let trait = style.symbolicTraits, !fontDescriptor.symbolicTraits.contains(trait) else { return self } let fontDesc = fontDescriptor.byTogglingStyle(style) fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: pointSize) + return FontRepresentable(descriptor: fontDesc, size: pointSize) } ///Get a new font by removing a text style. - func removeFontStyle(_ style: TextSpanStyle) -> UIFont { + func removeFontStyle(_ style: TextSpanStyle) -> FontRepresentable { guard let trait = style.symbolicTraits, fontDescriptor.symbolicTraits.contains(trait) else { return self } let fontDesc = fontDescriptor.byTogglingStyle(style) fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: pointSize) + return FontRepresentable(descriptor: fontDesc, size: pointSize) } /// Get a new font by toggling a text style. - func byToggalingFontStyle(_ style: TextSpanStyle) -> UIFont { + func byToggalingFontStyle(_ style: TextSpanStyle) -> FontRepresentable { let fontDesc = fontDescriptor.byTogglingStyle(style) fontDesc.withFamily(familyName) - return UIFont(descriptor: fontDesc, size: pointSize) + return FontRepresentable(descriptor: fontDesc, size: pointSize) } } diff --git a/Sources/RichEditorSwiftUI/UI/Extensions/String+Extension.swift b/Sources/RichEditorSwiftUI/UI/Extensions/String+Extension.swift index 2a03ce8..5d45089 100644 --- a/Sources/RichEditorSwiftUI/UI/Extensions/String+Extension.swift +++ b/Sources/RichEditorSwiftUI/UI/Extensions/String+Extension.swift @@ -44,7 +44,7 @@ internal struct NSFontTraitMask: OptionSet { extension NSMutableAttributedString { internal func applyFontTraits(_ traitMask: NSFontTraitMask, range: NSRange) { enumerateAttribute(.font, in: range, options: [.longestEffectiveRangeNotRequired]) { (attr, attrRange, stop) in - guard let font = attr as? UIFont else { return } + guard let font = attr as? FontRepresentable else { return } let descriptor = font.fontDescriptor var symbolicTraits = descriptor.symbolicTraits if traitMask.contains(.boldFontMask) { @@ -60,7 +60,7 @@ extension NSMutableAttributedString { symbolicTraits.remove(.traitItalic) } guard let newDescriptor = descriptor.withSymbolicTraits(symbolicTraits) else { return } - let newFont = UIFont(descriptor: newDescriptor, size: font.pointSize) + let newFont = FontRepresentable(descriptor: newDescriptor, size: font.pointSize) self.addAttribute(.font, value: newFont, range: attrRange) } } diff --git a/Sources/RichEditorSwiftUI/UI/Extensions/UIFontDescriptor+Extension.swift b/Sources/RichEditorSwiftUI/UI/Extensions/UIFontDescriptor+Extension.swift deleted file mode 100644 index a5f85f8..0000000 --- a/Sources/RichEditorSwiftUI/UI/Extensions/UIFontDescriptor+Extension.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// UIFontDescriptor+Extension.swift -// -// -// Created by Divyesh Vekariya on 28/12/23. -// - -import SwiftUI - -extension UIFontDescriptor { - /// Get a new font descriptor by toggling a text style. - func byTogglingStyle(_ style: TextSpanStyle) -> UIFontDescriptor { - guard let traits = style.symbolicTraits else { return self } - if symbolicTraits.contains(traits) { - return withSymbolicTraits(symbolicTraits.subtracting(traits)) ?? self - } else { - return withSymbolicTraits(symbolicTraits.union(traits)) ?? self - } - } -} diff --git a/Sources/RichEditorSwiftUI/UI/Utils/TextViewWrapper.swift b/Sources/RichEditorSwiftUI/UI/Utils/TextViewWrapper.swift index e16edeb..0a1348b 100644 --- a/Sources/RichEditorSwiftUI/UI/Utils/TextViewWrapper.swift +++ b/Sources/RichEditorSwiftUI/UI/Utils/TextViewWrapper.swift @@ -17,7 +17,7 @@ internal struct TextViewWrapper: UIViewRepresentable { private let isUserInteractionEnabled: Bool private let isScrollEnabled: Bool private let linelimit: Int? - private let fontStyle: UIFont? + private let fontStyle: FontRepresentable? private let fontColor: Color private let backGroundColor: UIColor private let tag: Int? @@ -30,7 +30,7 @@ internal struct TextViewWrapper: UIViewRepresentable { isUserInteractionEnabled: Bool = true, isScrollEnabled: Bool = false, linelimit: Int? = nil, - fontStyle: UIFont? = nil, + fontStyle: FontRepresentable? = nil, fontColor: Color = .black, backGroundColor: UIColor = .clear, tag: Int? = nil,