Skip to content

Commit a9813c1

Browse files
authored
[FloatingDisplay] Content design with SwiftUI (#180)
1 parent 7cc7815 commit a9813c1

File tree

12 files changed

+150
-88
lines changed

12 files changed

+150
-88
lines changed

Development/FluidInterfaceKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Development/Sources/FluidInterfaceKit-Demo/DemoFloatingDisplayKit/FloatingDisplayKitContentView.swift

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
// Created by Muukii on 2021/04/24.
66
//
77

8-
import FluidStack
98
import FluidSnackbar
9+
import FluidStack
1010
import SwiftUI
1111

12+
@MainActor
1213
let snackbarController = FloatingDisplayController(
1314
edgeTargetSafeArea: .init(
1415
top: .notificationWindow,
@@ -20,14 +21,48 @@ let snackbarController = FloatingDisplayController(
2021

2122
struct FloatingDisplayKitContentView: View {
2223
var body: some View {
23-
VStack {
24+
Form {
25+
26+
Button("Display Bar + SwiftUI content") {
27+
28+
snackbarController.display(
29+
context: .init(
30+
position: .top,
31+
transition: .slideIn,
32+
content: {
33+
ZStack {
34+
HStack {
35+
Image(systemName: "star")
36+
.resizable()
37+
.frame(width: 24, height: 24)
38+
39+
Button("Dismiss") {
40+
41+
}
42+
43+
}
44+
}
45+
.background(Color.red)
46+
}
47+
),
48+
waitsInQueue: false
49+
)
50+
51+
}
52+
2453
Button("Display Bar") {
25-
snackbarController.deliver(
26-
notification: .init {
27-
DemoSnackbarView(text: "Hello")
28-
},
29-
animator: FloatingDisplaySlideInTrantision()
54+
55+
snackbarController.display(
56+
context: .init(
57+
viewBuilder: {
58+
DemoSnackbarView(text: "Hello")
59+
},
60+
position: .top,
61+
transition: .slideIn
62+
),
63+
waitsInQueue: false
3064
)
65+
3166
}
3267

3368
Button("Display Popup") {

Development/Sources/FluidInterfaceKit-Demo/DemoRideauIntegrationViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,14 @@ final class DemoRideauIntegrationViewController: FluidStackController {
141141

142142
import SwiftUI
143143
import SwiftUISupport
144+
import SwiftUIHosting
144145

145146
private final class SwiftUIContentViewController: UIViewController {
146147

147148
override func viewDidLoad() {
148149
super.viewDidLoad()
149150

150-
let hostingView = HostingView { _ in
151+
let hostingView = SwiftUIHostingView {
151152
ZStack {
152153
Color.red
153154
VStack(spacing: 0) {

Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,19 @@ let package = Package(
1818
],
1919
dependencies: [
2020
.package(
21-
name: "GeometryKit",
2221
url: "https://github.com/FluidGroup/GeometryKit",
2322
.upToNextMajor(from: "1.1.0")
2423
),
2524
.package(
26-
name: "ResultBuilderKit",
2725
url: "https://github.com/FluidGroup/ResultBuilderKit.git",
2826
.upToNextMajor(from: "1.2.0")
2927
),
3028
.package(
31-
name: "Rideau",
3229
url: "https://github.com/FluidGroup/Rideau.git",
3330
.upToNextMajor(from: "2.1.0")
3431
),
32+
.package(url: "https://github.com/FluidGroup/swiftui-Hosting", from: "1.2.0"),
33+
.package(url: "https://github.com/FluidGroup/swift-rubber-banding", from: "1.0.0"),
3534
],
3635
targets: [
3736
.target(
@@ -44,7 +43,12 @@ let package = Package(
4443
.target(
4544
name: "FluidRuntime"
4645
),
47-
.target(name: "FluidGesture"),
46+
.target(
47+
name: "FluidGesture",
48+
dependencies: [
49+
.product(name: "RubberBanding", package: "swift-rubber-banding")
50+
]
51+
),
4852
.target(
4953
name: "FluidTooltipSupport",
5054
dependencies: ["FluidPortal"]
@@ -55,7 +59,11 @@ let package = Package(
5559
),
5660
.target(
5761
name: "FluidSnackbar",
58-
dependencies: ["FluidCore"]
62+
dependencies: [
63+
"FluidCore",
64+
.product(name: "SwiftUIHosting", package: "swiftui-Hosting"),
65+
.product(name: "RubberBanding", package: "swift-rubber-banding"),
66+
]
5967
),
6068
.target(
6169
name: "FluidPictureInPicture",
@@ -67,6 +75,6 @@ let package = Package(
6775
),
6876
.target(name: "FluidKeyboardSupport"),
6977

70-
.testTarget(name: "FluidStackTests", dependencies: ["FluidStack"])
78+
.testTarget(name: "FluidStackTests", dependencies: ["FluidStack"]),
7179
]
7280
)

Sources/FluidGesture/Drag.swift

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import UIKit
2+
import RubberBanding
23

34
extension UIView {
45

@@ -307,34 +308,6 @@ public struct DragDescriptor {
307308

308309
}
309310

310-
private func rubberBand(value: CGFloat, min: CGFloat, max: CGFloat, bandLength: CGFloat) -> CGFloat
311-
{
312-
if value >= min && value <= max {
313-
// While we're within range we don't rubber band the value.
314-
return value
315-
}
316-
317-
if bandLength <= 0 {
318-
// The rubber band doesn't exist, return the minimum value so that we stay put.
319-
return min
320-
}
321-
322-
let rubberBandCoefficient: CGFloat = 0.55
323-
// Accepts values from [0...+inf and ensures that f(x) < bandLength for all values.
324-
let band: (CGFloat) -> CGFloat = { value in
325-
let demoninator = value * rubberBandCoefficient / bandLength + 1
326-
return bandLength * (1 - 1 / demoninator)
327-
}
328-
if value > max {
329-
return band(value - max) + max
330-
331-
} else if value < min {
332-
return min - band(min - value)
333-
}
334-
335-
return value
336-
}
337-
338311
private var ref: Void?
339312

340313
extension UIPanGestureRecognizer {
Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
1+
import SwiftUI
2+
import SwiftUIHosting
23
import UIKit
34

4-
/**
5-
A context object that provides a concrete view to display
6-
*/
5+
/// A context object that provides a concrete view to display
76
open class FloatingDisplayContext: Hashable {
87

9-
public static func ==(lhs: FloatingDisplayContext, rhs: FloatingDisplayContext) -> Bool {
8+
public static func == (lhs: FloatingDisplayContext, rhs: FloatingDisplayContext) -> Bool {
109
lhs === rhs
1110
}
1211

@@ -16,24 +15,9 @@ open class FloatingDisplayContext: Hashable {
1615

1716
private let factory: () -> FloatingDisplayViewType
1817

19-
public var transition: FloatingDisplayTransitionType
20-
21-
public var position: FloatingDisplayController.DisplayPosition
22-
23-
/// A view that currently displaying.
24-
public private(set) weak var view: FloatingDisplayViewType?
18+
public let transition: FloatingDisplayTransitionType
2519

26-
/// A Boolean value that indicates deliverly was cancelled.
27-
open var wasCancelled: Bool = false
28-
29-
@available(*, deprecated, message: "Use the new initializer")
30-
public init(
31-
factory: @escaping () -> FloatingDisplayViewType
32-
) {
33-
self.factory = factory
34-
self.transition = FloatingDisplaySlideInTrantision()
35-
self.position = .top
36-
}
20+
public let position: FloatingDisplayController.DisplayPosition
3721

3822
public init(
3923
viewBuilder: @escaping () -> FloatingDisplayViewType,
@@ -48,8 +32,43 @@ open class FloatingDisplayContext: Hashable {
4832
func makeView() -> FloatingDisplayViewType {
4933
factory()
5034
}
35+
}
36+
37+
extension FloatingDisplayContext {
38+
39+
public convenience init<Content: View>(
40+
position: FloatingDisplayController.DisplayPosition,
41+
transition: FloatingDisplayTransitionType,
42+
@ViewBuilder content: @escaping () -> Content
43+
) {
44+
45+
self.init(
46+
viewBuilder: {
47+
_HostingWrapperView(hostingView: SwiftUIHostingView(content: content))
48+
},
49+
position: position,
50+
transition: transition
51+
)
52+
53+
}
5154

52-
func setView(_ view: FloatingDisplayViewType) {
53-
self.view = view
55+
}
56+
57+
private final class _HostingWrapperView: SnackbarDraggableBase {
58+
59+
private let hostingView: SwiftUIHostingView
60+
61+
init(hostingView: SwiftUIHostingView) {
62+
self.hostingView = hostingView
63+
super.init(topMargin: .zero)
64+
contentView.addSubview(hostingView)
65+
hostingView.translatesAutoresizingMaskIntoConstraints = false
66+
NSLayoutConstraint.activate([
67+
hostingView.topAnchor.constraint(equalTo: contentView.topAnchor),
68+
hostingView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
69+
hostingView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
70+
hostingView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
71+
])
5472
}
73+
5574
}

Sources/FluidSnackbar/FloatingDisplayController.swift

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import UIKit
22

3+
@MainActor
34
open class FloatingDisplayController {
45

56
private struct State: Equatable {
@@ -95,17 +96,6 @@ open class FloatingDisplayController {
9596

9697
}
9798

98-
@available(*, deprecated)
99-
open func deliver(
100-
notification context: FloatingDisplayContext,
101-
animator: FloatingDisplayTransitionType
102-
) {
103-
assert(Thread.isMainThread)
104-
105-
context.transition = animator
106-
enqueue(context: context)
107-
}
108-
10999
private func _display(
110100
context: FloatingDisplayContext,
111101
onDidDismiss: @escaping (FloatingDisplayViewType) -> Void
@@ -181,7 +171,6 @@ open class FloatingDisplayController {
181171
}
182172

183173
let notificationView = context.makeView()
184-
context.setView(notificationView)
185174

186175
let position = context.position
187176
let transition = context.transition

Sources/FluidSnackbar/Library/FloatingDisplayPopupTransition.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,11 @@ public struct FloatingDisplayPopupTransition: FloatingDisplayTransitionType {
3838
animator.startAnimation()
3939
}
4040
}
41+
42+
extension FloatingDisplayTransitionType where Self == FloatingDisplayPopupTransition {
43+
44+
public static var popup: Self {
45+
.init()
46+
}
47+
48+
}

Sources/FluidSnackbar/Library/FloatingDisplaySlideInTrantision.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,19 @@ public struct FloatingDisplayFloatUpTrantision: FloatingDisplayTransitionType {
103103

104104
}
105105
}
106+
107+
extension FloatingDisplayTransitionType where Self == FloatingDisplaySlideInTrantision {
108+
109+
public static var slideIn: Self {
110+
FloatingDisplaySlideInTrantision()
111+
}
112+
113+
}
114+
115+
extension FloatingDisplayTransitionType where Self == FloatingDisplayFloatUpTrantision {
116+
117+
public static var floatUp: Self {
118+
FloatingDisplayFloatUpTrantision()
119+
}
120+
121+
}

0 commit comments

Comments
 (0)