From ed360f981341110fa22b238f4465e75f9b3fd921 Mon Sep 17 00:00:00 2001 From: Martin Dufort Date: Thu, 28 Dec 2023 10:34:01 -0500 Subject: [PATCH 1/4] Updated the README file to provide information about guidelines and enhancements Added support for CollectionViews, TextField and Labels Added ActivityIndicatorView support Provide mechanism to apply tintColor to NSUIImageView --- README.md | 47 ++++++++++++++++++++++++++++++++++++ Sources/NSUI/Aliases.swift | 37 ++++++++++++++++++++++++++++ Sources/NSUI/ImageView.swift | 24 ++++++++++++++++++ Sources/NSUI/Label.swift | 23 ++++++++++++++++++ Sources/NSUI/Views.swift | 11 +++++++++ 5 files changed, 142 insertions(+) create mode 100644 Sources/NSUI/ImageView.swift create mode 100644 Sources/NSUI/Label.swift diff --git a/README.md b/README.md index 41bc288..8907687 100644 --- a/README.md +++ b/README.md @@ -24,19 +24,39 @@ dependencies: [ ``` ## Types +All Types and their `UIKit` & `AppKit` correspondance are defined in the `Aliases.swift` file. That file only defines the +NSUI type corresponding to the proper platform. +Here's the current list of types supported in NSUI: ``` +NSUIActivityIndicatorView NSUIApplication NSUIApplicationDelegate NSUIApplicationDelegateAdaptor + +// Collection Views +NSUICollectionDataSource +NSUICollectionView +NSUICollectionViewItem +NSUICollectionViewDelegate +NSUICollectionViewLayout +NSUICollectionViewFlowLayout +NSUICollectionViewLayoutAttributes +NSUICollectionViewDelegateFlowLayout + NSUIColor +NSUIEdgeInsets +NSUIFont NSUIHostingController NSUIImage NSUILongPressGestureRecognizer +NSUINib NSUIPasteboard NSUIResponder NSUITapGestureRecognizer NSUITextStorageEditActions +NSUITextField +NSUITextFieldDelegate NSUITextView NSUIViewController NSUIWorkspace @@ -53,6 +73,33 @@ public protocol NSUIViewRepresentable { } ``` +## Conventions +NSUI is not a multi-platform framework to replace both UIKit and AppKit. As stated above, `NSUI` takes the stance that +the API from `UIKit` is the first-class citizen. However, in some cases, both AppKit and UIKit are very close to each other. + +Let's take, for example, the `NSProgressIndicator` and `UIActivityIndicator` classes. To start the spinning indicator view, you need to +issue `startAnimating()` for UIKit and `startAnimation(sender:)` for AppKit. + +In order to bridge that slight difference, a bridging function is created in the `Views.swift` file which looks like this: +```swift +extension NSUIActivityIndicator { + public func startAnimating() { + self.startAnimation(nil) + } + + public func stopAnimating() { + self.stopAnimation(nil) + } +} +``` +This extension is wrapped within a: `#if canImport(AppKit)` and provides the `UIKit` functional equivalent to the `AppKit` class. + +## Enhancing `NSUI` +If you need to enhance `NSUI`, follow the following guidelines: +1- Check if the type you need is defined in the `Aliases.swift` file. If not, add it there +2- If your type, needs to supply a bridging `var` or `func`, define that in a proper swift file. +3- Always prioritize the `UIKit` naming scheme when defining new bridging `var` or `func`. + ## Contributing and Collaboration I would love to hear from you! Issues or pull requests work great. A [Discord server][discord] is also available for live help, but I have a strong bias towards answering in the form of documentation. diff --git a/Sources/NSUI/Aliases.swift b/Sources/NSUI/Aliases.swift index 8908132..b7a2347 100644 --- a/Sources/NSUI/Aliases.swift +++ b/Sources/NSUI/Aliases.swift @@ -3,16 +3,34 @@ import SwiftUI #if canImport(AppKit) import AppKit +public typealias NSUIActivityIndicatorView = NSProgressIndicator public typealias NSUIApplication = NSApplication public typealias NSUIApplicationDelegate = NSApplicationDelegate @available(macOS 11.0, *) public typealias NSUIApplicationDelegateAdaptor = NSApplicationDelegateAdaptor + +// Collection View +public typealias NSUICollectionDataSource = NSCollectionViewDataSource +public typealias NSUICollectionView = NSCollectionView +public typealias NSUICollectionViewItem = NSCollectionViewItem +public typealias NSUICollectionViewDelegate = NSCollectionViewDelegate +public typealias NSUICollectionViewLayout = NSCollectionViewLayout +public typealias NSUICollectionViewFlowLayout = NSCollectionViewFlowLayout +public typealias NSUICollectionViewLayoutAttributes = NSCollectionViewLayoutAttributes +public typealias NSUICollectionViewDelegateFlowLayout = NSCollectionViewDelegateFlowLayout + public typealias NSUIColor = NSColor +public typealias NSUIEdgeInsets = NSEdgeInsets +public typealias NSUIFont = NSFont public typealias NSUIImage = NSImage +public typealias NSUILabel = NSTextField public typealias NSUILongPressGestureRecognizer = NSPressGestureRecognizer +public typealias NSUINib = NSNib public typealias NSUIPasteboard = NSPasteboard public typealias NSUIResponder = NSResponder public typealias NSUITapGestureRecognizer = NSClickGestureRecognizer +public typealias NSUITextField = NSTextField +public typealias NSUITextFieldDelegate = NSTextFieldDelegate public typealias NSUITextView = NSTextView public typealias NSUIView = NSView public typealias NSUIViewController = NSViewController @@ -22,17 +40,36 @@ public typealias NSUIWorkspace = NSWorkspace #elseif canImport(UIKit) import UIKit +public typealias NSUIActivityIndicatorView = UIActivityIndicatorView public typealias NSUIApplication = UIApplication public typealias NSUIApplicationDelegate = UIApplicationDelegate @available(iOS 14.0, tvOS 14.0, *) public typealias NSUIApplicationDelegateAdaptor = UIApplicationDelegateAdaptor + +// Collection View +public typealias NSUICollectionDataSource = UICollectionViewDataSource +public typealias NSUICollectionView = UICollectionView +public typealias NSUICollectionViewItem = UICollectionViewItem +public typealias NSUICollectionViewDelegate = UICollectionViewDelegate +public typealias NSUICollectionViewLayout = UICollectionViewLayout +public typealias NSUICollectionViewFlowLayout = UICollectionViewFlowLayout +public typealias NSUICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes +public typealias NSUICollectionViewDelegateFlowLayout = UICollectionViewDelegateFlowLayout + public typealias NSUIColor = UIColor +public typealias NSUIEdgeInsets = UIEdgeInsets +public typealias NSUIFont = UIFont public typealias NSUIImage = UIImage +public typealias NSUILabel = UILabel public typealias NSUILongPressGestureRecognizer = UILongPressGestureRecognizer +public typealias NSUINib = UINib + @available(tvOS, unavailable) public typealias NSUIPasteboard = UIPasteboard public typealias NSUIResponder = UIResponder public typealias NSUITapGestureRecognizer = UITapGestureRecognizer +public typealias NSUITextField = UITextField +public typealias NSUITextFieldDelegate = UITextFieldDelegate public typealias NSUITextView = UITextView public typealias NSUIView = UIView public typealias NSUIViewController = UIViewController diff --git a/Sources/NSUI/ImageView.swift b/Sources/NSUI/ImageView.swift new file mode 100644 index 0000000..467be56 --- /dev/null +++ b/Sources/NSUI/ImageView.swift @@ -0,0 +1,24 @@ +import SwiftUI + +#if canImport(AppKit) +public typealias NSUIImageView = NSImageView +#elseif canImport(UIKit) +public typealias NSUIImageView = UIImageView +#endif + +#if canImport(AppKit) +extension NSImageView { + public func tintColor(color: NSUIColor) { + guard + let selfImage = self.image + else { return } + let image = NSImage(size: selfImage.size, flipped: false) { (rect) -> Bool in + color.set() + rect.fill() + selfImage.draw(in: rect, from: NSRect(origin: .zero, size: selfImage.size), operation: .destinationIn, fraction: 1.0) + return true + } + self.image = image + } +} +#endif diff --git a/Sources/NSUI/Label.swift b/Sources/NSUI/Label.swift new file mode 100644 index 0000000..e01cbab --- /dev/null +++ b/Sources/NSUI/Label.swift @@ -0,0 +1,23 @@ +#if canImport(AppKit) +import AppKit + +public extension NSUILabel { + var text: String { + get { + return stringValue + } + set { + stringValue = newValue + } + } + var textAlignment: NSTextAlignment { + get { + return alignment + } + + set { + alignment = newValue + } + } +} +#endif diff --git a/Sources/NSUI/Views.swift b/Sources/NSUI/Views.swift index 19712ec..2593c62 100644 --- a/Sources/NSUI/Views.swift +++ b/Sources/NSUI/Views.swift @@ -9,6 +9,17 @@ extension NSUIResponder { } } +extension NSUIActivityIndicatorView { + public func startAnimating() { + self.startAnimation(nil) + } + + public func stopAnimating() { + self.stopAnimation(nil) + } +} + + #elseif canImport(UIKit) import UIKit From beac1222fddd9c3b8b6c0aa09bfca4e6c6879745 Mon Sep 17 00:00:00 2001 From: Martin Dufort Date: Thu, 28 Dec 2023 10:48:34 -0500 Subject: [PATCH 2/4] Fix alias error for UICollectionViewCell --- Sources/NSUI/Aliases.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NSUI/Aliases.swift b/Sources/NSUI/Aliases.swift index b7a2347..f7b17db 100644 --- a/Sources/NSUI/Aliases.swift +++ b/Sources/NSUI/Aliases.swift @@ -49,7 +49,7 @@ public typealias NSUIApplicationDelegateAdaptor = UIApplicationDelegateAdaptor // Collection View public typealias NSUICollectionDataSource = UICollectionViewDataSource public typealias NSUICollectionView = UICollectionView -public typealias NSUICollectionViewItem = UICollectionViewItem +public typealias NSUICollectionViewItem = UICollectionViewCell public typealias NSUICollectionViewDelegate = UICollectionViewDelegate public typealias NSUICollectionViewLayout = UICollectionViewLayout public typealias NSUICollectionViewFlowLayout = UICollectionViewFlowLayout From c0be53d09039b15c7cf2653e139daa575a316fb8 Mon Sep 17 00:00:00 2001 From: Martin Dufort Date: Thu, 28 Dec 2023 11:11:28 -0500 Subject: [PATCH 3/4] Moving NSUIImageView to the proper Aliases.swift file --- Sources/NSUI/Aliases.swift | 2 ++ Sources/NSUI/ImageView.swift | 10 ++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Sources/NSUI/Aliases.swift b/Sources/NSUI/Aliases.swift index f7b17db..d571dd7 100644 --- a/Sources/NSUI/Aliases.swift +++ b/Sources/NSUI/Aliases.swift @@ -23,6 +23,7 @@ public typealias NSUIColor = NSColor public typealias NSUIEdgeInsets = NSEdgeInsets public typealias NSUIFont = NSFont public typealias NSUIImage = NSImage +public typealias NSUIImageView = NSImageView public typealias NSUILabel = NSTextField public typealias NSUILongPressGestureRecognizer = NSPressGestureRecognizer public typealias NSUINib = NSNib @@ -60,6 +61,7 @@ public typealias NSUIColor = UIColor public typealias NSUIEdgeInsets = UIEdgeInsets public typealias NSUIFont = UIFont public typealias NSUIImage = UIImage +public typealias NSUIImageView = UIImageView public typealias NSUILabel = UILabel public typealias NSUILongPressGestureRecognizer = UILongPressGestureRecognizer public typealias NSUINib = UINib diff --git a/Sources/NSUI/ImageView.swift b/Sources/NSUI/ImageView.swift index 467be56..86dfe02 100644 --- a/Sources/NSUI/ImageView.swift +++ b/Sources/NSUI/ImageView.swift @@ -1,14 +1,8 @@ import SwiftUI #if canImport(AppKit) -public typealias NSUIImageView = NSImageView -#elseif canImport(UIKit) -public typealias NSUIImageView = UIImageView -#endif - -#if canImport(AppKit) -extension NSImageView { - public func tintColor(color: NSUIColor) { +public extension NSImageView { + func tintColor(color: NSUIColor) { guard let selfImage = self.image else { return } From 4d16817e789b05f7cacd7df8396dcdff61171c96 Mon Sep 17 00:00:00 2001 From: Martin Dufort Date: Fri, 29 Dec 2023 11:20:52 -0500 Subject: [PATCH 4/4] Make tintColor a variable setter for NSUIImageView(AppKit) as to not break UIKit logic. --- Sources/NSUI/ImageView.swift | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Sources/NSUI/ImageView.swift b/Sources/NSUI/ImageView.swift index 86dfe02..00dc504 100644 --- a/Sources/NSUI/ImageView.swift +++ b/Sources/NSUI/ImageView.swift @@ -2,17 +2,25 @@ import SwiftUI #if canImport(AppKit) public extension NSImageView { - func tintColor(color: NSUIColor) { - guard - let selfImage = self.image - else { return } - let image = NSImage(size: selfImage.size, flipped: false) { (rect) -> Bool in - color.set() - rect.fill() - selfImage.draw(in: rect, from: NSRect(origin: .zero, size: selfImage.size), operation: .destinationIn, fraction: 1.0) - return true + var tintColor: NSUIColor { + @available(*, unavailable) + get { + fatalError("Unavailable") + } + set { + guard + let selfImage = self.image + else { + return + } + let image = NSImage(size: selfImage.size, flipped: false) { (rect) -> Bool in + newValue.set() + rect.fill() + selfImage.draw(in: rect, from: NSRect(origin: .zero, size: selfImage.size), operation: .destinationIn, fraction: 1.0) + return true + } + self.image = image } - self.image = image } } #endif