diff --git a/Development/Development/BookScrollView.swift b/Development/Development/BookScrollView.swift new file mode 100644 index 0000000..8e94e2d --- /dev/null +++ b/Development/Development/BookScrollView.swift @@ -0,0 +1,113 @@ +// +// BookScrollView.swift +// Development +// +// Created by Muukii on 2024/07/25. +// + +import SwiftUI +import SwiftUIScrollViewInteroperableDragGesture +import SwiftUISnapDraggingModifier + +#if DEBUG + +@available(iOS 18, *) +private var scrollView: some View { + ScrollView([.horizontal, .vertical]) { + Grid(horizontalSpacing: 20, verticalSpacing: 20) { + ForEach(0..<4) { _ in + GridRow { + ForEach(0..<4) { _ in Color.teal.frame(width: 30, height: 30) } + } + } + } + .padding() + .background(Color.red) + .padding() + .background(Color.blue) + } +} + +@available(iOS 18, *) +#Preview("Normal") { + + @Previewable @State var offset: CGSize = .zero + + ZStack { + + VStack { + scrollView + } + .frame(width: 200, height: 200) + .background(Color.green.secondary) + .padding() + .background(Color.green.tertiary) + .modifier( + SnapDraggingModifier( + gestureMode: .scrollViewInteroperable( + .init(ignoresScrollView: false, sticksToEdges: false) + ), + offset: $offset + ) + ) + .background(Color.purple.tertiary) + + } +} + +@available(iOS 18, *) +#Preview("SticksToEdges") { + + @Previewable @State var offset: CGSize = .zero + + ZStack { + + VStack { + scrollView + } + .frame(width: 200, height: 200) + .background(Color.green.secondary) + .padding() + .background(Color.green.tertiary) + .modifier( + SnapDraggingModifier( + gestureMode: .scrollViewInteroperable( + .init(ignoresScrollView: false, sticksToEdges: true) + ), + offset: $offset + ) + ) + .background(Color.purple.tertiary) + + } +} + +@available(iOS 18, *) +#Preview("IgnoreScrollView") { + + @Previewable @State var offset: CGSize = .zero + + ZStack { + + VStack { + scrollView + } + .frame(width: 200, height: 200) + .background(Color.green.secondary) + .padding() + .background(Color.green.tertiary) + .modifier( + SnapDraggingModifier( + gestureMode: .scrollViewInteroperable( + .init(ignoresScrollView: true, sticksToEdges: true) + ), + offset: $offset + ) + ) + .background(Color.purple.tertiary) + + } +} + + +#endif diff --git a/Sources/SwiftUISnapDraggingModifier/File.swift b/Sources/SwiftUISnapDraggingModifier/File.swift index 076806c..20419f4 100644 --- a/Sources/SwiftUISnapDraggingModifier/File.swift +++ b/Sources/SwiftUISnapDraggingModifier/File.swift @@ -1,7 +1,7 @@ import SwiftUI import SwiftUISupportSizing -public struct SheetModifier: ViewModifier { +public struct BlanketModifier: ViewModifier { private let displayContent: () -> DisplayContent @Binding var isPresented: Bool @@ -10,156 +10,218 @@ public struct SheetModifier: ViewModifier { @State var contentSize: CGSize = .zero @State var safeAreaInsets: EdgeInsets = .init() + private let onDismiss: (() -> Void)? + private var hidingOffset: CGFloat { (contentSize.height + safeAreaInsets.bottom) } - + private var animation: SnapDraggingModifier.SpringParameter { -// .interpolation(mass: 1, stiffness: 1, damping: 1) + // .interpolation(mass: 1, stiffness: 1, damping: 1) return .hard } public init( isPresented: Binding, + onDismiss: (() -> Void)?, @ViewBuilder displayContent: @escaping () -> DisplayContent ) { self._isPresented = isPresented - self.displayContent = displayContent + self.onDismiss = onDismiss + self.displayContent = displayContent } public func body(content: Content) -> some View { ZStack { content - + VStack { Spacer(minLength: 0) - displayContent() - .readingGeometry( - transform: \.size, - target: $contentSize - ) - .fixedSize(horizontal: false, vertical: true) - .modifier( - SnapDraggingModifier( - gestureMode: { - if #available(iOS 18.0, *) { - return .scrollViewInteroperable(.init(ignoresScrollView: false, sticksToEdges: true)) + + ZStack { + displayContent() + .onAppear(perform: { + print("appear") + }) + } + .readingGeometry( + transform: \.size, + target: $contentSize + ) + .fixedSize(horizontal: false, vertical: true) + .modifier( + SnapDraggingModifier( + gestureMode: { + if #available(iOS 18.0, *) { + return .scrollViewInteroperable( + .init(ignoresScrollView: false, sticksToEdges: true) + ) + } else { + return .normal + } + }(), + offset: $contentOffset, + axis: .vertical, + verticalBoundary: .init(min: 0, max: .infinity, bandLength: 50), + springParameter: animation, + handler: .init( + onEndDragging: { velocity, offset, contentSize in + + print(velocity, offset) + if velocity.dy > 50 || offset.height > (contentSize.width / 2) { + isPresented = false + return .init(width: 0, height: hidingOffset) } else { - return .normal + return .zero } - }(), - offset: $contentOffset, - axis: .vertical, - verticalBoundary: .init(min: 0, max: .infinity, bandLength: 50), - springParameter: animation, - handler: .init( - onEndDragging: { velocity, offset, contentSize in - - print(velocity, offset) - if velocity.dy > 50 || offset.height > (contentSize.width / 2) { - isPresented = false - return .init(width: 0, height: hidingOffset) - } else { - return .zero - } - }) - ) + }) ) - .onChange(of: isPresented) { isPresented in - if isPresented { - withAnimation(.spring(response: 0.45)) { - contentOffset.height = 0 - } - } else { - withAnimation(.spring(response: 0.45)) { - contentOffset.height = hidingOffset - } + ) + .onChange(of: isPresented) { isPresented in + if isPresented { + withAnimation(.spring(response: 0.45)) { + contentOffset.height = 0 + } + } else { + withAnimation(.spring(response: 0.45)) { + contentOffset.height = hidingOffset } } - .onChange(of: contentOffset) { contentOffset in - + } + .onChange(of: hidingOffset) { hidingOffset in + if isPresented == false { + self.contentOffset.height = hidingOffset } + } } .readingGeometry( transform: \.safeAreaInsets, target: $safeAreaInsets - ) + ) } } } -#Preview { +extension View { - struct SheetContent: View { + public func blanket( + item: Binding, + onDismiss: (() -> Void)? = nil, + @ViewBuilder content: @escaping (Item) -> Content + ) -> some View where Item: Identifiable, Content: View { - @State var isExpanded = false + self.modifier( + BlanketModifier( + isPresented: .init( + get: { item.wrappedValue != nil }, + set: { if !$0 { item.wrappedValue = nil } } + ), + onDismiss: onDismiss, + displayContent: { + if let item = item.wrappedValue { + content(item) + } + } + ) + ) - var body: some View { - ZStack { - RoundedRectangle(cornerRadius: 20) - .fill(.background) - HStack { - VStack(alignment: .leading) { - - Text("This is a sheet") - .font(.title) + } + + public func blanket( + isPresented: Binding, + onDismiss: (() -> Void)? = nil, + @ViewBuilder content: @escaping () -> Content + ) -> some View where Content: View { + + self.modifier( + BlanketModifier(isPresented: isPresented, onDismiss: onDismiss, displayContent: content) + ) + + } + +} + +#if DEBUG + +struct SheetContent: View { + + @State var isExpanded = false + + private let title: String + + init(title: String) { + self.title = title + } + + var body: some View { + ZStack { + RoundedRectangle(cornerRadius: 20) + .fill(.background) + HStack { + VStack(alignment: .leading) { + + Text(title) + .font(.title) + + HStack { + VStack { + Text("Hello, World!") + Text("Hello, World!") + Text("Hello, World!") + } + .padding(10) + .background(RoundedRectangle(cornerRadius: 10).fill(.tertiary)) - HStack { + ScrollView { VStack { - Text("Hello, World!") - Text("Hello, World!") - Text("Hello, World!") - } - .padding(10) - .background(RoundedRectangle(cornerRadius: 10).fill(.tertiary)) - - ScrollView { - VStack { - ForEach(0..<50) { index in - Text("Hello, World!") - } + ForEach(0..<50) { index in + Text("Hello, World!") } } - .frame(height: 300) } - - ScrollView(.horizontal) { - HStack { - Text("Horizontal ScrollView") - Text("Horizontal ScrollView") - Text("Horizontal ScrollView") - } + .frame(height: 300) + } + + ScrollView(.horizontal) { + HStack { + Text("Horizontal ScrollView") + Text("Horizontal ScrollView") + Text("Horizontal ScrollView") } - - if isExpanded { - VStack { - Text("Hello, World!") - Text("Hello, World!") - Text("Hello, World!") - } - .padding(10) - .background(RoundedRectangle(cornerRadius: 10).fill(.tertiary)) + } + + if isExpanded { + VStack { + Text("Hello, World!") + Text("Hello, World!") + Text("Hello, World!") } - - HStack { - Spacer() - Button("Detail") { - withAnimation(.spring) { - isExpanded.toggle() - } + .padding(10) + .background(RoundedRectangle(cornerRadius: 10).fill(.tertiary)) + } + + HStack { + Spacer() + Button("Detail") { + withAnimation(.spring) { + isExpanded.toggle() } - .buttonBorderShape(.roundedRectangle) } + .buttonBorderShape(.roundedRectangle) } - Spacer(minLength: 0) } - .padding() + Spacer(minLength: 0) } - .clipped() - .padding(8) + .padding() } + .clipped() + .padding(8) } +} + +#Preview("isPresented") { + struct Preview: View { @@ -174,11 +236,9 @@ public struct SheetModifier: ViewModifier { .fill(Color.purple) .ignoresSafeArea() } - .modifier( - SheetModifier(isPresented: $isPresented) { - SheetContent() - } - ) + .blanket(isPresented: $isPresented) { + SheetContent(title: "This is a blanket") + } } } @@ -186,23 +246,41 @@ public struct SheetModifier: ViewModifier { } -#Preview("Transition") { - - struct TransitionExample: View { - @State var isPresented = false - +#Preview("item") { + + struct Item: Identifiable { + let id = UUID() + let title: String + } + + struct Preview: View { + + @State var item: Item? + var body: some View { VStack { - Button("Show") { - isPresented.toggle() + Button("Show A") { + item = .init(title: "This is a blanket A") } - if isPresented { - Text("Hello, World!") - .transition(.opacity.animation(.smooth)) + Button("Show B") { + item = .init(title: "This is a blanket B") } + Button("Show C") { + item = .init(title: "This is a blanket C") + } + Rectangle() + .fill(Color.purple) + .ignoresSafeArea() + } + .blanket(item: $item) { item in + SheetContent(title: item.title) } } } - - return TransitionExample() + + return Preview() } + +#endif + +