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
2 changes: 1 addition & 1 deletion .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
with:
lfs: true
- name: Test BSWInterfaceKit macOS
run: set -o pipefail && swift build | xcbeautify --renderer github-actions
run: set -o pipefail && SKIP_ZERO=1 swift build | xcbeautify --renderer github-actions

android-build:
runs-on: mobile
Expand Down
42 changes: 21 additions & 21 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ let applePlatforms = TargetDependencyCondition.when(
)

var packageDependencies: [Package.Dependency] = [
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.18.3"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.18.7"),
.package(url: "https://github.com/theleftbit/BSWFoundation.git", from: "7.2.4"),
.package(url: "https://github.com/kean/Nuke.git", from: "12.8.0"),
]

if !zero {
packageDependencies.append(contentsOf: [
.package(url: "https://source.skip.tools/skip.git", from: "1.6.21"),
.package(url: "https://source.skip.tools/skip-fuse-ui.git", from: "1.9.1"),
.package(url: "https://source.skip.tools/skip.git", from: "1.6.27"),
.package(url: "https://source.skip.tools/skip-fuse-ui.git", from: "1.10.0"),
])
}

Expand Down Expand Up @@ -54,8 +54,8 @@ if !zero {
let package = Package(
name: "BSWInterfaceKit",
platforms: [
.iOS(.v16),
.macOS(.v13),
.iOS(.v17),
.macOS(.v14),
.watchOS(.v11)
],
products: [
Expand Down
119 changes: 9 additions & 110 deletions Sources/BSWInterfaceKit/SwiftUI/Extensions/SwiftUI+AlwaysPopover.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#if canImport(UIKit.UIView)

#if canImport(Darwin)
import SwiftUI

#Preview {
Expand All @@ -12,7 +11,7 @@ import SwiftUI
])
}

private struct ContentView: View {
struct ContentView: View {

struct Item: Identifiable {
let id: Int
Expand All @@ -31,116 +30,16 @@ private struct ContentView: View {
presentingDetailsOfItem = item
} label: {
Image(systemName: item.systemImageName)
.alwaysPopover(id: item.id, isPresented: $presentingDetailsOfItem) {
Text(item.description)
.lineLimit(nil)
.multilineTextAlignment(.center)
.padding()
}
}

.popover(item: $presentingDetailsOfItem) { item in
Text(item.description)
.lineLimit(nil)
.multilineTextAlignment(.center)
.padding()
.presentationCompactAdaptation(.popover)
}
}
}
}
}

public extension View {

@available(iOS, deprecated: 16.4, message: "Use presentationCompactAdaptation(horizontal:vertical:) instead.")
func alwaysPopover<Content: View, Value: Identifiable>(id: Value.ID, isPresented: Binding<Value?>, @ViewBuilder content: @escaping () -> Content) -> some View {
self.modifier(AlwaysPopoverModifier(id: id, isPresented: isPresented, contentBlock: content))
}
}

private struct AlwaysPopoverModifier<PopoverContent: View, Value: Identifiable>: ViewModifier {

let id: Value.ID
let isPresented: Binding<Value?>
let contentBlock: () -> PopoverContent
@State private var anchorView = UIView()

func body(content: Content) -> some View {
if let value = isPresented.wrappedValue, value.id == id {
presentPopover(value: value)
}

return content
.background(InternalAnchorView(uiView: anchorView))
}

func presentPopover(value: Value) {
let contentController = ContentViewController(
// Ensures the text wraps to fit the content
rootView: contentBlock()
.fixedSize(horizontal: false, vertical: true),
isPresented: isPresented
)
contentController.modalPresentationStyle = .popover

let view = anchorView
guard let popover = contentController.popoverPresentationController else { return }
popover.sourceView = view
popover.sourceRect = view.bounds
popover.delegate = contentController

guard let sourceVC = view.next() as UIViewController? else { return }
sourceVC.present(contentController, animated: true)
}

struct InternalAnchorView: UIViewRepresentable {
let uiView: UIView

func makeUIView(context: Self.Context) -> UIView {
uiView
}

func updateUIView(_ uiView: UIView, context: Self.Context) { }
}

class ContentViewController<FinalView: View>: UIHostingController<FinalView>, UIPopoverPresentationControllerDelegate {

var isPresented: Binding<Value?>

init(rootView: FinalView, isPresented: Binding<Value?>) {
self.isPresented = isPresented
super.init(rootView: rootView)
}

@preconcurrency required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()
updatePreferredContentSize()
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updatePreferredContentSize()
}

private let MaxWidth: CGFloat = 320

private func updatePreferredContentSize() {
let size = view.systemLayoutSizeFitting(
CGSize(width: MaxWidth, height: UIView.layoutFittingCompressedSize.height),
withHorizontalFittingPriority: .required,
verticalFittingPriority: .fittingSizeLevel
)
preferredContentSize = size
}

// MARK: UIPopoverPresentationControllerDelegate

func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .none
}

func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
self.isPresented.wrappedValue = nil
}
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import SwiftUI
#if canImport(Darwin)
/// Example of how to reload `AsyncView`
/// when the ID of the operation changes.
@available(iOS 17, macOS 14, watchOS 9, *)
#Preview {

@Previewable
Expand Down
22 changes: 17 additions & 5 deletions Sources/BSWInterfaceKit/SwiftUI/ViewModifiers/BlockingTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#if canImport(Darwin)
import SwiftUI

@available(iOS 17.0, macOS 14.0, *)
#Preview {
@State
@Previewable
Expand All @@ -20,7 +19,9 @@ import SwiftUI
readyToPerform: $perform,
confirmationStrategy: .confirmWith(title: "Are you sure?", message: "This will block the main thread for 2 seconds.", confirmButtonTitle: "Yes", cancelButtonTitle: "No"),
task: {
try await Task.sleep(for: .seconds(2))
struct SomeError: Swift.Error {}
try await Task.sleep(for: .seconds(1))
throw SomeError()
}
)
}
Expand Down Expand Up @@ -155,11 +156,20 @@ struct PerformEquatableBlockingView<T: Equatable, V: View>: View {
try await task(value)
self.hudState = .success(successMessage)
try? await Task.sleep(for: .seconds(successDisplaySeconds))
self.hudState = .none
} catch {
#if canImport(Darwin)
withAnimation {
self.hudState = .none
} completion: {
taskError = error
}
#else
self.hudState = .none
try? await Task.sleep(for: .milliseconds(300))
taskError = error
#endif
}

self.hudState = .none
}
.errorAlert(error: $taskError)
.hud(hudState: $hudState)
Expand All @@ -171,7 +181,9 @@ struct PerformEquatableBlockingView<T: Equatable, V: View>: View {
handleConfirmation(false)
} label: { Text(confirmationCancelButtonTitle) }
} message: {
if let confirmationMessage { Text(confirmationMessage) }
if let confirmationMessage {
Text(confirmationMessage)
}
}
}

Expand Down
Loading
Loading