Skip to content

Commit

Permalink
1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
nathantannar4 committed Nov 3, 2023
1 parent 8455bd0 commit 7f781a8
Show file tree
Hide file tree
Showing 10 changed files with 458 additions and 86 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
build.sh
Sources/EnginePrivate
Sources/EngineCore/EngineCore.xcframework
Sources/EngineCore/EngineCore.xcframework.zip
Sources/EngineCore/Example
21 changes: 21 additions & 0 deletions Example/Example/ViewStyleExamples.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,27 @@ struct ViewStyleExamples: View {
Text("Label")
}
.labeledViewStyle(DoubleLabelStyle())

Text("Multiple View Styling")
.font(.headline)

HStack {
LabeledView {
Text("Content")
} label: {
Text("Label")
}
.border(Color.blue)

LabeledView {
Text("Content")
} label: {
Text("Label")
}
.labeledViewStyle(PaddedLabeledViewStyle())
.border(Color.blue)
}
.labeledViewStyle(BorderedLabeledViewStyle())
}
}
.frame(maxWidth: .infinity, alignment: .leading)
Expand Down
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ With the `@StyledView` macro nearly any `View` can be transformed into one that

> Xcode's syntax highlighting currently does not work for types generated by a macro
[Open ViewStyle.swift](https://github.com/nathantannar4/Engine/blob/main/Sources/Engine/Sources/StyledView.swift)
[Open StyledView.swift](https://github.com/nathantannar4/Engine/blob/main/Sources/Engine/Sources/StyledView.swift)

#### Examples

Expand All @@ -82,23 +82,26 @@ struct LabeledView<Label: View, Content: View>: StyledView {
}
}

// 1. Define a custom style
struct BorderedLabeledViewStyle: LabeledViewStyle {
func makeBody(configuration: Configuration) -> some View {
LabeledView(configuration)
.border(Color.red)
extension View {
func labelViewStyle<Style: LabelViewStyle>(_ style: Style) -> some View {
modifier(LabelViewStyleModifier(style))
}
}

// 2. Apply the style to the styled view
struct ContentView: View {
var body: some View {
LabeledView {
Text("Label")
} content: {
Text("Content")
struct VerticalLabeledViewStyle: LabeledViewStyle {
func makeBody(configuration: Configuration) -> some View {
VStack {
configuration.label

configuration.content
}
.labelStyle(BorderedLabeledViewStyle())
}
}

struct BorderedLabeledViewStyle: LabeledViewStyle {
func makeBody(configuration: Configuration) -> some View {
LabeledView(configuration)
.border(Color.red)
}
}
```
Expand Down
140 changes: 140 additions & 0 deletions Sources/Engine/Sources/StaticContent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//
// Copyright (c) Nathan Tannar
//

import SwiftUI

import EngineCore

/// A static type-erased `View`.
///
/// > Warning: The ``TypeDescriptor/descriptor`` should match the type
/// returned by `content`
@frozen
public struct StaticContent<Descriptor: TypeDescriptor>: View {

@usableFromInline
var content: Any

@inlinable
public init(
_ descriptor: Descriptor.Type = Descriptor.self,
content: () -> Any
) {
self.content = content()
}

public var body: Never {
bodyError()
}

public static func _makeView(
view: _GraphValue<Self>,
inputs: _ViewInputs
) -> _ViewOutputs {
func project<T>(_ type: T.Type) -> _ViewOutputs {
let conformance = ViewProtocolDescriptor.conformance(of: T.self)!
var visitor = ViewOutputsVisitor(view: view[\.content], inputs: inputs)
conformance.visit(visitor: &visitor)
return visitor.outputs
}
let type = unsafeBitCast(Descriptor.descriptor, to: Any.Type.self)
return _openExistential(type, do: project)
}

public static func _makeViewList(
view: _GraphValue<Self>,
inputs: _ViewListInputs
) -> _ViewListOutputs {
func project<T>(_ type: T.Type) -> _ViewListOutputs {
let conformance = ViewProtocolDescriptor.conformance(of: T.self)!
var visitor = ViewListOutputsVisitor(view: view[\.content], inputs: inputs)
conformance.visit(visitor: &visitor)
return visitor.outputs
}
let type = unsafeBitCast(Descriptor.descriptor, to: Any.Type.self)
return _openExistential(type, do: project)
}

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public static func _viewListCount(
inputs: _ViewListCountInputs
) -> Int? {
func project<T>(_ type: T.Type) -> Int? {
let conformance = ViewProtocolDescriptor.conformance(of: T.self)!
var visitor = ViewListOutputsCountVisitor(inputs: inputs)
conformance.visit(visitor: &visitor)
return visitor.outputs
}
let type = unsafeBitCast(Descriptor.descriptor, to: Any.Type.self)
return _openExistential(type, do: project)
}
}

private struct StaticContentBody<Content: View>: View {
var content: Any

var body: Content {
content as! Content
}
}

private struct ViewOutputsVisitor: ViewVisitor {
var view: _GraphValue<Any>
var inputs: _ViewInputs

var outputs: _ViewOutputs!

mutating func visit<Content>(type: Content.Type) where Content: View {
let view = unsafeBitCast(view, to: _GraphValue<StaticContentBody<Content>>.self)
outputs = StaticContentBody<Content>._makeView(view: view, inputs: inputs)
}
}

private struct ViewListOutputsVisitor: ViewVisitor {
var view: _GraphValue<Any>
var inputs: _ViewListInputs

var outputs: _ViewListOutputs!

mutating func visit<Content>(type: Content.Type) where Content: View {
let view = unsafeBitCast(view, to: _GraphValue<StaticContentBody<Content>>.self)
outputs = StaticContentBody<Content>._makeViewList(view: view, inputs: inputs)
}
}

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
private struct ViewListOutputsCountVisitor: ViewVisitor {
var inputs: _ViewListCountInputs

var outputs: Int?

mutating func visit<Content>(type: Content.Type) where Content: View {
outputs = StaticContentBody<Content>._viewListCount(inputs: inputs)
}
}

// MARK: - Previews

struct StaticContent_Previews: PreviewProvider {

struct Content: View {
var body: some View {
Text("Hello, World")
}
}

struct Descriptor: TypeDescriptor {
static var descriptor: UnsafeRawPointer {
TypeIdentifier(Content.self).metadata
}
}

static var previews: some View {
VStack {
StaticContent<Descriptor> {
Content()
}
}
}
}
151 changes: 151 additions & 0 deletions Sources/Engine/Sources/StaticModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
//
// Copyright (c) Nathan Tannar
//

import SwiftUI

import EngineCore

/// A static type-erased `ViewModifier`.
///
/// > Warning: The ``TypeDescriptor/descriptor`` should match the type
/// returned by `modifier`
@frozen
public struct StaticModifier<Descriptor: TypeDescriptor>: ViewModifier {

@usableFromInline
var modifier: Any

@inlinable
public init(
_ descriptor: Descriptor.Type = Descriptor.self,
modifier: () -> Any
) {
self.modifier = modifier()
}

public func body(content: Content) -> Never {
bodyError()
}

public static func _makeView(
modifier: _GraphValue<Self>,
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs {
func project<T>(_ type: T.Type) -> _ViewOutputs {
let conformance = ViewModifierProtocolDescriptor.conformance(of: T.self)!
var visitor = ViewOutputsVisitor(view: modifier[\.modifier], inputs: inputs, body: body)
conformance.visit(visitor: &visitor)
return visitor.outputs
}
let type = unsafeBitCast(Descriptor.descriptor, to: Any.Type.self)
return _openExistential(type, do: project)
}

public static func _makeViewList(
modifier: _GraphValue<Self>,
inputs: _ViewListInputs,
body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs
) -> _ViewListOutputs {
func project<T>(_ type: T.Type) -> _ViewListOutputs {
let conformance = ViewModifierProtocolDescriptor.conformance(of: T.self)!
var visitor = ViewListOutputsVisitor(view: modifier[\.modifier], inputs: inputs, body: body)
conformance.visit(visitor: &visitor)
return visitor.outputs
}
let type = unsafeBitCast(Descriptor.descriptor, to: Any.Type.self)
return _openExistential(type, do: project)
}

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public static func _viewListCount(
inputs: _ViewListCountInputs,
body: (_ViewListCountInputs) -> Int?
) -> Int? {
func project<T>(_ type: T.Type) -> Int? {
withoutActuallyEscaping(body) { body in
let conformance = ViewModifierProtocolDescriptor.conformance(of: T.self)!
var visitor = ViewListOutputsCountVisitor(inputs: inputs, body: body)
conformance.visit(visitor: &visitor)
return visitor.outputs
}
}
let type = unsafeBitCast(Descriptor.descriptor, to: Any.Type.self)
return _openExistential(type, do: project)
}
}

private struct StaticModifierBody<Modifier: ViewModifier>: ViewModifier {
var modifier: Any

func body(content: Content) -> some View {
content.modifier(modifier as! Modifier)
}
}

private struct ViewOutputsVisitor: ViewModifierVisitor {
var view: _GraphValue<Any>
var inputs: _ViewInputs
var body: (_Graph, _ViewInputs) -> _ViewOutputs

var outputs: _ViewOutputs!

mutating func visit<Modifier>(type: Modifier.Type) where Modifier: ViewModifier {
let modifier = unsafeBitCast(view, to: _GraphValue<StaticModifierBody<Modifier>>.self)
outputs = StaticModifierBody<Modifier>._makeView(modifier: modifier, inputs: inputs, body: body)
}
}

private struct ViewListOutputsVisitor: ViewModifierVisitor {
var view: _GraphValue<Any>
var inputs: _ViewListInputs
var body: (_Graph, _ViewListInputs) -> _ViewListOutputs

var outputs: _ViewListOutputs!

mutating func visit<Modifier>(type: Modifier.Type) where Modifier: ViewModifier {
let modifier = unsafeBitCast(view, to: _GraphValue<StaticModifierBody<Modifier>>.self)
outputs = StaticModifierBody<Modifier>._makeViewList(modifier: modifier, inputs: inputs, body: body)
}
}

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
private struct ViewListOutputsCountVisitor: ViewModifierVisitor {
var inputs: _ViewListCountInputs
var body: (_ViewListCountInputs) -> Int?

var outputs: Int?

mutating func visit<Modifier>(type: Modifier.Type) where Modifier: ViewModifier {
outputs = StaticModifierBody<Modifier>._viewListCount(inputs: inputs, body: body)
}
}

// MARK: - Previews

struct StaticModifier_Previews: PreviewProvider {

struct Modifier: ViewModifier {
func body(content: Content) -> some View {
content.border(Color.red)
}
}

struct Descriptor: TypeDescriptor {
static var descriptor: UnsafeRawPointer {
TypeIdentifier(Modifier.self).metadata
}
}

static var previews: some View {
VStack {
Text("Hello, World")
.modifier {
StaticModifier<Descriptor> {
Modifier()
}
}
}
}
}
Loading

0 comments on commit 7f781a8

Please sign in to comment.