Skip to content

Commit

Permalink
Add SliderStyle support (#6)
Browse files Browse the repository at this point in the history
* Update _EnvironmentKeyTransformModifier

* Add SliderStyle implementation

* Add implementation for AccessibilityNumber

* Add AccessibilityBoundedNumber.swift implementation

* Add AbstractAnyAccessibilityValue

* Add AccessibilityBoundedNumber init implementation

* Add Slider implementation

* Update Slider.body

* Update Slider and View’s doc

* Fix doc typo

* Fix build warnings
  • Loading branch information
Kyle-Ye committed Dec 16, 2023
1 parent 702ce69 commit 7481074
Show file tree
Hide file tree
Showing 33 changed files with 1,541 additions and 29 deletions.
7 changes: 5 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ let openSwiftUITarget = Target.target(
],
swiftSettings: [
.enableExperimentalFeature("AccessLevelOnImport"),
.define("OPENSWIFTUI_SUPPRESS_DEPRECATED_WARNINGS")
],
linkerSettings: [
.unsafeFlags(
Expand All @@ -36,13 +37,15 @@ let openSwiftUITestTarget = Target.testTarget(
name: "OpenSwiftUITests",
dependencies: [
"OpenSwiftUI",
]
],
exclude: ["README.md"]
)
let openSwiftUICompatibilityTestTarget = Target.testTarget(
name: "OpenSwiftUICompatibilityTests",
dependencies: [
"OpenSwiftUI",
]
],
exclude: ["README.md"]
)

let package = Package(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,8 @@
// Status: WIP
// ID: 0D3243EDC3DD4D641848661DCC354D4B

struct AccessibilityButtonShapeModifier<V: View>: AccessibilityConfigurationModifier {
typealias Configuration = Never
typealias Content = V

// deleted method
var configuration: Never { fatalError() }

func body(content: V) -> some View {
struct AccessibilityButtonShapeModifier<Content: View>: AccessibilityConfigurationModifier {
func body(content: Content) -> some View {
content.modifier(Child())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
// Status: Complete

protocol AccessibilityConfigurationModifier {
associatedtype Configuration
associatedtype Configuration = Never
associatedtype Body
associatedtype Content

var configuration: Configuration { get }
func body(content: Self.Content) -> Self.Body
}

extension AccessibilityConfigurationModifier where Configuration == Never {
var configuration: Configuration { fatalError() }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
struct AccessibilityLabelModifier<Content: View>: AccessibilityConfigurationModifier {
func body(content: Content) -> some View {
content.modifier(ChildModifier())
}

private struct ChildModifier: PrimitiveViewModifier {}
}

extension View {
func accessibilityLabel() -> some View {
AccessibilityLabelModifier().body(content: self)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// AccessibilityBoundedNumber.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/2.
// Lastest Version: iOS 15.5
// Status: Complete

import Foundation

struct AccessibilityBoundedNumber {
var number: AccessibilityNumber
var lowerBound: AccessibilityNumber?
var upperBound: AccessibilityNumber?
var stride: AccessibilityNumber?

init?<S: Strideable>(for value: S, in range: ClosedRange<S>?, by strideValue: S.Stride?) {
let clampedValue = range.map { value.clamped(to: $0) }
let newValue = clampedValue ?? value
guard let numericValue = newValue as? AccessibilityNumeric,
let numberValue = numericValue.asNumber() else {
return nil
}
number = numberValue
if let range {
lowerBound = range.minimumValue?.asNumber()
upperBound = range.maximumValue?.asNumber()
}
if let strideValue,
let numericStride = strideValue as? AccessibilityNumeric {
stride = numericStride.asNumber()
}
}
}

extension AccessibilityBoundedNumber: AccessibilityValue {
// This kind of description logic is very strange
// But that's how Apple's implementation even on iOS 17 :)
// eg.
// For 1.5 and [1.0, 2.0], the accessiblity output would be 150%
// For 1.5 and [1.3, 2.3], the accessiblity output would be 1.5
var localizedDescription: String? {
let range: Double = if let lowerBound, let upperBound {
upperBound.base.doubleValue - lowerBound.base.doubleValue
} else {
.zero
}
if abs(range - 100) >= .ulpOfOne {
let style: NumberFormatter.Style = (abs(range - 1.0) < .ulpOfOne) ? .percent : .decimal
return NumberFormatter.localizedString(from: number.base, number: style)
} else {
return NumberFormatter.localizedString(from: NSNumber(value: number.base.doubleValue / 100), number: .percent)
}
}

var displayDescription: String? {
localizedDescription
}

var value: NSNumber { number.value }
var minValue: NSNumber? { lowerBound?.value }
var maxValue: NSNumber? { upperBound?.value }
static var type: AnyAccessibilityValueType { .boundedNumber }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// AccessibilityNumber.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/2.
// Lastest Version: iOS 15.5
// Status: Complete

import Foundation

struct AccessibilityNumber {
var base: NSNumber
}

extension AccessibilityNumber: AccessibilityValue {
var localizedDescription: String? {
NumberFormatter.localizedString(from: value, number: .decimal)
}
var displayDescription: String? { localizedDescription }
var value: NSNumber { base }
var minValue: NSNumber? { nil }
var maxValue: NSNumber? { nil }
static var type: AnyAccessibilityValueType { .number }
}

extension AccessibilityNumber: ExpressibleByFloatLiteral {
init(floatLiteral value: Double) {
base = NSNumber(floatLiteral: value)
}
}

extension AccessibilityNumber: ExpressibleByIntegerLiteral {
init(integerLiteral value: Int) {
base = NSNumber(integerLiteral: value)
}
}

extension AccessibilityNumber: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let data = try container.decode(Data.self)
self.base = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSNumber.self, from: data)!
}

func encode(to encoder: Encoder) throws {
let data = try NSKeyedArchiver.archivedData(withRootObject: base, requiringSecureCoding: true)
var container = encoder.singleValueContainer()
try container.encode(data)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// AccessibilityNumber.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/3.
// Lastest Version: iOS 15.5
// Status: Complete

import Foundation

protocol AccessibilityNumeric {
var isValidMinValue: Bool { get }
var isValidMaxValue: Bool { get }
func asNumber() -> AccessibilityNumber?
}

extension AccessibilityNumeric where Self: FixedWidthInteger {
var isValidMinValue: Bool {
// TODO: Add Unit Test and check usage
if Self.bitWidth == 8 || !Self.isSigned {
true
} else {
self != .min
}
}

var isValidMaxValue: Bool { self != .max }
}

extension Int: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Int8: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Int16: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Int32: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Int64: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt8: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt16: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt32: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt64: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension AccessibilityNumeric where Self: BinaryFloatingPoint {
var isValidMinValue: Bool {
isFinite && self > -Self.greatestFiniteMagnitude
}

var isValidMaxValue: Bool {
isFinite && self < Self.greatestFiniteMagnitude
}
}

extension Float: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Double: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// AccessibilityPlatformSafe.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/2.
// Lastest Version: iOS 15.5
// Status: Complete

import Foundation

protocol AccessibilityPlatformSafe {}

extension String: AccessibilityPlatformSafe {}
extension Double: AccessibilityPlatformSafe {}
extension Int: AccessibilityPlatformSafe {}
extension UInt: AccessibilityPlatformSafe {}
extension UInt8: AccessibilityPlatformSafe {}
extension Bool: AccessibilityPlatformSafe {}
extension NSNumber: AccessibilityPlatformSafe {}
extension Optional: AccessibilityPlatformSafe where Wrapped: AccessibilityPlatformSafe {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// AccessibilitySliderValue.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/16.
// Lastest Version: iOS 15.5
// Status: Complete

struct AccessibilitySliderValue: AccessibilityValueByProxy {
var base: AccessibilityBoundedNumber
static var type: AnyAccessibilityValueType { .slider }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//
// AccessibilityValue.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/2.
// Lastest Version: iOS 15.5
// Status: Complete

import Foundation

protocol AccessibilityValue: Equatable {
associatedtype PlatformValue: AccessibilityPlatformSafe
var localizedDescription: String? { get }
var displayDescription: String? { get }
var value: PlatformValue { get }
var minValue: PlatformValue? { get }
var maxValue: PlatformValue? { get }
var step: PlatformValue? { get }
static var type: AnyAccessibilityValueType { get }
}

extension AccessibilityValue {
var minValue: PlatformValue? { nil }
var maxValue: PlatformValue? { nil }
var step: PlatformValue? { nil }
}

extension AccessibilityValue where PlatformValue: CustomStringConvertible {
var localizedDescription: String? { value.description }
var displayDescription: String? { value.description }
}

extension AccessibilityValue where Self == Self.PlatformValue {
var value: PlatformValue { self }
}

extension Int: AccessibilityValue {
typealias PlatformValue = Int
static var type: AnyAccessibilityValueType { .int }
}

extension Double: AccessibilityValue {
typealias PlatformValue = Double
static var type: AnyAccessibilityValueType { .number }
}

extension Bool: AccessibilityValue {
typealias PlatformValue = Bool
static var type: AnyAccessibilityValueType { .bool }
}

extension String: AccessibilityValue {
typealias PlatformValue = String
static var type: AnyAccessibilityValueType { .string }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// AccessibilityValueByProxy.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/16.
// Lastest Version: iOS 15.5
// Status: Complete

protocol AccessibilityValueByProxy: AccessibilityValue {
associatedtype Base: AccessibilityValue
var base: Base { get }
}

extension AccessibilityValueByProxy {
var localizedDescription: String? { base.localizedDescription }
var displayDescription: String? { base.displayDescription }
var value: Base.PlatformValue { base.value }
var minValue: Base.PlatformValue? { base.minValue }
var maxValue: Base.PlatformValue? { base.maxValue }
var step: Base.PlatformValue? { base.step }
}
Loading

0 comments on commit 7481074

Please sign in to comment.