Skip to content

Commit 36235c4

Browse files
authored
Merge pull request #12 from ryanlintott/beta
Updated to Swift 6
2 parents 9ecd806 + a0e2045 commit 36235c4

62 files changed

Lines changed: 854 additions & 475 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Package.swift

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,6 @@ let package = Package(
3030
name: "FrameUpTests",
3131
dependencies: ["FrameUp"]
3232
),
33-
]
33+
],
34+
swiftLanguageVersions: [.v5, .version("6")]
3435
)
35-
36-
let swiftSettings: [SwiftSetting] = [
37-
.enableExperimentalFeature("StrictConcurrency"),
38-
.enableUpcomingFeature("DisableOutwardActorInference"),
39-
.enableUpcomingFeature("IsolatedDefaultValues"),
40-
]
41-
42-
for target in package.targets {
43-
var settings = target.swiftSettings ?? []
44-
settings.append(contentsOf: swiftSettings)
45-
target.swiftSettings = settings
46-
}

README.md

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ A collection of SwiftUI tools to help with layout.
1414
- SwiftUI [`Layouts`](#layouts) like [`HFlowLayout`](#hflowlayout), [`VFlowLayout`](#vflowlayout), [`VMasonryLayout`](#vmasonrylayout), [`HMasonryLayout`](#hmasonrylayout), and [`LayoutThatFits`](#layoutthatfits)
1515
- [`AutoRotatingView`](#autorotatingview) to set allowable orientations for a view.
1616
- [Frame Adjustment](#frame-adjustment) tools like [`WidthReader`](#widthreader), [`HeightReader`](#heightreader), [`onSizeChange(perform:)`](#onsizechangeperform), [`keyboardHeight`](#keyboardHeight), [`.relativePadding`](#relativepaddingedges-lengthfactor), [`ScaledView`](#scaledview) and [`OverlappingImage`](#overlappingimage).
17+
- [unclippedTextRenderer](#unclippedtextrenderer) for fixing clipped `Text`.
1718
- [`SmartScrollView`](#smartscrollview) with optional scrolling, a content-fitable frame, and live edge inset values.
18-
- [`TwoSidedView`](#twosidedview) and [`FlippingView`](#flippingview) for making flippable views with a different view on the back side.
19+
- [`FlippingView`](#flippingview) and [`rotation3DEffect(back:)`](#rotation3deffectback) for making flippable views with a different view on the back side.
1920
- [`TabMenu`](#tabmenu), a customizable iOS tab menu with `onReselect` and `onDoubleTap` functions.
2021

2122
Some widget-related tools
@@ -332,6 +333,20 @@ VStack(spacing: 0) {
332333
}
333334
```
334335

336+
## Text
337+
### unclippedTextRenderer
338+
*\*iOS 18+, macOS 15+, watchOS 11+, tvOS 18+, visionOS 2+*
339+
340+
SwiftUI `Text` has a clipping frame that cannot be adjusted and will occasionally clip the rendered text. This modifier applies an `UnclippedTextRenderer` that removes this clipping frame.
341+
342+
This modifier is unnecessary if another text renderer is used as all text renderers will remove the clipping frame.
343+
344+
```swift
345+
Text("f")
346+
.font(.custom("zapfino", size: 30))
347+
.unclippedTextRenderer()
348+
```
349+
335350
## SmartScrollView
336351
*\*iOS only*
337352

@@ -352,9 +367,26 @@ SmartScrollView(.vertical, showsIndicators: true, optionalScrolling: true, shrin
352367
- If placed directly inside a NavigationView with a resizing header, this view may behave strangely when scrolling. To avoid this add 1 point of padding just inside the NavigationView.
353368
- If the available space for this view grows for any reason other than screen rotation, this view might not grow to fill the space.
354369

355-
## TwoSidedView
356-
### rotation3DEffect(angle:, axis:, anchor:, anchorZ, perspective:, back:)
357-
An alternative to rotation3DEffect that provides a closure for views that will be seen on the back side of this view.
370+
## FlippingView
371+
372+
### FlippingView
373+
A two-sided view that can be flipped by tapping or swiping.
374+
375+
The axis, anchor, perspective, drag distance to flip, animation for tap to flip and more can all be customized.
376+
377+
For visionOS a slightly different initializer might be needed and the flips will occur in 3d space. If instead you want a perspective effect on a flat view you can use `PerspectiveFlippingView`
378+
379+
```swift
380+
FlippingView(flips: $flips) {
381+
Color.blue.overlay(Text("Up"))
382+
} back: {
383+
Color.red.overlay(Text("Back"))
384+
}
385+
```
386+
387+
### rotation3DEffect(angle:axis:anchor:anchorZ:perspective:backsideFlip:back:)
388+
*\*deprecated in visionOS*
389+
Renders a view’s content as if it’s rotated in three dimensions around the specified axis with a closure containing a different view to show on the back.
358390

359391
The example below is a view with two sides. One blue side that says "Front" and a red side on the back that says "Back". Changing the angle will show each side as it becomes visible.
360392

@@ -365,17 +397,25 @@ Color.blue.overlay(Text("Front"))
365397
}
366398
```
367399

368-
### FlippingView
369-
A two-sided view that can be flipped by tapping or swiping.
400+
### rotation3DEffect(angle:axis:anchor:backsideFlip:back:)
401+
*\*visionOS*
402+
Rotates this view’s rendered output in three dimensions around the given axis of rotation with a closure containing a different view on the back. A minimum thickness that offsets the two views is required to ensure the side facing the user renders on top.
403+
```swift
404+
Color.blue.overlay(Text("Front"))
405+
.rotation3DEffect(angle) {
406+
Color.red.overlay(Text("Back"))
407+
}
408+
```
370409

371-
The axis, anchor, perspective, drag distance to flip, animation for tap to flip and more can all be customized.
410+
### perspectiveRotationEffect(angle:axis:anchor:anchorZ:perspective:backsideFlip:back:)
411+
*\*visionOS*
412+
Renders a view’s content as if it’s rotated in three dimensions around the specified axis with a closure containing a different view to show on the back. The view is not actually rotated in 3d space.
372413

373414
```swift
374-
FlippingView(flips: $flips) {
375-
Color.blue.overlay(Text("Up"))
376-
} back: {
377-
Color.red.overlay(Text("Back"))
378-
}
415+
Color.blue.overlay(Text("Front"))
416+
.rotation3DEffect(angle) {
417+
Color.red.overlay(Text("Back"))
418+
}
379419
```
380420

381421
## TabMenu
@@ -485,6 +525,7 @@ If you like the SwiftUI `Layout` protocol but you need to target an older OS tha
485525
An `FULayout` will work in the same way as a SwiftUI `Layout`. The main difference is it will require a `maxWidth` or `maxHeight` parameter when initializing in order to know the available space. This can be provided by `GeometryReader` or with [`WidthReader`](#widthreader) or [`HeightReader`](#heightreader) from this package.
486526

487527
### ViewBuilder
528+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
488529
An `FULayout` uses `callAsFunction()` with a view builder so you can use it just like a SwiftUI `Layout`.
489530

490531
```swift
@@ -497,6 +538,7 @@ VFlow(maxWidth: 200) {
497538
*Caution: This method uses Apple's private protocol `_VariadicView` under the hood. There is a small risk Apple could change the implementation so if this concerns you, use method 2 below.*
498539

499540
### `.forEach()`
541+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
500542
This method works in a very similar way to `ForEach()`.
501543

502544
```swift
@@ -508,6 +550,7 @@ MyFULayout().forEach(["Hello", "World"], id: \.self) { item in
508550

509551
## FULayouts
510552
### HFlow
553+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
511554
The [`FULayout`](#fulayout) equivalent of [`HFlowLayout`](#hflowlayout).
512555

513556
A FrameUp `FULayout` that arranges views in horizontal rows flowing from one to the next with adjustable horizontal and vertical spacing and support for horiztonal and vertical alignment including a justified alignment that will space elements in completed rows evenly.
@@ -525,6 +568,7 @@ WidthReader { width in
525568
```
526569

527570
### VFlow
571+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
528572
The [`FULayout`](#fulayout) equivalent of [`VFlowLayout`](#vflowlayout).
529573

530574
A FrameUp `FULayout` that arranges views in vertical columns flowing from one to the next with adjustable horizontal and vertical spacing and support for horiztonal and vertical alignment including a justified alignment that will space elements in completed columns evenly.
@@ -542,6 +586,7 @@ WidthReader { width in
542586
```
543587

544588
### HMasonry
589+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
545590
The [`FULayout`](#fulayout) equivalent of [`HMasonryLayout`](#hmasonrylayout).
546591

547592
A FrameUp `FULayout` that arranges views into a set number of rows by adding each view to the shortest row.
@@ -558,6 +603,7 @@ HeightReader { height in
558603
```
559604

560605
### VMasonry
606+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
561607
The [`FULayout`](#fulayout) equivalent of [`VMasonryLayout`](#vmasonrylayout).
562608

563609
A FrameUp `FULayout` that arranges views into a set number of rows by adding each view to the shortest row.
@@ -574,6 +620,7 @@ WidthReader { width in
574620
```
575621

576622
### FULayoutThatFits
623+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
577624
The [`FULayout`](#fulayout) equivalent of [`LayoutThatFits`](#layoutthatfits).
578625

579626
An `FULayout` that picks the first provided layout that will fit the content in the provided maxWidth, maxHeight, or both. This is most helpful when switching between `HStackFULayout` and `VStackFULayout` as the content only needs to be provided once and will even animate when the stack changes.
@@ -593,6 +640,7 @@ FULayoutThatFits(
593640
```
594641

595642
### FUViewThatFits
643+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
596644
The [`FULayout`](#fulayout) equivalent of SwiftUI `ViewThatFits`.
597645

598646
An `FULayout` that presents the first view that fits the provided maxWidth, maxHeight, or both depending on which parameters are used.
@@ -618,15 +666,19 @@ WidthReader { width in
618666
Alternative stack layouts that can be wrapped in [`AnyFULayout`](#anyfulayout) and then toggled between with animation. Useful when you want to toggle between VStack and HStack based on available space.
619667

620668
#### HStackFULayout
669+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
621670
Similar to `HStack` but `Spacer()` cannot be used and content will always use a fixed size on the horizontal axis.
622671

623672
#### VStackFULayout
673+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
624674
Similar to `VStack` but `Spacer()` cannot be used and content will always use a fixed size on the vertical axis.
625675

626676
#### ZStackFULayout
677+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
627678
Similar to `ZStack` but content will always use a fixed size on both the vertical and horizontal axes.
628679

629680
### AnyFULayout
681+
*\*Deprecated iOS 16, macOS 13, watchOS 7, tvOS 14, visionOS 1*
630682
The [`FULayout`](#fulayout) equivalent of SwiftUI `AnyLayout`.
631683

632684
A type-erased FrameUp layout can be used to wrap multiple layouts and switch between them with animation.

Sources/FrameUp/AutoRotatingView/AutoRotatingView.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import SwiftUI
1111
/// A view that rotates and resizes the content frame to match device orientation.
1212
public struct AutoRotatingView<Content: View>: View {
1313
/// The current orientation of the content relative to the device.
14-
@State private var contentOrientation: InterfaceOrientation? = nil
14+
@State private var contentOrientation: FUInterfaceOrientation? = nil
1515
/// The current orientation of the device.
16-
@State private var interfaceOrientation: InterfaceOrientation? = nil
16+
@State private var interfaceOrientation: FUInterfaceOrientation? = nil
1717

1818
/// Allowed orientations for the content.
19-
let allowedOrientations: [InterfaceOrientation]
19+
let allowedOrientations: [FUInterfaceOrientation]
2020
/// Toggle to turn this modifier on or off.
2121
let isOn: Bool
2222
/// Animation to use for the orientation change.
@@ -31,15 +31,15 @@ public struct AutoRotatingView<Content: View>: View {
3131
/// - allowedOrientations: Set of allowed orientations for this view. Default is all.
3232
/// - isOn: Toggles ability to rotate views.
3333
/// - animation: Animation to use when altering the view orientation.
34-
/// - Returns: A view rotated to match a device orientations from an allowed orientation set.
35-
public init(_ allowedOrientations: [InterfaceOrientation] = InterfaceOrientation.allCases, isOn: Bool = true, animation: Animation? = .default, @ViewBuilder content: () -> Content) {
34+
/// - content: Content to be rotated to match a device orientations from an allowed orientation set.
35+
public init(_ allowedOrientations: [FUInterfaceOrientation] = FUInterfaceOrientation.allCases, isOn: Bool = true, animation: Animation? = .default, @ViewBuilder content: () -> Content) {
3636
self.allowedOrientations = allowedOrientations
3737
self.isOn = isOn
3838
self.animation = animation
3939
self.content = content()
4040
}
4141

42-
func newInterfaceOrientation(deviceOrientation: InterfaceOrientation?) -> InterfaceOrientation? {
42+
func newInterfaceOrientation(deviceOrientation: FUInterfaceOrientation?) -> FUInterfaceOrientation? {
4343
if let newSupportedOrientation = InfoDictionary.supportedInterfaceOrientations.first(where: { $0 == deviceOrientation }), newSupportedOrientation != interfaceOrientation {
4444
return newSupportedOrientation
4545
} else if interfaceOrientation == nil {
@@ -49,7 +49,7 @@ public struct AutoRotatingView<Content: View>: View {
4949
}
5050
}
5151

52-
func newContentOrientation(deviceOrientation: InterfaceOrientation?, interfaceOrientation: InterfaceOrientation?, allowedOrientations: [InterfaceOrientation]) -> InterfaceOrientation? {
52+
func newContentOrientation(deviceOrientation: FUInterfaceOrientation?, interfaceOrientation: FUInterfaceOrientation?, allowedOrientations: [FUInterfaceOrientation]) -> FUInterfaceOrientation? {
5353
if let newOrientation = deviceOrientation, allowedOrientations.contains(newOrientation), newOrientation != contentOrientation {
5454
return newOrientation
5555
} else if contentOrientation == nil {
@@ -59,8 +59,7 @@ public struct AutoRotatingView<Content: View>: View {
5959
}
6060
}
6161

62-
@MainActor
63-
func changeOrientations(allowedOrientations: [InterfaceOrientation]? = nil) {
62+
func changeOrientations(allowedOrientations: [FUInterfaceOrientation]? = nil) {
6463
if isOn {
6564
let allowedOrientations = allowedOrientations ?? self.allowedOrientations
6665
/// if the new device orientation is a valid interface orientation it will not be nil

Sources/FrameUp/AutoRotatingView/InterfaceOrientation-extension.swift renamed to Sources/FrameUp/AutoRotatingView/FUInterfaceOrientation.swift

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,18 @@
55
// Created by Ryan Lintott on 2022-05-23.
66
//
77

8+
#if os(iOS)
89
import SwiftUI
910

10-
#if os(iOS)
11-
@available(iOS, deprecated: 15.0, message: "This extension is no longer necessary as it's built into the API")
12-
public enum InterfaceOrientation: CaseIterable, Sendable {
11+
public enum FUInterfaceOrientation: CaseIterable, Sendable {
1312
case portrait
1413
case landscapeRight
1514
case landscapeLeft
1615
case portraitUpsideDown
1716
}
1817

19-
//@available(iOS 15.0, *)
20-
internal extension InterfaceOrientation {
21-
nonisolated init?(key: String) {
18+
internal extension FUInterfaceOrientation {
19+
init?(key: String) {
2220
switch key {
2321
case "UIInterfaceOrientationPortrait":
2422
self = .portrait
@@ -71,4 +69,17 @@ internal extension InterfaceOrientation {
7169
}
7270
}
7371
}
72+
73+
@available(iOS 15, * )
74+
internal extension FUInterfaceOrientation {
75+
init?(_ interfaceOrientation: InterfaceOrientation) {
76+
switch interfaceOrientation {
77+
case .landscapeLeft: self = .landscapeLeft
78+
case .landscapeRight: self = .landscapeRight
79+
case .portrait: self = .portrait
80+
case .portraitUpsideDown: self = .portraitUpsideDown
81+
default: return nil
82+
}
83+
}
84+
}
7485
#endif

Sources/FrameUp/AutoRotatingView/InfoDictionary.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
// Created by Ryan Lintott on 2021-05-11.
66
//
77

8+
#if os(iOS)
89
import SwiftUI
910

10-
#if os(iOS)
11-
struct InfoDictionary {
12-
static let supportedInterfaceOrientations: [InterfaceOrientation] = {
11+
enum InfoDictionary {
12+
static let supportedInterfaceOrientations: [FUInterfaceOrientation] = {
1313
if let orientations = Bundle.main.infoDictionary?["UISupportedInterfaceOrientations"] as? [String] {
14-
return orientations.compactMap { InterfaceOrientation(key: $0) }
14+
return orientations.compactMap { FUInterfaceOrientation(key: $0) }
1515
} else {
1616
return []
1717
}

Sources/FrameUp/AutoRotatingView/RotationMatchingOrientationViewModifier.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
// Created by Ryan Lintott on 2020-12-31.
66
//
77

8+
#if os(iOS)
89
import SwiftUI
910

10-
#if os(iOS)
1111
extension View {
1212
/// Rotates a view and alters it's frame to match device orientations from an allowed orientation set.
1313
///
@@ -17,9 +17,9 @@ extension View {
1717
/// - isOn: Toggle to turn this modifier on or off.
1818
/// - animation: Animation to use when altering the view orientation.
1919
/// - Returns: A view rotated to match a device orientations from an allowed orientation set.
20-
@available(*, deprecated, message: "Use AutoRotatingView view instead of this modifier.")
21-
public func rotationMatchingOrientation(_ allowedOrientations: [InterfaceOrientation]? = nil, isOn: Bool = true, withAnimation animation: Animation? = nil) -> some View {
22-
AutoRotatingView(allowedOrientations ?? InterfaceOrientation.allCases, isOn: isOn, animation: animation) {
20+
@available(*, deprecated, renamed: "AutoRotatingView", message: "Use AutoRotatingView view instead of this modifier.")
21+
public func rotationMatchingOrientation(_ allowedOrientations: [FUInterfaceOrientation]? = nil, isOn: Bool = true, withAnimation animation: Animation? = nil) -> some View {
22+
AutoRotatingView(allowedOrientations ?? FUInterfaceOrientation.allCases, isOn: isOn, animation: animation) {
2323
self
2424
}
2525
}

Sources/FrameUp/AutoRotatingView/UIDeviceOrientation-extension.swift

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,17 @@
55
// Created by Ryan Lintott on 2021-05-14.
66
//
77

8+
#if os(iOS)
89
import SwiftUI
910

10-
#if os(iOS)
1111
internal extension UIDeviceOrientation {
12-
var interfaceOrientation: InterfaceOrientation? {
12+
var interfaceOrientation: FUInterfaceOrientation? {
1313
switch self {
14-
case .portrait:
15-
return .portrait
16-
case .portraitUpsideDown:
17-
return .portraitUpsideDown
18-
case .landscapeLeft:
19-
return .landscapeLeft
20-
case .landscapeRight:
21-
return .landscapeRight
22-
default:
23-
return nil
14+
case .portrait: .portrait
15+
case .portraitUpsideDown: .portraitUpsideDown
16+
case .landscapeLeft: .landscapeLeft
17+
case .landscapeRight: .landscapeRight
18+
default: nil
2419
}
2520
}
2621
}

Sources/FrameUp/FULayout/AnyFULayout.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
import SwiftUI
99

1010
/**
11-
A type-erased instance of the FrameUp layout protocol.
11+
A type-erased instance of ``FULayout``.
1212

13-
If you want to make a view that can toggle between layouts, wrap each one in AnyLayout.
13+
If you want to make a view that can toggle between layouts, wrap each one in `AnyFULayout`.
1414
*/
1515
public struct AnyFULayout: FULayout {
1616
/// The name of the wrapped layout (just used as a label)
@@ -52,6 +52,3 @@ public struct AnyFULayout: FULayout {
5252
hasher.combine(String(describing: Self.self))
5353
}
5454
}
55-
56-
//@available(iOS 16, macOS 13, *)
57-
//extension AnyFULayout: Layout { }

0 commit comments

Comments
 (0)