-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic layout system support (#7)
* Add AlignmentID * Add Angle and Axis test case and update docs * Add Spacing and LayoutComputer * Add AlignmentKey * Add ViewDimensions missing implementation * Update Alignment doc for OpenSwiftUI * Add empty HStack implementation
- Loading branch information
Showing
63 changed files
with
1,968 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// | ||
// Defaultable.swift | ||
// OpenSwiftUI | ||
// | ||
// Created by Kyle on 2023/12/16. | ||
// Lastest Version: iOS 15.5 | ||
// Status: Complete | ||
|
||
protocol Defaultable { | ||
associatedtype Value | ||
static var defaultValue: Value { get } | ||
} |
165 changes: 165 additions & 0 deletions
165
Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentID.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// | ||
// AlignmentID.swift | ||
// OpenSwiftUI | ||
// | ||
// Created by Kyle on 2023/12/16. | ||
// Lastest Version: iOS 15.5 | ||
// Status: Complete | ||
|
||
#if canImport(Darwin) | ||
import CoreGraphics | ||
#elseif os(Linux) | ||
import Foundation | ||
#endif | ||
|
||
/// A type that you use to create custom alignment guides. | ||
/// | ||
/// Every built-in alignment guide that ``VerticalAlignment`` or | ||
/// ``HorizontalAlignment`` defines as a static property, like | ||
/// ``VerticalAlignment/top`` or ``HorizontalAlignment/leading``, has a | ||
/// unique alignment identifier type that produces the default offset for | ||
/// that guide. To create a custom alignment guide, define your own alignment | ||
/// identifier as a type that conforms to the `AlignmentID` protocol, and | ||
/// implement the required ``AlignmentID/defaultValue(in:)`` method: | ||
/// | ||
/// private struct FirstThirdAlignment: AlignmentID { | ||
/// static func defaultValue(in context: ViewDimensions) -> CGFloat { | ||
/// context.height / 3 | ||
/// } | ||
/// } | ||
/// | ||
/// When implementing the method, calculate the guide's default offset | ||
/// from the view's origin. If it's helpful, you can use information from the | ||
/// ``ViewDimensions`` input in the calculation. This parameter provides context | ||
/// about the specific view that's using the guide. The above example creates an | ||
/// identifier called `FirstThirdAlignment` and calculates a default value | ||
/// that's one-third of the height of the aligned view. | ||
/// | ||
/// Use the identifier's type to create a static property in an extension of | ||
/// one of the alignment guide types, like ``VerticalAlignment``: | ||
/// | ||
/// extension VerticalAlignment { | ||
/// static let firstThird = VerticalAlignment(FirstThirdAlignment.self) | ||
/// } | ||
/// | ||
/// You can apply your custom guide like any of the built-in guides. For | ||
/// example, you can use an ``HStack`` to align its views at one-third | ||
/// of their height using the guide defined above: | ||
/// | ||
/// struct StripesGroup: View { | ||
/// var body: some View { | ||
/// HStack(alignment: .firstThird, spacing: 1) { | ||
/// HorizontalStripes().frame(height: 60) | ||
/// HorizontalStripes().frame(height: 120) | ||
/// HorizontalStripes().frame(height: 90) | ||
/// } | ||
/// } | ||
/// } | ||
/// | ||
/// struct HorizontalStripes: View { | ||
/// var body: some View { | ||
/// VStack(spacing: 1) { | ||
/// ForEach(0..<3) { _ in Color.blue } | ||
/// } | ||
/// } | ||
/// } | ||
/// | ||
/// Because each set of stripes has three equal, vertically stacked | ||
/// rectangles, they align at the bottom edge of the top rectangle. This | ||
/// corresponds in each case to a third of the overall height, as | ||
/// measured from the origin at the top of each set of stripes: | ||
/// | ||
/// ![Three vertical stacks of rectangles, arranged in a row. | ||
/// The rectangles in each stack have the same height as each other, but | ||
/// different heights than the rectangles in the other stacks. The bottom edges | ||
/// of the top-most rectangle in each stack are aligned with each | ||
/// other.](AlignmentId-1-iOS) | ||
/// | ||
/// You can also use the ``View/alignmentGuide(_:computeValue:)-6y3u2`` view | ||
/// modifier to alter the behavior of your custom guide for a view, as you | ||
/// might alter a built-in guide. For example, you can change | ||
/// one of the stacks of stripes from the previous example to align its | ||
/// `firstThird` guide at two thirds of the height instead: | ||
/// | ||
/// struct StripesGroupModified: View { | ||
/// var body: some View { | ||
/// HStack(alignment: .firstThird, spacing: 1) { | ||
/// HorizontalStripes().frame(height: 60) | ||
/// HorizontalStripes().frame(height: 120) | ||
/// HorizontalStripes().frame(height: 90) | ||
/// .alignmentGuide(.firstThird) { context in | ||
/// 2 * context.height / 3 | ||
/// } | ||
/// } | ||
/// } | ||
/// } | ||
/// | ||
/// The modified guide calculation causes the affected view to place the | ||
/// bottom edge of its middle rectangle on the `firstThird` guide, which aligns | ||
/// with the bottom edge of the top rectangle in the other two groups: | ||
/// | ||
/// ![Three vertical stacks of rectangles, arranged in a row. | ||
/// The rectangles in each stack have the same height as each other, but | ||
/// different heights than the rectangles in the other stacks. The bottom edges | ||
/// of the top-most rectangle in the first two stacks are aligned with each | ||
/// other, and with the bottom edge of the middle rectangle in the third | ||
/// stack.](AlignmentId-2-iOS) | ||
/// | ||
public protocol AlignmentID { | ||
/// Calculates a default value for the corresponding guide in the specified | ||
/// context. | ||
/// | ||
/// Implement this method when you create a type that conforms to the | ||
/// ``AlignmentID`` protocol. Use the method to calculate the default | ||
/// offset of the corresponding alignment guide. SwiftUI interprets the | ||
/// value that you return as an offset in the coordinate space of the | ||
/// view that's being laid out. For example, you can use the context to | ||
/// return a value that's one-third of the height of the view: | ||
/// | ||
/// private struct FirstThirdAlignment: AlignmentID { | ||
/// static func defaultValue(in context: ViewDimensions) -> CGFloat { | ||
/// context.height / 3 | ||
/// } | ||
/// } | ||
/// | ||
/// You can override the default value that this method returns for a | ||
/// particular guide by adding the | ||
/// ``View/alignmentGuide(_:computeValue:)-9mdoh`` view modifier to a | ||
/// particular view. | ||
/// | ||
/// - Parameter context: The context of the view that you apply | ||
/// the alignment guide to. The context gives you the view's dimensions, | ||
/// as well as the values of other alignment guides that apply to the | ||
/// view, including both built-in and custom guides. You can use any of | ||
/// these values, if helpful, to calculate the value for your custom | ||
/// guide. | ||
/// | ||
/// - Returns: The offset of the guide from the origin in the | ||
/// view's coordinate space. | ||
static func defaultValue(in context: ViewDimensions) -> CGFloat | ||
|
||
static func _combineExplicit(childValue: CGFloat, _ n: Int, into parentValue: inout CGFloat?) | ||
} | ||
|
||
extension AlignmentID { | ||
// n == 0: | ||
// value = childValue = c0 | ||
// parentValue = childValue = c0 | ||
// n == 1: | ||
// value = parentValue! = c0 | ||
// parentValue = (c0 + c1) / 2 | ||
// n == 2: | ||
// value = parentValue! = (c0 + c1) / 2 | ||
// parentValue = (c0 + c1 + c2) / 3 | ||
public static func _combineExplicit(childValue: CGFloat, _ n: Int, into parentValue: inout CGFloat?) { | ||
let value = (n == 0) ? childValue : parentValue! | ||
let n = CGFloat(n) | ||
parentValue = (value * n + childValue) / (n + 1.0) | ||
} | ||
} | ||
|
||
protocol FrameAlignment: AlignmentID {} | ||
|
||
extension FrameAlignment { | ||
static func _combineExplicit(childValue _: CGFloat, _: Int, into _: inout CGFloat?) {} | ||
} |
43 changes: 43 additions & 0 deletions
43
Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// | ||
// AlignmentKey.swift | ||
// OpenSwiftUI | ||
// | ||
// Created by Kyle on 2023/12/17. | ||
// Lastest Version: iOS 15.5 | ||
// Status: Complete | ||
// ID: E20796D15DD3D417699102559E024115 | ||
|
||
@usableFromInline | ||
@frozen | ||
struct AlignmentKey: Hashable, Comparable { | ||
private let bits: UInt | ||
|
||
@usableFromInline | ||
static func < (lhs: AlignmentKey, rhs: AlignmentKey) -> Bool { | ||
lhs.bits < rhs.bits | ||
} | ||
|
||
@UnsafeLockedPointer | ||
private static var typeCache = TypeCache(typeIDs: [:], types: []) | ||
|
||
struct TypeCache { | ||
var typeIDs: [ObjectIdentifier: UInt] | ||
var types: [AlignmentID.Type] | ||
} | ||
|
||
init(id: AlignmentID.Type, axis _: Axis) { | ||
let index: UInt | ||
if let value = AlignmentKey.typeCache.typeIDs[ObjectIdentifier(id)] { | ||
index = value | ||
} else { | ||
index = UInt(AlignmentKey.typeCache.types.count) | ||
AlignmentKey.typeCache.types.append(id) | ||
AlignmentKey.typeCache.typeIDs[ObjectIdentifier(id)] = index | ||
} | ||
bits = index * 2 + 3 | ||
} | ||
|
||
var id: AlignmentID.Type { | ||
AlignmentKey.typeCache.types[Int(bits / 2 - 1)] | ||
} | ||
} |
Oops, something went wrong.