Skip to content
This repository was archived by the owner on Apr 19, 2023. It is now read-only.
Open
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
25 changes: 15 additions & 10 deletions Sources/StylableScrollView/BackButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,25 @@ public struct BackButton: View {

@Environment(\.presentationMode)
public var presentationMode: Binding<PresentationMode>

@State private var hasBeenShownAtLeastOnce: Bool = false

public var body: some View {
(presentationMode.wrappedValue.isPresented || hasBeenShownAtLeastOnce) ?
Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
Image(systemName: "chevron.left")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 20, alignment: .leading)
.foregroundColor(color)
.padding(.horizontal, 16)
.font(Font.body.bold())
}
Button(
action: {
self.presentationMode.wrappedValue.dismiss()
},
label: {
Image(systemName: "chevron.left")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 20, alignment: .leading)
.foregroundColor(color)
.padding(.horizontal, 16)
.font(Font.body.bold())
}
)
.onAppear {
self.hasBeenShownAtLeastOnce = true
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/StylableScrollView/Extensions/Color.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ public extension Color {
return scheme == .light ? Color(red: 0.95, green: 0.95, blue: 0.97) : Color(red: 0.11, green: 0.11, blue: 0.12)
}

/// `Color.systemGray6` multi-platform equivalent.
///
/// - Parameters:
/// - scheme: The current color scheme.
static func label(for scheme: ColorScheme) -> Color {
return scheme == .light ? Color.black : Color.white
}

/// A multi-platform solution for obtaining the window's background color.
static var backgroundColor: Color {

Expand Down
76 changes: 56 additions & 20 deletions Sources/StylableScrollView/Extensions/Modifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import Foundation
import SwiftUI

// MARK: - SCROLLVIEW STYLE
// MARK: - SCROLLVIEW STYLE
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public extension View {

Expand Down Expand Up @@ -53,17 +53,18 @@ public extension View {
/// )
/// }
///
/// ![A stretchable scroll view with a big text talking about French footballer Kylian Mbappé. On top, a stretchable header can be seen.](StretchableHeader-kmbappe.png)
/// ![A stretchable scroll view with a big text talking about French footballer
/// Kylian Mbappé. On top, a stretchable header can be seen.](StretchableHeader-kmbappe.png)
///
@inlinable @ViewBuilder func scrollViewStyle<S>(_ style: S) -> some View where S : ScrollViewStyle {
@inlinable @ViewBuilder func scrollViewStyle<S>(_ style: S) -> some View where S: ScrollViewStyle {

self.environment(\.scrollViewStyle, AnyScrollViewStyle(style))

}

}

// MARK: - STRETCHABLE HEADER
// MARK: - STRETCHABLE HEADER
@available(iOS 15.0, macOS 12, *)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
Expand All @@ -84,19 +85,24 @@ public extension ScrollView {
/// - header: The header image that will be used for the sticky header.
/// - title: The title that appears on top of the header.
/// - navBar: The contents of the navigationBar that appears when scrolling.
/// - leadingElements: The navigation bar elements that will appear on the leading side.
/// - trailingElements: The navigation bar elements that will appear on the trailing side.
///
@ViewBuilder @inlinable func stretchableHeader<Title, NavBar>(
@ViewBuilder @inlinable func stretchableHeader<Title, NavBar, TrailingElements, LeadingElements>(
header: Image,
title: () -> Title,
navBar: () -> NavBar
) -> some View where Title: View, NavBar: View {
navBar: () -> NavBar,
leadingElements: @escaping (NavigationBarProxy) -> LeadingElements,
trailingElements: @escaping (NavigationBarProxy) -> TrailingElements
) -> some View where Title: View, NavBar: View, LeadingElements: View, TrailingElements: View {

StylableScrollView(
.vertical,
showIndicators: false,
content: {
self
})
}
)
.scrollViewStyle(
StretchableScrollViewStyle(
header: {
Expand All @@ -105,7 +111,12 @@ public extension ScrollView {
.aspectRatio(contentMode: .fill)
},
title: title,
navBarContent: navBar
navBarContent: navBar,
leadingElements: {
leadingElements($0)
}, trailingElements: {
trailingElements($0)
}
)
)

Expand All @@ -126,32 +137,42 @@ public extension ScrollView {
/// - header: The view that will be used for the sticky header.
/// - title: The title that appears on top of the header.
/// - navBar: The contents of the navigationBar that appears when scrolling.
/// - leadingElements: The navigation bar elements that will appear on the leading side.
/// - trailingElements: The navigation bar elements that will appear on the trailing side.
///
@ViewBuilder @inlinable func stretchableHeader<Header, Title, NavBar>(
@ViewBuilder @inlinable func stretchableHeader<Header, Title, NavBar, TrailingElements, LeadingElements>(
header: () -> Header,
title: () -> Title,
navBar: () -> NavBar
) -> some View where Header: View, Title: View, NavBar: View {
navBar: () -> NavBar,
leadingElements: @escaping (NavigationBarProxy) -> LeadingElements,
trailingElements: @escaping (NavigationBarProxy) -> TrailingElements
) -> some View where Header: View, Title: View, NavBar: View, LeadingElements: View, TrailingElements: View {

StylableScrollView(
.vertical,
showIndicators: false,
content: {
self
})
}
)
.scrollViewStyle(
StretchableScrollViewStyle(
header: header,
title: title,
navBarContent: navBar
navBarContent: navBar,
leadingElements: {
leadingElements($0)
}, trailingElements: {
trailingElements($0)
}
)
)

}

}

// MARK: - SIZE MODIFIERS
// MARK: - SIZE MODIFIERS
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public extension View {

Expand All @@ -175,7 +196,7 @@ public extension View {

}

// MARK: - NAVIGATION BAR ELEMENTS
// MARK: - NAVIGATION BAR ELEMENTS
extension View {

/// Creates a fake navigation bar item for ``StretchableScrollViewStyle``'s fake navigation bar using
Expand All @@ -184,7 +205,8 @@ extension View {
/// - Parameters:
/// - axis: The horizontal edge the element will appear at.
/// - content: The element view
/// - affectedByColorChanges: Whether the element is affected by the changes in color when shown in a navigation Bar. Defaults to `true`.
/// - affectedByColorChanges: Whether the element is affected by
/// the changes in color when shown in a navigation Bar. Defaults to `true`.
///
/// Use this modifier to set a specific navigation bar element for
/// ``StretchableScrollViewStyle`` instances within a view:
Expand All @@ -202,9 +224,22 @@ extension View {
@available(iOS 15.0, macOS 12.0, *)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
@ViewBuilder public func navigationBarElement<Content: View>(axis: HorizontalEdge, _ content: () -> Content) -> some View {

self.preference(
@available(*, deprecated, message: "Please pass your elements as arguments to the ScrollViewStyle.")
public func navigationBarElement<Content: View>(axis: HorizontalEdge, _ content: () -> Content) -> some View {

guard Constants.MAJOR < 1 else {
fatalError(".navigationBarElement(axis:,_:) is obsolete and is not working anymore.")
}

/*
* This function passes the navigation bar elements as preference items to the scroll view style,
* but they do not update, even if a state variable is changed.
*
* For this reason, this function is deprecated and should be deleted before the stable release,
* unless we find a way to make it work as desired.
*/
#warning("TODO: We should remove this function before the stable release (X.y.z | X > 0).")
return self.preference(
key: Preferences.NavigationBar.Elements.Key.self,
value: [
Preferences.NavigationBar.Elements.Data(
Expand All @@ -215,4 +250,5 @@ extension View {
)

}

}
14 changes: 14 additions & 0 deletions Sources/StylableScrollView/Namespaces/Constants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// File.swift
//
//
// Created by Alex Modroño Vara on 26/12/21.
//

import Foundation

struct Constants {
static let MAJOR = 0
static let MINOR = 1
static let PATCH = 0
}
38 changes: 38 additions & 0 deletions Sources/StylableScrollView/Namespaces/NavigationBarProxy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
* NON-VIOLENT PUBLIC LICENSE v4 ("LICENSE"). THE WORK IS PROTECTED BY
* COPYRIGHT AND ALL OTHER APPLICABLE LAWS. ANY USE OF THE WORK OTHER THAN
* AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY
* EXERCISING ANY RIGHTS TO THE WORK PROVIDED IN THIS LICENSE, YOU AGREE
* TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE
* MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
* CONTAINED HERE IN AS CONSIDERATION FOR ACCEPTING THE TERMS AND
* CONDITIONS OF THIS LICENSE AND FOR AGREEING TO BE BOUND BY THE TERMS
* AND CONDITIONS OF THIS LICENSE.
*
* StylableScrollView.swift
*
* Created by Alex Modroño Vara on 26/12/21.
*
*/
import Foundation
import SwiftUI

// MARK: - NAVIGATION BAR
//
/// A proxy for access to several aspects of a Navigation Bar Element,
/// such as whether the navigation bar has changed display mode, the
/// preferred color for navigation bar elements, and more.
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public struct NavigationBarProxy {

public enum Mode {
case large
case inline
}

public var mode: Mode

public var elementsColor: Color

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import Foundation
import SwiftUI

// MARK: – SWIFTLINT
// swiftlint:disable nesting

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public struct Preferences {

Expand All @@ -26,11 +29,11 @@ public struct Preferences {

/// Allows us to read the minY of the header from ancestor views.
public struct Key: PreferenceKey {

public typealias Value = Data

public static var defaultValue: Data = Data.init(minY: 0)

public static func reduce(value: inout Data, nextValue: () -> Data) {
value = nextValue()
}
Expand Down Expand Up @@ -74,7 +77,7 @@ public struct Preferences {
public typealias Value = [Data]

public static var defaultValue: [Data] = []

public static func reduce(value: inout [Data], nextValue: () -> [Data]) {
value.append(contentsOf: nextValue())
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/StylableScrollView/ScrollViewStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import SwiftUI
public protocol ScrollViewStyle {

/// A view that represents the body of a ScrollView.
associatedtype Body : View
associatedtype Body: View

/// Creates a view that represents the body of a ScrollView.
///
Expand Down
16 changes: 12 additions & 4 deletions Sources/StylableScrollView/StylableScrollView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,18 @@ import SwiftUI
/// )
/// }
///
/// ![A stretchable scroll view with a big text talking about French footballer Kylian Mbappé. On top, a stretchable header can be seen.](StretchableHeader-kmbappe.png)
/// ![A stretchable scroll view with a big text talking about
/// French footballer Kylian Mbappé. On top, a stretchable header can be seen.](StretchableHeader-kmbappe.png)
///
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public struct StylableScrollView<Content>: View {

/// The actual version of StylableScrollView that is being used.
public let version: String = "\(Constants.MAJOR).\(Constants.MINOR).\(Constants.PATCH)"

/// The style that is being used.
@Environment(\.scrollViewStyle) private var style

/// The configuration for the ScrollView
private var configuration: ScrollViewStyleConfiguration

Expand All @@ -93,7 +97,7 @@ public struct StylableScrollView<Content>: View {

}

public extension StylableScrollView where Content : View {
public extension StylableScrollView where Content: View {

/// Creates an instance of an ``StylableScrollView`` that is scrollable in a specific
/// axis, and can show indicators while scrolling, using the body that you define.
Expand All @@ -104,7 +108,11 @@ public extension StylableScrollView where Content : View {
/// component of the content offset, in a way that's suitable for the platform.
/// - content: The scroll view's content.
///
init(_ axes: Axis.Set = .vertical, showIndicators: Bool = true, content: () -> Content) {
init(
_ axes: Axis.Set = .vertical,
showIndicators: Bool = true,
content: () -> Content
) {

self.init(
ScrollViewStyleConfiguration(
Expand Down
4 changes: 2 additions & 2 deletions Sources/StylableScrollView/Styles/AnyScrollViewStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import SwiftUI
/// A type-erased ``ScrollViewStyle``.
public struct AnyScrollViewStyle: ScrollViewStyle {
private let styleMakeBody: (ScrollViewStyle.Configuration) -> AnyView

public init<S: ScrollViewStyle>(_ style: S) {
self.styleMakeBody = style.makeTypeErasedBody
}

public func makeBody(configuration: ScrollViewStyle.Configuration) -> AnyView {
self.styleMakeBody(configuration)
}
Expand Down
Loading