Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,28 @@ on:
branches: [main]

jobs:
build-platforms:
runs-on: macos-26
strategy:
fail-fast: false
matrix:
platform: [iOS, tvOS, visionOS, watchOS]

steps:
- uses: actions/checkout@v4

- name: Build for ${{ matrix.platform }}
run: xcodebuild -scheme HandySwiftUI -destination 'generic/platform=${{ matrix.platform }}' CODE_SIGNING_ALLOWED=NO build

test-macos:
runs-on: macos-26

steps:
- uses: actions/checkout@v4

- name: Build package
run: swift build
run: swift build --build-system swiftbuild

- name: Run tests
if: hashFiles('Tests/**') != ''
run: swift test
run: swift test --build-system swiftbuild
116 changes: 74 additions & 42 deletions Sources/HandySwiftUI/Extensions/ColorExt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import SwiftUI
#endif

// MARK: - Apple-provided colors
// UIKit on watchOS has no semantic colors. There, the label and standard colors map to SwiftUI's own colors, which use the watchOS
// system palette, and the colors without a watchOS counterpart (backgrounds, fills, separators, …) are not available.
extension Color {
// MARK: - UI Element Colors (https://developer.apple.com/documentation/uikit/uicolor/ui_element_colors)
// MARK: Label Colors
/// The color for text labels that contain primary content.
public static var label: Color {
#if os(macOS)
return Color(NSColor.labelColor)
#elseif os(watchOS)
return .primary
#else
return Color(UIColor.label)
#endif
Expand All @@ -23,31 +27,35 @@ extension Color {
public static var secondaryLabel: Color {
#if os(macOS)
return Color(NSColor.secondaryLabelColor)
#elseif os(watchOS)
return .secondary
#else
return Color(UIColor.secondaryLabel)
#endif
}

/// The color for text labels that contain tertiary content.
public static var tertiaryLabel: Color {
#if os(macOS)
return Color(NSColor.tertiaryLabelColor)
#else
return Color(UIColor.tertiaryLabel)
#endif
}
#if !os(watchOS)
/// The color for text labels that contain tertiary content.
public static var tertiaryLabel: Color {
#if os(macOS)
return Color(NSColor.tertiaryLabelColor)
#else
return Color(UIColor.tertiaryLabel)
#endif
}

/// The color for text labels that contain quaternary content.
public static var quaternaryLabel: Color {
#if os(macOS)
return Color(NSColor.quaternaryLabelColor)
#else
return Color(UIColor.quaternaryLabel)
#endif
}
/// The color for text labels that contain quaternary content.
public static var quaternaryLabel: Color {
#if os(macOS)
return Color(NSColor.quaternaryLabelColor)
#else
return Color(UIColor.quaternaryLabel)
#endif
}
#endif

// MARK: Fill Colors
#if !os(tvOS)
#if !os(tvOS) && !os(watchOS)
/// An overlay fill color for thin and small shapes.
public static var systemFill: Color {
#if os(macOS)
Expand Down Expand Up @@ -152,39 +160,45 @@ extension Color {
#endif

// MARK: Separator Colors
/// The color for thin borders or divider lines that allows some underlying content to be visible.
public static var separator: Color {
#if os(macOS)
return Color(NSColor.separatorColor)
#else
return Color(UIColor.separator)
#endif
}
#if !os(watchOS)
/// The color for thin borders or divider lines that allows some underlying content to be visible.
public static var separator: Color {
#if os(macOS)
return Color(NSColor.separatorColor)
#else
return Color(UIColor.separator)
#endif
}

/// The color for borders or divider lines that hides any underlying content.
public static var opaqueSeparator: Color {
#if os(macOS)
return Color(NSColor.separatorColor).opacity(1)
#else
return Color(UIColor.opaqueSeparator)
#endif
}
/// The color for borders or divider lines that hides any underlying content.
public static var opaqueSeparator: Color {
#if os(macOS)
return Color(NSColor.separatorColor).opacity(1)
#else
return Color(UIColor.opaqueSeparator)
#endif
}
#endif

// MARK: Link Color
/// The color for links.
public static var link: Color {
#if os(macOS)
return Color(NSColor.linkColor)
#else
return Color(UIColor.link)
#endif
}
#if !os(watchOS)
/// The color for links.
public static var link: Color {
#if os(macOS)
return Color(NSColor.linkColor)
#else
return Color(UIColor.link)
#endif
}
#endif

// MARK: - Standard Colors (https://developer.apple.com/documentation/uikit/uicolor/standard_colors)
// MARK: Adaptable Colors
public static var systemBlue: Color {
#if os(macOS)
return Color(NSColor.systemBlue)
#elseif os(watchOS)
return .blue
#else
return Color(UIColor.systemBlue)
#endif
Expand All @@ -193,6 +207,8 @@ extension Color {
public static var systemGreen: Color {
#if os(macOS)
return Color(NSColor.systemGreen)
#elseif os(watchOS)
return .green
#else
return Color(UIColor.systemGreen)
#endif
Expand All @@ -201,6 +217,8 @@ extension Color {
public static var systemIndigo: Color {
#if os(macOS)
return Color(NSColor.systemIndigo)
#elseif os(watchOS)
return .indigo
#else
return Color(UIColor.systemIndigo)
#endif
Expand All @@ -209,6 +227,8 @@ extension Color {
public static var systemOrange: Color {
#if os(macOS)
return Color(NSColor.systemOrange)
#elseif os(watchOS)
return .orange
#else
return Color(UIColor.systemOrange)
#endif
Expand All @@ -217,6 +237,8 @@ extension Color {
public static var systemPink: Color {
#if os(macOS)
return Color(NSColor.systemPink)
#elseif os(watchOS)
return .pink
#else
return Color(UIColor.systemPink)
#endif
Expand All @@ -225,6 +247,8 @@ extension Color {
public static var systemPurple: Color {
#if os(macOS)
return Color(NSColor.systemPurple)
#elseif os(watchOS)
return .purple
#else
return Color(UIColor.systemPurple)
#endif
Expand All @@ -233,6 +257,8 @@ extension Color {
public static var systemRed: Color {
#if os(macOS)
return Color(NSColor.systemRed)
#elseif os(watchOS)
return .red
#else
return Color(UIColor.systemRed)
#endif
Expand All @@ -241,6 +267,8 @@ extension Color {
public static var systemTeal: Color {
#if os(macOS)
return Color(NSColor.systemTeal)
#elseif os(watchOS)
return .teal
#else
return Color(UIColor.systemTeal)
#endif
Expand All @@ -249,6 +277,8 @@ extension Color {
public static var systemYellow: Color {
#if os(macOS)
return Color(NSColor.systemYellow)
#elseif os(watchOS)
return .yellow
#else
return Color(UIColor.systemYellow)
#endif
Expand All @@ -258,12 +288,14 @@ extension Color {
public static var systemGray: Color {
#if os(macOS)
return Color(NSColor.systemGray)
#elseif os(watchOS)
return .gray
#else
return Color(UIColor.systemGray)
#endif
}

#if !os(tvOS)
#if !os(tvOS) && !os(watchOS)
public static var systemGray2: Color {
#if os(macOS)
return Color(NSColor.systemGray)
Expand Down
58 changes: 31 additions & 27 deletions Sources/HandySwiftUI/Extensions/ImageExt.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ImageIO
import SwiftUI
import UniformTypeIdentifiers

Expand Down Expand Up @@ -106,35 +107,38 @@ extension Image {
import UIKit

extension UIImage {
/// Resizes the image to fit within the specified maximum width and height.
///
/// - Parameters:
/// - maxWidth: The maximum width for the resized image.
/// - maxHeight: The maximum height for the resized image.
/// - Returns: A new `UIImage` instance with the resized dimensions, or `nil` if resizing fails.
@MainActor
public func resized(maxWidth: CGFloat, maxHeight: CGFloat) -> UIImage? {
let size = self.size
if size.width <= maxWidth && size.height <= maxHeight { return self }

#if os(visionOS)
let screenScale = 1.0
#else
let screenScale = UIScreen.main.scale
#endif
let widthRatio = (maxWidth / screenScale) / size.width
let heightRatio = (maxHeight / screenScale) / size.height
let scaleFactor = min(widthRatio, heightRatio)

let newSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)
// watchOS has neither UIGraphicsImageRenderer nor UIScreen
#if !os(watchOS)
/// Resizes the image to fit within the specified maximum width and height.
///
/// - Parameters:
/// - maxWidth: The maximum width for the resized image.
/// - maxHeight: The maximum height for the resized image.
/// - Returns: A new `UIImage` instance with the resized dimensions, or `nil` if resizing fails.
@MainActor
public func resized(maxWidth: CGFloat, maxHeight: CGFloat) -> UIImage? {
let size = self.size
if size.width <= maxWidth && size.height <= maxHeight { return self }

#if os(visionOS)
let screenScale = 1.0
#else
let screenScale = UIScreen.main.scale
#endif
let widthRatio = (maxWidth / screenScale) / size.width
let heightRatio = (maxHeight / screenScale) / size.height
let scaleFactor = min(widthRatio, heightRatio)

let newSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)

let renderer = UIGraphicsImageRenderer(size: newSize)
let image = renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: newSize))
}

let renderer = UIGraphicsImageRenderer(size: newSize)
let image = renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: newSize))
return image.withRenderingMode(self.renderingMode)
}

return image.withRenderingMode(self.renderingMode)
}
#endif

/// Converts the image to HEIC data with specified compression quality.
///
Expand Down
Loading
Loading