Skip to content

Commit

Permalink
⚡ WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
muukii committed Jul 21, 2023
1 parent 5de10d4 commit d64fd7c
Show file tree
Hide file tree
Showing 5 changed files with 610 additions and 1 deletion.
5 changes: 4 additions & 1 deletion Demo/SwiftUIPreview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import SwiftUI
import AsyncDisplayKit
import TextureBridging
import TextureSwiftSupport

enum Preview_Node: PreviewProvider {

Expand Down Expand Up @@ -31,7 +32,9 @@ private final class CustomNode: ASDisplayNode {
}

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
return ASWrapperLayoutSpec(layoutElements: [textNode])
LayoutSpec {
textNode
}
}

}
Expand Down
19 changes: 19 additions & 0 deletions DemoComponents/DemoComponents.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// DemoComponents.h
// DemoComponents
//
// Created by Muukii on 2023/07/21.
// Copyright © 2023 muukii. All rights reserved.
//

#import <Foundation/Foundation.h>

//! Project version number for DemoComponents.
FOUNDATION_EXPORT double DemoComponentsVersionNumber;

//! Project version string for DemoComponents.
FOUNDATION_EXPORT const unsigned char DemoComponentsVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <DemoComponents/PublicHeader.h>


349 changes: 349 additions & 0 deletions DemoComponents/Source.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@

import SwiftUI
import AsyncDisplayKit
import TextureSwiftSupport

enum Preview_Node: PreviewProvider {

private typealias TargetComponent = CustomNode

static var previews: some View {

Group {
ViewHost(instantiated: NodeView(node: TargetComponent()))
}

}

}

private final class CustomNode: ASDisplayNode {

private let textNode = ASTextNode()

override func didLoad() {
super.didLoad()
backgroundColor = .red

textNode.attributedText = NSAttributedString(string: "Hello")

automaticallyManagesSubnodes = true
}

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
LayoutSpec {
textNode
}
}

}

public struct ViewHost<ContentView: UIView>: UIViewRepresentable {

private let contentView: ContentView
private let _update: @MainActor (_ uiView: ContentView, _ context: Context) -> Void

public init(
instantiated: ContentView,
update: @escaping @MainActor (_ uiView: ContentView, _ context: Context) -> Void = { _, _ in }
) {
self.contentView = instantiated
self._update = update
}

public func makeUIView(context: Context) -> ContentView {
return contentView
}

public func updateUIView(_ uiView: ContentView, context: Context) {
_update(uiView, context)
}

}

import UIKit
import AsyncDisplayKit

open class NodeView<D: ASDisplayNode>: UIView {

public var node: D {
internalView.node
}

private let internalView: _InternalNodeView<D>

/// Creates an instance
/// - Parameters:
/// - node: a node that embedded
/// - expandsWidth: Boolean value that indicates whether the node always expands to maximum width in bounding rect.
/// - frame:
public init(
node: D,
expandsWidth: Bool = false,
frame: CGRect = .zero
) {

let internalView = _InternalNodeView(
node: node,
expandsWidth: expandsWidth,
frame: frame
)
self.internalView = internalView

super.init(frame: frame)

addSubview(internalView)
internalView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
internalView.leftAnchor.constraint(equalTo: leftAnchor),
internalView.rightAnchor.constraint(equalTo: rightAnchor),
internalView.topAnchor.constraint(equalTo: topAnchor),
internalView.bottomAnchor.constraint(equalTo: bottomAnchor),
])

}

@available(*, unavailable)
public required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

open override func invalidateIntrinsicContentSize() {
internalView.invalidateIntrinsicContentSize()
super.invalidateIntrinsicContentSize()
}

}

private final class _InternalNodeView<D: ASDisplayNode>: UILabel /* To use `textRect` method */ {

// MARK: - Unavailable

@available(*, unavailable)
override var text: String? {
didSet {}
}

@available(*, unavailable)
override var font: UIFont! {
didSet {}
}

@available(*, unavailable)
override var textColor: UIColor! {
didSet {}
}

@available(*, unavailable)
override var shadowColor: UIColor? {
didSet {}
}

@available(*, unavailable)
override var shadowOffset: CGSize {
didSet {}
}

@available(*, unavailable)
override var textAlignment: NSTextAlignment {
didSet {}
}

@available(*, unavailable)
override var lineBreakMode: NSLineBreakMode {
didSet {}
}

@available(*, unavailable)
override var attributedText: NSAttributedString? {
didSet {}
}

@available(*, unavailable)
override var highlightedTextColor: UIColor? {
didSet {}
}

@available(*, unavailable)
override var isHighlighted: Bool {
didSet {}
}

@available(*, unavailable)
override var isEnabled: Bool {
didSet {}
}

@available(*, unavailable)
override var numberOfLines: Int {
didSet {}
}

@available(*, unavailable)
override var adjustsFontSizeToFitWidth: Bool {
didSet {}
}

@available(*, unavailable)
override var baselineAdjustment: UIBaselineAdjustment {
didSet {}
}

@available(*, unavailable)
override var minimumScaleFactor: CGFloat {
didSet {}
}

@available(*, unavailable)
override var allowsDefaultTighteningForTruncation: Bool {
didSet {}
}

@available(*, unavailable)
override func drawText(in rect: CGRect) {
super.drawText(in: rect)
}

@available(*, unavailable)
override var preferredMaxLayoutWidth: CGFloat {
didSet {}
}

// MARK: - Properties

let node: D
private let wrapper: WrapperNode
private let delegateProxy = __InterfaceStateDelegateProxy()
public let expandsWidth: Bool

// MARK: - Initializers

init(
node: D,
expandsWidth: Bool,
frame: CGRect = .zero
) {

self.node = node
self.wrapper = .init(wrapped: node)
self.expandsWidth = expandsWidth

super.init(frame: frame)

/// To call `textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int)`
super.numberOfLines = 0
isUserInteractionEnabled = true

addSubnode(wrapper)
wrapper.add(delegateProxy)

delegateProxy.didLayoutBlock = { [weak self] in
self?.invalidateIntrinsicContentSize()
}

}

@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

deinit {
node.remove(delegateProxy)
}

override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {

let validate: (_ value: CGFloat, _ fallback: CGFloat) -> CGFloat = { value, fallback in
// To guard crash inside Texture
guard ASPointsValidForSize(value) else {
return fallback
}
return value
}

var range = ASSizeRangeUnconstrained

range.max.width = validate(bounds.width, 10000)

if expandsWidth {
range.min.width = validate(bounds.width, 0)
}

let calculatedlayout = wrapper.calculateLayoutThatFits(range)
// let calculatedSize = wrapper.calculateSizeThatFits(range.max)
// Log.debug(.generic, "[CalculateSizeThatFits] size: \(range.max), layout: \(calculatedSize) node: \(node)")
return CGRect(origin: .zero, size: calculatedlayout.size)
}

// MARK: - Functions

override func layoutSubviews() {

super.layoutSubviews()

wrapper.frame = bounds
}
}

private final class WrapperNode: ASDisplayNode {

private let wrapped: ASDisplayNode

init(wrapped: ASDisplayNode) {
self.wrapped = wrapped
super.init()
addSubnode(wrapped)
}

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
ASWrapperLayoutSpec(layoutElement: wrapped)
}

}

private final class __InterfaceStateDelegateProxy: NSObject, ASInterfaceStateDelegate {

var didLayoutBlock: (() -> Void)?

@objc dynamic func interfaceStateDidChange(_ newState: ASInterfaceState, from oldState: ASInterfaceState) {

}

@objc dynamic func didEnterVisibleState() {

}

@objc dynamic func didExitVisibleState() {

}

func didEnterDisplayState() {

}

@objc dynamic func didExitDisplayState() {

}

@objc dynamic func didEnterPreloadState() {

}

@objc dynamic func didExitPreloadState() {

}

@objc dynamic func nodeDidLayout() {
didLayoutBlock?()
}

@objc dynamic func nodeDidLoad() {

}

@objc dynamic func hierarchyDisplayDidFinish() {

}

}

0 comments on commit d64fd7c

Please sign in to comment.