Skip to content

Commit

Permalink
Merge pull request #7 from tgrapperon/view-modifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
tgrapperon committed Jan 21, 2023
2 parents bab51e0 + a317b20 commit f24d467
Showing 1 changed file with 104 additions and 65 deletions.
169 changes: 104 additions & 65 deletions Sources/SwiftUILayoutGuides/SwiftUILayoutGuides.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ public struct WithLayoutMargins<Content>: View where Content: View {
///
/// - Note: This modifier is equivalent to calling ``.fitToReadableContentWidth()`` on
/// the content view.
@available(
iOS, deprecated: 9999.0, message: "Use the `.fitToReadableContentWidth` modifier instead."
)
@available(
macOS, deprecated: 9999.0, message: "Use the `.fitToReadableContentWidth` modifier instead."
)
@available(
tvOS, deprecated: 9999.0, message: "Use the `.fitToReadableContentWidth` modifier instead."
)
@available(
watchOS, deprecated: 9999.0, message: "Use the `.fitToReadableContentWidth` modifier instead."
)
public struct FitReadableContentWidth<Content>: View where Content: View {
let alignment: Alignment
let content: Content
Expand All @@ -58,27 +70,24 @@ public struct FitReadableContentWidth<Content>: View where Content: View {
}

public var body: some View {
InsetContent(alignment: alignment, content: content)
.measureLayoutMargins()
}

private struct InsetContent: View {
let alignment: Alignment
let content: Content
@Environment(\.readableContentInsets) var readableContentInsets
var body: some View {
content
.frame(maxWidth: .infinity, alignment: alignment)
.padding(.leading, readableContentInsets.leading)
.padding(.trailing, readableContentInsets.trailing)
}
self.modifier(FitLayoutGuidesWidth(alignment: alignment, kind: .readableContent))
}
}

/// This view makes its content `View` fit the layout margins guide width.
///
/// - Note: This modifier is equivalent to calling ``.fitToLayoutMarginsWidth()`` on
/// the content view.
@available(iOS, deprecated: 9999.0, message: "Use the `.fitToLayoutMarginsWidth` modifier instead.")
@available(
macOS, deprecated: 9999.0, message: "Use the `.fitToLayoutMarginsWidth` modifier instead."
)
@available(
tvOS, deprecated: 9999.0, message: "Use the `.fitToLayoutMarginsWidth` modifier instead."
)
@available(
watchOS, deprecated: 9999.0, message: "Use the `.fitToLayoutMarginsWidth` modifier instead."
)
public struct FitLayoutMarginsWidth<Content>: View where Content: View {
let alignment: Alignment
let content: Content
Expand All @@ -98,15 +107,45 @@ public struct FitLayoutMarginsWidth<Content>: View where Content: View {
}

public var body: some View {
InsetContent(alignment: alignment, content: content)
.measureLayoutMargins()
self.modifier(FitLayoutGuidesWidth(alignment: alignment, kind: .layoutMargins))
}
}

private struct InsetContent: View {
internal struct FitLayoutGuidesWidth: ViewModifier {
enum Kind {
case layoutMargins
case readableContent
}

let alignment: Alignment
let kind: Kind

func body(content: Content) -> some View {
switch kind {
case .layoutMargins:
content.modifier(InsetLayoutMargins(alignment: alignment))
.measureLayoutMargins()
case .readableContent:
content.modifier(InsetReadableContent(alignment: alignment))
.measureLayoutMargins()
}
}

private struct InsetReadableContent: ViewModifier {
let alignment: Alignment
@Environment(\.readableContentInsets) var readableContentInsets
func body(content: Content) -> some View {
content
.frame(maxWidth: .infinity, alignment: alignment)
.padding(.leading, readableContentInsets.leading)
.padding(.trailing, readableContentInsets.trailing)
}
}

private struct InsetLayoutMargins: ViewModifier {
let alignment: Alignment
let content: Content
@Environment(\.layoutMarginsInsets) var layoutMarginsInsets
var body: some View {
func body(content: Content) -> some View {
content
.frame(maxWidth: .infinity, alignment: alignment)
.padding(.leading, layoutMarginsInsets.leading)
Expand All @@ -124,9 +163,9 @@ extension View {
/// - Note: This modifier is equivalent to wrapping the view inside a
/// ``FitReadableContentWidth`` view.
public func fitToReadableContentWidth(alignment: Alignment = .center) -> some View {
FitReadableContentWidth(alignment: alignment) { self }
self.modifier(FitLayoutGuidesWidth(alignment: alignment, kind: .readableContent))
}

/// Use this modifier to make the view fit the layout margins guide width.
///
/// - Parameter alignment: The `Alignment` to use when the view is smaller than
Expand All @@ -135,16 +174,14 @@ extension View {
/// - Note: This modifier is equivalent to wrapping the view inside a
/// ``FitLayoutMarginsWidth`` view.
public func fitToLayoutMarginsWidth(alignment: Alignment = .center) -> some View {
FitLayoutMarginsWidth(alignment: alignment) { self }
self.modifier(FitLayoutGuidesWidth(alignment: alignment, kind: .layoutMargins))
}


/// Use this modifier to populate the ``layoutMarginsInsets`` and ``readableContentInsets``
/// for the target view.
///
/// - Note: You don't have to wrap this view inside a ``WithLayoutMargins`` view.
public func measureLayoutMargins() -> some View {
modifier(LayoutGuidesModifier())
self.modifier(LayoutGuidesModifier())
}
}

Expand Down Expand Up @@ -182,11 +219,13 @@ struct LayoutGuidesModifier: ViewModifier {
.environment(\.layoutMarginsInsets, layoutMarginsInsets)
.environment(\.readableContentInsets, readableContentInsets)
.background(
LayoutGuides(onLayoutMarginsGuideChange: {
layoutMarginsInsets = $0
}, onReadableContentGuideChange: {
readableContentInsets = $0
})
LayoutGuides(
onLayoutMarginsGuideChange: {
layoutMarginsInsets = $0
},
onReadableContentGuideChange: {
readableContentInsets = $0
})
)
#endif
}
Expand All @@ -213,7 +252,7 @@ struct LayoutGuidesModifier: ViewModifier {
final class LayoutGuidesView: UIView {
var onLayoutMarginsGuideChange: (EdgeInsets) -> Void = { _ in }
var onReadableContentGuideChange: (EdgeInsets) -> Void = { _ in }

override func layoutMarginsDidChange() {
super.layoutMarginsDidChange()
updateLayoutMargins()
Expand Down Expand Up @@ -318,43 +357,43 @@ struct LayoutGuidesModifier: ViewModifier {
}
}

#if os(iOS)
@available(iOS 16.0, *)
struct SwiftUILayoutGuides_Previews: PreviewProvider {
static func sample<Content>(_ title: String, _ content: () -> Content) -> some View
where Content: View {
VStack(alignment: .leading) {
Text(title)
.font(Font.system(size: 20, weight: .bold))
.padding()
content()
#if os(iOS)
@available(iOS 16.0, *)
struct SwiftUILayoutGuides_Previews: PreviewProvider {
static func sample<Content>(_ title: String, _ content: () -> Content) -> some View
where Content: View {
VStack(alignment: .leading) {
Text(title)
.font(Font.system(size: 20, weight: .bold))
.padding()
content()
}
.border(Color.primary, width: 2)
}
.border(Color.primary, width: 2)
}

static var previews: some View {
NavigationSplitView {
VStack(spacing: 0) {
sample("ScrollView") { ScrollViewTest() }
sample("List.plain") { ListTest().listStyle(.plain) }
#if os(iOS) || os(tvOS)
sample("List.grouped") { ListTest().listStyle(.grouped) }
sample("List.insetGrouped") { ListTest().listStyle(.insetGrouped) }
#endif
}
} detail: {
VStack(spacing: 0) {
sample("ScrollView") { ScrollViewTest() }
sample("List.plain") { ListTest().listStyle(.plain) }
#if os(iOS) || os(tvOS)
sample("List.grouped") { ListTest().listStyle(.grouped) }
sample("List.insetGrouped") { ListTest().listStyle(.insetGrouped) }
#endif
static var previews: some View {
NavigationSplitView {
VStack(spacing: 0) {
sample("ScrollView") { ScrollViewTest() }
sample("List.plain") { ListTest().listStyle(.plain) }
#if os(iOS) || os(tvOS)
sample("List.grouped") { ListTest().listStyle(.grouped) }
sample("List.insetGrouped") { ListTest().listStyle(.insetGrouped) }
#endif
}
} detail: {
VStack(spacing: 0) {
sample("ScrollView") { ScrollViewTest() }
sample("List.plain") { ListTest().listStyle(.plain) }
#if os(iOS) || os(tvOS)
sample("List.grouped") { ListTest().listStyle(.grouped) }
sample("List.insetGrouped") { ListTest().listStyle(.insetGrouped) }
#endif
}
}
.previewInterfaceOrientation(.landscapeRight)
.previewDevice(PreviewDevice(rawValue: "iPad Pro (11-inch) (4th generation)"))
}
.previewInterfaceOrientation(.landscapeRight)
.previewDevice(PreviewDevice(rawValue: "iPad Pro (11-inch) (4th generation)"))
}
}
#endif
#endif
#endif

0 comments on commit f24d467

Please sign in to comment.