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
74 changes: 37 additions & 37 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
name: ci

on:
on:
push:
branches:
branches:
- master
pull_request:
branches:
- '*'
workflow_dispatch:
branches:
- "*"
workflow_dispatch:

jobs:
build:
Expand Down Expand Up @@ -40,33 +40,33 @@ jobs:
- os: "26.0.1"
xcode: "26.0.1"
sim: "iPhone 16 Pro"
parallel: NO # Stop random test job failures
parallel: NO # Stop random test job failures
runs-on: macos-15
- os: "18.5"
xcode: "16.4"
sim: "iPhone 16 Pro"
parallel: NO # Stop random test job failures
parallel: NO # Stop random test job failures
runs-on: macos-15
- os: "17.5"
xcode: "15.4"
sim: "iPhone 15 Pro"
parallel: NO # Stop random test job failures
parallel: NO # Stop random test job failures
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Testing in iOS ${{ matrix.os }}
run: |
xcodebuild clean test \
-workspace FloatingPanel.xcworkspace \
-scheme FloatingPanel \
-destination 'platform=iOS Simulator,OS=${{ matrix.os }},name=${{ matrix.sim }}' \
-parallel-testing-enabled '${{ matrix.parallel }}'
- uses: actions/checkout@v4
- name: Testing in iOS ${{ matrix.os }}
run: |
xcodebuild clean test \
-workspace FloatingPanel.xcworkspace \
-scheme FloatingPanel \
-destination 'platform=iOS Simulator,OS=${{ matrix.os }},name=${{ matrix.sim }}' \
-parallel-testing-enabled '${{ matrix.parallel }}'
timeout-minutes: 20

example:
runs-on: macos-15
env:
DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer
DEVELOPER_DIR: /Applications/Xcode_26.0.1.app/Contents/Developer
strategy:
fail-fast: false
matrix:
Expand All @@ -78,13 +78,13 @@ jobs:
- example: "SamplesObjC"
- example: "SamplesSwiftUI"
steps:
- uses: actions/checkout@v4
- name: Building ${{ matrix.example }}
run: |
xcodebuild clean build \
-workspace FloatingPanel.xcworkspace \
-scheme ${{ matrix.example }} \
-sdk iphonesimulator
- uses: actions/checkout@v4
- name: Building ${{ matrix.example }}
run: |
xcodebuild clean build \
-workspace FloatingPanel.xcworkspace \
-scheme ${{ matrix.example }} \
-sdk iphonesimulator

swiftpm:
runs-on: ${{ matrix.runs-on }}
Expand All @@ -93,7 +93,7 @@ jobs:
strategy:
fail-fast: false
matrix:
xcode: ["16.4", "15.4"]
xcode: ["26.0.1", "16.4", "15.4"]
platform: [iphoneos, iphonesimulator]
arch: [x86_64, arm64]
exclude:
Expand All @@ -119,20 +119,20 @@ jobs:
sys: "ios17.2-simulator"
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: "Swift Package Manager build"
run: |
xcrun swift build \
--sdk "$(xcrun --sdk ${{ matrix.platform }} --show-sdk-path)" \
-Xswiftc "-target" -Xswiftc "${{ matrix.arch }}-apple-${{ matrix.sys }}"
- uses: actions/checkout@v4
- name: "Swift Package Manager build"
run: |
xcrun swift build \
--sdk "$(xcrun --sdk ${{ matrix.platform }} --show-sdk-path)" \
-Xswiftc "-target" -Xswiftc "${{ matrix.arch }}-apple-${{ matrix.sys }}"

cocoapods:
runs-on: macos-15
env:
DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer
DEVELOPER_DIR: /Applications/Xcode_26.0.1.app/Contents/Developer
steps:
- uses: actions/checkout@v4
- name: "CocoaPods: pod lib lint"
run: pod lib lint --allow-warnings --verbose
- name: "CocoaPods: pod spec lint"
run: pod spec lint --allow-warnings --verbose
- uses: actions/checkout@v4
- name: "CocoaPods: pod lib lint"
run: pod lib lint --allow-warnings --verbose
- name: "CocoaPods: pod spec lint"
run: pod spec lint --allow-warnings --verbose
55 changes: 43 additions & 12 deletions Examples/SamplesSwiftUI/SamplesSwiftUI/UseCases/MainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,28 @@ import UIKit
import os.log

struct MainView: View {
enum CardContent: String, CaseIterable, Identifiable {
case list
case detail

var id: String { rawValue }
}
@State private var panelLayout: FloatingPanelLayout? = MyFloatingPanelLayout()
@State private var panelState: FloatingPanelState?
@State private var selectedContent: CardContent = .list

var body: some View {
ZStack {
Color.orange
.ignoresSafeArea()
.floatingPanel(
coordinator: MyPanelCoordinator.self
) { proxy in
ContentView(proxy: proxy)
}
.floatingPanelSurfaceAppearance(.transparent())
.floatingPanelLayout(panelLayout)
.floatingPanelState($panelState)
.onChange(of: panelState) { newValue in
Logger().debug("Panel state changed: \(newValue ?? .hidden)")
}

VStack(spacing: 32) {
Picker("type", selection: $selectedContent) {
ForEach(CardContent.allCases) {
type in
Text(type.rawValue).tag(type)
}
}
.pickerStyle(.segmented)
Button("Move to full") {
withAnimation(.interactiveSpring) {
panelState = .full
Expand All @@ -46,8 +48,37 @@ struct MainView: View {
Text("Switch to My layout")
}
}
Spacer()
}
}
.floatingPanel(
coordinator: MyPanelCoordinator.self
) { proxy in
switch selectedContent {
case .list:
ContentView(proxy: proxy)
case .detail:
HStack {
Spacer()
VStack {
Text("Detail content")
.padding(.top, 32)
Spacer()
}
Spacer()
}
.padding()
.background {
BackgroundView()
}
}
}
.floatingPanelSurfaceAppearance(.transparent())
.floatingPanelLayout(panelLayout)
.floatingPanelState($panelState)
.onChange(of: panelState) { newValue in
Logger().debug("Panel state changed: \(newValue ?? .hidden)")
}
}
}

Expand Down
23 changes: 23 additions & 0 deletions Examples/SamplesSwiftUI/SamplesSwiftUI/Views/BackgroundView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import SwiftUI

struct BackgroundView: View {
var body: some View {
GeometryReader { geometry in
Rectangle()
.fill(.clear)
.frame(height: geometry.size.height * 2)
.backgroundEffect()
}
}
}

extension View {
@ViewBuilder
fileprivate func backgroundEffect() -> some View {
if #available(iOS 26, *) {
self.glassEffect(.regular, in: .rect)
} else {
self.background(.regularMaterial)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct ContentView: View {
.frame(maxWidth: .infinity, alignment: .leading)
.frame(height: 60)
.background(.clear)
.padding(.horizontal)
}
}
}
Expand All @@ -28,6 +29,7 @@ struct ContentView: View {
.frame(maxWidth: .infinity, alignment: .leading)
.frame(height: 60)
.background(.clear)
.padding(.horizontal)
}
}
}
Expand All @@ -38,12 +40,7 @@ struct ContentView: View {
}
// Prevent revealing underlying content at the bottom of the panel when the panel is moving beyond its fully‑expanded position.
.background {
GeometryReader { geometry in
Rectangle()
.fill(.clear)
.frame(height: geometry.size.height * 2)
.background(.regularMaterial)
}
BackgroundView()
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions Sources/SwiftUI/FloatingPanelView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,11 @@ struct FloatingPanelView<MainView: View, ContentView: View>: UIViewControllerRep
_ uiViewController: UIHostingController<MainView>,
context: Context
) {
uiViewController.rootView = main

context.coordinator.updateContent(content(context.coordinator.proxy))
context.coordinator.onUpdate(context: context)

applyEnvironment(context: context)
applyAnimatableEnvironment(context: context)
}
Expand Down Expand Up @@ -160,6 +164,9 @@ class FloatingPanelCoordinatorProxy {

private var subscriptions: Set<AnyCancellable> = Set()

// Store a reference to the content hosting controller for dynamic updates
private weak var contentHostingController: UIViewController?

var proxy: FloatingPanelProxy { origin.proxy }
var controller: FloatingPanelController { origin.controller }

Expand All @@ -181,12 +188,25 @@ class FloatingPanelCoordinatorProxy {
mainHostingController: UIHostingController<Main>,
contentHostingController: UIHostingController<Content>
) {
// Store the content hosting controller reference
self.contentHostingController = contentHostingController

origin.setupFloatingPanel(
mainHostingController: mainHostingController,
contentHostingController: contentHostingController
)
}

/// Updates the content of the floating panel with new content.
func updateContent<Content: View>(_ newContent: Content) {
guard
let hostingController = contentHostingController as? UIHostingController<Content>
else {
return
}
hostingController.rootView = newContent
}

func onUpdate<Representable>(
context: UIViewControllerRepresentableContext<Representable>
) where Representable: UIViewControllerRepresentable {
Expand Down