Skip to content

Commit

Permalink
add OffsetRenderNode
Browse files Browse the repository at this point in the history
  • Loading branch information
lkzhao committed Apr 23, 2024
1 parent f132cde commit 84c03d0
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 7 deletions.
1 change: 0 additions & 1 deletion Sources/UIComponent/Components/Layout/Inset/Insets.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,3 @@ struct InsetsRenderNode: RenderNode {
[CGPoint(x: insets.left, y: insets.top)]
}
}

82 changes: 82 additions & 0 deletions Sources/UIComponent/Components/Layout/Other/Offset.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//
// File.swift
//
//
// Created by Luke Zhao on 4/23/24.
//

import UIKit

/// Wraps a content component and applies the specified offset to it.
/// Instead of creating an instance directly, use the ``Component/offset(_:)`` modifier.
public struct Offset: Component {
let content: any Component
let offset: CGPoint

public init(content: any Component, offset: CGPoint) {
self.content = content
self.offset = offset
}

public func layout(_ constraint: Constraint) -> some RenderNode {
OffsetRenderNode(content: content.layout(constraint), offset: offset)
}
}

public struct DynamicOffset: Component {
let content: any Component
let offsetProvider: (Constraint) -> CGPoint

public init(content: any Component, offsetProvider: @escaping (Constraint) -> CGPoint) {
self.content = content
self.offsetProvider = offsetProvider
}

public func layout(_ constraint: Constraint) -> some RenderNode {
let offset = offsetProvider(constraint)
return OffsetRenderNode(content: content.layout(constraint), offset: offset)
}
}

struct OffsetRenderNode: RenderNode {
typealias View = UIView

let content: any RenderNode
let offset: CGPoint

/// The size of the render node, adjusted for the insets.
var size: CGSize {
content.size
}

/// The content render nodes of this render node.
var children: [any RenderNode] {
[content]
}

/// The positions of the content render nodes within this render node.
var positions: [CGPoint] {
[offset]
}

func visibleIndexes(in frame: CGRect) -> any Collection<Int> {
[0]
}

func visibleRenderables(in frame: CGRect) -> [Renderable] {
var result = [Renderable]()
if shouldRenderView, frame.intersects(CGRect(origin: .zero, size: size)) {
result.append(Renderable(frame: CGRect(origin: .zero, size: size), renderNode: self, fallbackId: defaultReuseKey))
}
let indexes = visibleIndexes(in: frame)
for i in indexes {
let child = children[i]
let position = positions[i]
let childRenderables = child.visibleRenderables(in: frame).map {
Renderable(frame: $0.frame + position, renderNode: $0.renderNode, fallbackId: "item-\(i)-\($0.fallbackId)")
}
result.append(contentsOf: childRenderables)
}
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ extension Component {
/// - Parameter offset: The `CGPoint` value to apply as an offset.
/// - Returns: A component offset by the specified point.
public func offset(_ offset: CGPoint) -> some Component {
Insets(content: self, insets: UIEdgeInsets(top: offset.y, left: offset.x, bottom: -offset.y, right: -offset.x))
Offset(content: self, offset: offset)
}

/// Applies an offset to the component using separate x and y values.
Expand All @@ -447,17 +447,14 @@ extension Component {
/// - y: The vertical offset.
/// - Returns: A component offset by the specified x and y values.
public func offset(x: CGFloat = 0, y: CGFloat = 0) -> some Component {
Insets(content: self, insets: UIEdgeInsets(top: y, left: x, bottom: -y, right: -x))
Offset(content: self, offset: CGPoint(x: x, y: y))
}

/// Applies a dynamic offset to the component based on constraints at layout time.
/// - Parameter offsetProvider: A closure that provides a `CGPoint` based on the given `Constraint`.
/// - Returns: A component that dynamically adjusts its offset based on the provided point.
public func offset(_ offsetProvider: @escaping (Constraint) -> CGPoint) -> some Component {
DynamicInsets(content: self) {
let offset = offsetProvider($0)
return UIEdgeInsets(top: offset.y, left: offset.x, bottom: -offset.y, right: -offset.x)
}
DynamicOffset(content: self, offsetProvider: offsetProvider)
}

// MARK: - Visible insets modifiers
Expand Down

0 comments on commit 84c03d0

Please sign in to comment.