From 03acfb13839777440d472a8f596b88d88b6acb21 Mon Sep 17 00:00:00 2001 From: Ryan Lintott Date: Tue, 25 Jan 2022 16:56:22 -0500 Subject: [PATCH] Fixed build errors for macOS by adjusting availability of functions to iOS only and changing availability of the package to macOS 11 or later. WidgetSize parameters and methods referencing current device sizes moved to a separate file and made only available on iOS. Moved OverlappingImage to a folder and moved the UIImage inits to a separate file. Removed the example test that cause build crashes. Added Swift Package Index badges. --- Package.swift | 2 +- README.md | 6 +- .../Images/OverlappingImage+UIImage.swift | 30 ++++++++ .../{ => Images}/OverlappingImage.swift | 18 ----- .../FixWidgetPreviewAlignmentBug.swift | 2 + .../FrameUp/WidgetSize/WidgetDemoFrame.swift | 4 +- .../WidgetSize/WidgetFamily+extensions.swift | 4 +- .../WidgetSize/WidgetRelativeShape.swift | 2 + .../WidgetSize/WidgetSize+CurrentDevice.swift | 75 +++++++++++++++++++ .../WidgetSize/WidgetSize+WidgetKit.swift | 8 +- Sources/FrameUp/WidgetSize/WidgetSize.swift | 63 ---------------- Tests/FrameUpTests/FrameUpTests.swift | 1 - 12 files changed, 125 insertions(+), 90 deletions(-) create mode 100644 Sources/FrameUp/Images/OverlappingImage+UIImage.swift rename Sources/FrameUp/{ => Images}/OverlappingImage.swift (75%) create mode 100644 Sources/FrameUp/WidgetSize/WidgetSize+CurrentDevice.swift diff --git a/Package.swift b/Package.swift index 5c70239..0744294 100644 --- a/Package.swift +++ b/Package.swift @@ -7,7 +7,7 @@ let package = Package( name: "FrameUp", platforms: [ .iOS(.v14), - .macOS(.v10_15) + .macOS(.v11) ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. diff --git a/README.md b/README.md index 378b25f..2d26914 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ FrameUp Logo -![Platform iOS 14](https://img.shields.io/badge/platform-iOS%2014-blue.svg) -[![SPM compatible](https://img.shields.io/badge/SPM-compatible-4BC51D.svg?style=flat)](https://github.com/apple/swift-package-manager) +[![Swift Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fryanlintott%2FFrameUp%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/ryanlintott/FrameUp) +[![Platform Compatibility](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fryanlintott%2FFrameUp%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/ryanlintott/FrameUp) ![License - MIT](https://img.shields.io/github/license/ryanlintott/FrameUp) ![Version](https://img.shields.io/github/v/tag/ryanlintott/FrameUp?label=version) ![GitHub last commit](https://img.shields.io/github/last-commit/ryanlintott/FrameUp) @@ -33,7 +33,7 @@ Check out the example app to see how you can use this package in your iOS app. Import the package using `import FrameUp` # Platforms -This package is compatible with iOS 14 or later. It's technically compatible with macOS 10.15 but hasn't been tested yet. +This package is compatible with iOS 14 or later. It's technically compatible with macOS 11 but hasn't been tested yet. # Is this Production-Ready? Really it's up to you. I currently use this package in my own [Old English Wordhord app](https://oldenglishwordhord.com/app). diff --git a/Sources/FrameUp/Images/OverlappingImage+UIImage.swift b/Sources/FrameUp/Images/OverlappingImage+UIImage.swift new file mode 100644 index 0000000..846e688 --- /dev/null +++ b/Sources/FrameUp/Images/OverlappingImage+UIImage.swift @@ -0,0 +1,30 @@ +// +// OverlappingImage+UIImage.swift +// FrameUp +// +// Created by Ryan Lintott on 2022-01-25. +// + +import SwiftUI + +#if canImport(UIKit) +extension OverlappingImage { + /// Creates an image view that overlaps content at the edges of its frame + /// - Parameters: + /// - uiImage: Image that will overlap content. + /// - top: Overlap percent at top edge. + /// - bottom: Overlap percent at bottom edge. + public init(uiImage: UIImage, top: CGFloat = 0, bottom: CGFloat = 0) { + self.init(Image(uiImage: uiImage), aspectRatio: uiImage.size.aspectRatio, top: top, bottom: bottom) + } + + /// Creates an image view that overlaps content at the edges of its frame + /// - Parameters: + /// - uiImage: Image that will overlap content. + /// - left: Overlap percent at left edge. + /// - right: Overlap percent at right edge. + public init(uiImage: UIImage, left: CGFloat = 0, right: CGFloat = 0) { + self.init(Image(uiImage: uiImage), aspectRatio: uiImage.size.aspectRatio, left: left, right: right) + } +} +#endif diff --git a/Sources/FrameUp/OverlappingImage.swift b/Sources/FrameUp/Images/OverlappingImage.swift similarity index 75% rename from Sources/FrameUp/OverlappingImage.swift rename to Sources/FrameUp/Images/OverlappingImage.swift index 34ac683..27c8c38 100644 --- a/Sources/FrameUp/OverlappingImage.swift +++ b/Sources/FrameUp/Images/OverlappingImage.swift @@ -63,24 +63,6 @@ public struct OverlappingImage: View { self.newAspect = aspectRatio * newWidthPercent } - /// Creates an image view that overlaps content at the edges of its frame - /// - Parameters: - /// - uiImage: Image that will overlap content. - /// - top: Overlap percent at top edge. - /// - bottom: Overlap percent at bottom edge. - public init(uiImage: UIImage, top: CGFloat = 0, bottom: CGFloat = 0) { - self.init(Image(uiImage: uiImage), aspectRatio: uiImage.size.aspectRatio, top: top, bottom: bottom) - } - - /// Creates an image view that overlaps content at the edges of its frame - /// - Parameters: - /// - uiImage: Image that will overlap content. - /// - left: Overlap percent at left edge. - /// - right: Overlap percent at right edge. - public init(uiImage: UIImage, left: CGFloat = 0, right: CGFloat = 0) { - self.init(Image(uiImage: uiImage), aspectRatio: uiImage.size.aspectRatio, left: left, right: right) - } - public var body: some View { Color.clear .overlay( diff --git a/Sources/FrameUp/WidgetSize/FixWidgetPreviewAlignmentBug.swift b/Sources/FrameUp/WidgetSize/FixWidgetPreviewAlignmentBug.swift index 4498069..7b53e3d 100644 --- a/Sources/FrameUp/WidgetSize/FixWidgetPreviewAlignmentBug.swift +++ b/Sources/FrameUp/WidgetSize/FixWidgetPreviewAlignmentBug.swift @@ -8,6 +8,7 @@ import SwiftUI import WidgetKit +#if os(iOS) /// *Not working yet!* View modifier that will adjust the widget's position in SwiftUI Previews /// /// Attempts to fix the widget preview alignment bug @@ -64,3 +65,4 @@ public extension View { self.modifier(FixWidgetPreviewAlignmentBug(adjustment: adjustment)) } } +#endif diff --git a/Sources/FrameUp/WidgetSize/WidgetDemoFrame.swift b/Sources/FrameUp/WidgetSize/WidgetDemoFrame.swift index 0e8283a..ddd966b 100644 --- a/Sources/FrameUp/WidgetSize/WidgetDemoFrame.swift +++ b/Sources/FrameUp/WidgetSize/WidgetDemoFrame.swift @@ -42,7 +42,7 @@ public struct WidgetDemoFrame: View { public var body: some View { Group { - if #available(iOS 15.0, *) { + if #available(iOS 15.0, macOS 12, *) { content(designCanvasSize, designCornerRadius) .containerShape(widgetShape) } else { @@ -55,6 +55,7 @@ public struct WidgetDemoFrame: View { .scaledToFrame(homeScreenSize, contentMode: .fit) } } +#if os(iOS) public extension WidgetDemoFrame { /// Creates a widget demo view for a specified widget size and corner radius for the current device. /// - Parameters: @@ -70,3 +71,4 @@ public extension WidgetDemoFrame { ) } } +#endif diff --git a/Sources/FrameUp/WidgetSize/WidgetFamily+extensions.swift b/Sources/FrameUp/WidgetSize/WidgetFamily+extensions.swift index 7144ce0..403a0a4 100644 --- a/Sources/FrameUp/WidgetSize/WidgetFamily+extensions.swift +++ b/Sources/FrameUp/WidgetSize/WidgetFamily+extensions.swift @@ -9,11 +9,13 @@ import SwiftUI import WidgetKit public extension WidgetFamily { +#if os(iOS) /// Supported families for the current device. static var supportedFamiliesForCurrentDevice: [WidgetFamily] { WidgetSize.supportedSizesForCurrentDevice.compactMap { $0.widgetFamily } } - +#endif + /// Equivalent widget size. Only returns nil for unknown values. var size: WidgetSize? { switch self { diff --git a/Sources/FrameUp/WidgetSize/WidgetRelativeShape.swift b/Sources/FrameUp/WidgetSize/WidgetRelativeShape.swift index 17b9a85..0d4e8f5 100644 --- a/Sources/FrameUp/WidgetSize/WidgetRelativeShape.swift +++ b/Sources/FrameUp/WidgetSize/WidgetRelativeShape.swift @@ -8,6 +8,7 @@ import WidgetKit import SwiftUI +#if os(iOS) @available(iOS, unavailable) @available(iOSApplicationExtension 14.0, *) /// A scalable version of ContainerRelativeShape. @@ -50,3 +51,4 @@ public extension ScaledShape where Content == ScaledContainerRelativeShape { self.init(shape: ScaledContainerRelativeShape(scaleFactor: scaleFactor), scale: scaleSize, anchor: .topLeading) } } +#endif diff --git a/Sources/FrameUp/WidgetSize/WidgetSize+CurrentDevice.swift b/Sources/FrameUp/WidgetSize/WidgetSize+CurrentDevice.swift new file mode 100644 index 0000000..54ccc9e --- /dev/null +++ b/Sources/FrameUp/WidgetSize/WidgetSize+CurrentDevice.swift @@ -0,0 +1,75 @@ +// +// WidgetSize+CurrentDevice.swift +// FrameUp +// +// Created by Ryan Lintott on 2022-01-25. +// + +import SwiftUI + +#if os(iOS) +public extension WidgetSize { + /// The screen size ignoring orientation. + private static let currentScreenSize = UIScreen.main.fixedCoordinateSpace.bounds.size + + /// The current device. + private static let currentDevice = UIDevice.current.userInterfaceIdiom + + /// Find the supported sizes for a specified device + /// - Parameter device: iPhone, iPad, etc + /// - Returns: An array of widget sizes + static func supportedSizes(for device: UIUserInterfaceIdiom) -> [WidgetSize] { + switch device { + case .pad: + if #available(iOS 15.0, *) { + return [.small, .medium, .large, .extraLarge] + } else { + fallthrough + } + case .phone: + return [.small, .medium, .large] + default: + return [] + } + } + + /// Supported widget sizes for the current device. + static var supportedSizesForCurrentDevice: [WidgetSize] { + supportedSizes(for: currentDevice) + } + + /// Size for this widget on the current device. + /// - Parameter iPadTarget: Widget frame target. iPad widgets have a design canvas frame used for laying out the content, and a smaller Home Screen frame that the content is scaled to fit. + /// - Returns: Size for this widget for the current device. Zero if device does not have widgets or if no size is available. + func sizeForCurrentDevice(iPadTarget: WidgetTarget = .homeScreen) -> CGSize { + switch Self.currentDevice { + case .pad: + return sizeForiPad(screenSize: Self.currentScreenSize, target: iPadTarget) + case .phone: + return sizeForiPhone(screenSize: Self.currentScreenSize) + default: + return .zero + } + } + + /// How much the widget is scaled down to fit on the Home Screen. + /// + /// Home Screen width divided by design canvas width + /// - Parameter screenSize: iPad screen size ignoring orientation. + /// - Returns: Widget scale factor between design canvas and Home Screen. + func scaleFactorForiPad(screenSize: CGSize) -> CGFloat { + sizeForiPad(screenSize: Self.currentScreenSize, target: .homeScreen).width / sizeForiPad(screenSize: Self.currentScreenSize, target: .designCanvas).width + } + + /// How much the widget is scaled down to fit on the Home Screen. + /// + /// Home Screen width divided by design canvas width. iPhone value will always be 1. + var scaleFactorForCurrentDevice: CGFloat { + guard Self.currentDevice == .pad else { + return 1 + } + + return scaleFactorForiPad(screenSize: Self.currentScreenSize) + } +} +#endif diff --git a/Sources/FrameUp/WidgetSize/WidgetSize+WidgetKit.swift b/Sources/FrameUp/WidgetSize/WidgetSize+WidgetKit.swift index 6df1a33..c6e7606 100644 --- a/Sources/FrameUp/WidgetSize/WidgetSize+WidgetKit.swift +++ b/Sources/FrameUp/WidgetSize/WidgetSize+WidgetKit.swift @@ -9,7 +9,7 @@ import Foundation import WidgetKit public extension WidgetSize { - /// Equivalent widget family. Optional as extraLarge will return nil unless running iOS 15.0 or later. + /// Equivalent widget family. Optional as extraLarge will return nil unless running iOS 15.0 or later or macOS 12 or later. var widgetFamily: WidgetFamily? { switch self { case .small: return .systemSmall @@ -17,7 +17,11 @@ public extension WidgetSize { case .large: return .systemLarge case .extraLarge: if #available(iOS 15.0, *) { - return .systemExtraLarge + #if os(iOS) + return .systemExtraLarge + #else + return nil + #endif } else { return nil } diff --git a/Sources/FrameUp/WidgetSize/WidgetSize.swift b/Sources/FrameUp/WidgetSize/WidgetSize.swift index eb9324e..ca32ba7 100644 --- a/Sources/FrameUp/WidgetSize/WidgetSize.swift +++ b/Sources/FrameUp/WidgetSize/WidgetSize.swift @@ -29,35 +29,6 @@ public enum WidgetTarget { } public extension WidgetSize { - /// The screen size ignoring orientation. - private static let currentScreenSize = UIScreen.main.fixedCoordinateSpace.bounds.size - - /// The current device. - private static let currentDevice = UIDevice.current.userInterfaceIdiom - - /// Find the supported sizes for a specified device - /// - Parameter device: iPhone, iPad, etc - /// - Returns: An array of widget sizes - static func supportedSizes(for device: UIUserInterfaceIdiom) -> [WidgetSize] { - switch device { - case .pad: - if #available(iOS 15.0, *) { - return [.small, .medium, .large, .extraLarge] - } else { - fallthrough - } - case .phone: - return [.small, .medium, .large] - default: - return [] - } - } - - /// Supported widget sizes for the current device. - static var supportedSizesForCurrentDevice: [WidgetSize] { - supportedSizes(for: currentDevice) - } - /// Smallest widget size possibe for each WidgetFamily static let minimumSizes: [WidgetSize: CGSize] = [ .small: CGSize(width: 141, height: 141), @@ -153,40 +124,6 @@ public extension WidgetSize { func sizeForiPad(screenSize: CGSize, target: WidgetTarget) -> CGSize { Self.sizesForiPad(screenSize: screenSize, target: target)[self] ?? .zero } - - - /// Size for this widget on the current device. - /// - Parameter iPadTarget: Widget frame target. iPad widgets have a design canvas frame used for laying out the content, and a smaller Home Screen frame that the content is scaled to fit. - /// - Returns: Size for this widget for the current device. Zero if device does not have widgets or if no size is available. - func sizeForCurrentDevice(iPadTarget: WidgetTarget = .homeScreen) -> CGSize { - switch Self.currentDevice { - case .pad: - return sizeForiPad(screenSize: Self.currentScreenSize, target: iPadTarget) - case .phone: - return sizeForiPhone(screenSize: Self.currentScreenSize) - default: - return .zero - } - } - - /// How much the widget is scaled down to fit on the Home Screen. - /// - /// Home Screen width divided by design canvas width - /// - Parameter screenSize: iPad screen size ignoring orientation. - /// - Returns: Widget scale factor between design canvas and Home Screen. - func scaleFactorForiPad(screenSize: CGSize) -> CGFloat { - sizeForiPad(screenSize: Self.currentScreenSize, target: .homeScreen).width / sizeForiPad(screenSize: Self.currentScreenSize, target: .designCanvas).width - } - - /// How much the widget is scaled down to fit on the Home Screen. - /// - /// Home Screen width divided by design canvas width. iPhone value will always be 1. - var scaleFactorForCurrentDevice: CGFloat { - guard Self.currentDevice == .pad else { - return 1 - } - return scaleFactorForiPad(screenSize: Self.currentScreenSize) - } } diff --git a/Tests/FrameUpTests/FrameUpTests.swift b/Tests/FrameUpTests/FrameUpTests.swift index 5799dbc..04a431c 100644 --- a/Tests/FrameUpTests/FrameUpTests.swift +++ b/Tests/FrameUpTests/FrameUpTests.swift @@ -6,6 +6,5 @@ final class FrameUpTests: XCTestCase { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct // results. - XCTAssertEqual(FrameUp().text, "Hello, World!") } }