iOS AsyncDisplayKit/Texture中ASAbsoluteLayoutSpec无法拉伸内容填充宽度
问题:Texture(AsyncDisplayKit)列表单元格内容无法撑满行宽,需添加右下角三角形图标
我在iOS应用中使用AsyncDisplayKit/Texture实现列表,每行要求水平显示文本与图片,并在右下角放置绿色三角形图标。但引入ASAbsoluteLayoutSpec后,单元格内容无法撑满整个行宽,即使将其sizing设为.default也无效;移除该Spec后布局正常,但无法添加图标,尝试设置flexGrow=1和alignSelf=.stretch均无效果。
以下是原复现代码:
import UIKit import SnapKit import AsyncDisplayKit class ViewController: UIViewController, ASTableDataSource { let tableNode = ASTableNode() override func viewDidLoad() { super.viewDidLoad() view.addSubnode(tableNode) tableNode.view.snp.makeConstraints { make in make.edges.equalToSuperview() } tableNode.dataSource = self } func tableNode(_ tableNode: ASTableNode, numberOfRowsInSection section: Int) -> Int { return 100 } func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { let row = indexPath.row return { return MyCellNode(str: "Row \(row)") } } } let sizeToUse = 15.0 class MyCellNode: ASCellNode { private var title = ASTextNode() private let savedIcon = SaveIconNode() private var thumbnailNode = ASNetworkImageNode() init(str : String) { super.init() automaticallyManagesSubnodes = true automaticallyRelayoutOnSafeAreaChanges = true automaticallyRelayoutOnLayoutMarginsChanges = true title.attributedText = NSAttributedString(string: str, attributes: [.foregroundColor:UIColor.white,.font:UIFont.systemFont(ofSize: 30, weight: .medium)]) title.backgroundColor = .darkGray thumbnailNode = ASNetworkImageNode() thumbnailNode.isLayerBacked = true thumbnailNode.cornerRoundingType = .precomposited thumbnailNode.cornerRadius = sizeToUse thumbnailNode.style.preferredSize = CGSize(width: 60, height: 60) thumbnailNode.url = URL(string: "https://favicon.mars51.com/instagram.com") } override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { let content = ASStackLayoutSpec(direction: .horizontal, spacing: sizeToUse, justifyContent: .spaceBetween, alignItems: .start, children: [title,thumbnailNode]) let inseted = ASInsetLayoutSpec(insets: UIEdgeInsets(top: sizeToUse, left: sizeToUse, bottom: sizeToUse, right: sizeToUse), child: content) savedIcon.style.preferredSize = CGSize(width: sizeToUse, height: sizeToUse) savedIcon.style.layoutPosition = CGPoint(x: constrainedSize.max.width - sizeToUse, y: 0) let finalLayout = ASAbsoluteLayoutSpec(sizing: .default, children: [inseted,savedIcon]) return finalLayout } override func layout() { super.layout() var f = savedIcon.frame f.origin.y = calculatedSize.height - f.height savedIcon.frame = f } } class SaveIconNode: ASDisplayNode { override init() { super.init() isLayerBacked = true backgroundColor = .clear isOpaque = false } override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled isCancelledBlock: () -> Bool, isRasterizing: Bool) { guard let context = UIGraphicsGetCurrentContext() else { return } context.beginPath() context.move(to: CGPoint(x: bounds.maxX, y: bounds.minY)) context.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY)) context.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY)) context.closePath() UIColor.green.setFill() context.fillPath() } }
解决方案
核心问题是ASAbsoluteLayoutSpec的.default sizing不会自动让子元素撑满可用宽度,而是基于子元素固有尺寸计算自身大小。我们可以用ASOverlayLayoutSpec替代,将三角形图标作为叠加层放在内容布局的右下角,既保留内容的撑满布局,又能准确定位图标。
修改后的MyCellNode布局代码如下:
class MyCellNode: ASCellNode { private var title = ASTextNode() private let savedIcon = SaveIconNode() private var thumbnailNode = ASNetworkImageNode() init(str : String) { super.init() automaticallyManagesSubnodes = true automaticallyRelayoutOnSafeAreaChanges = true automaticallyRelayoutOnLayoutMarginsChanges = true title.attributedText = NSAttributedString(string: str, attributes: [.foregroundColor:UIColor.white,.font:UIFont.systemFont(ofSize: 30, weight: .medium)]) title.backgroundColor = .darkGray thumbnailNode = ASNetworkImageNode() thumbnailNode.isLayerBacked = true thumbnailNode.cornerRoundingType = .precomposited thumbnailNode.cornerRadius = sizeToUse thumbnailNode.style.preferredSize = CGSize(width: 60, height: 60) thumbnailNode.url = URL(string: "https://favicon.mars51.com/instagram.com") // 设置图标对齐方式为右下角 savedIcon.style.preferredSize = CGSize(width: sizeToUse, height: sizeToUse) savedIcon.style.alignSelf = .end savedIcon.style.flexGrow = 0 } override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { // 内容布局:水平Stack,两端对齐撑满宽度 let content = ASStackLayoutSpec(direction: .horizontal, spacing: sizeToUse, justifyContent: .spaceBetween, alignItems: .start, children: [title, thumbnailNode]) // 给内容添加内边距 let contentInset = ASInsetLayoutSpec(insets: UIEdgeInsets(top: sizeToUse, left: sizeToUse, bottom: sizeToUse, right: sizeToUse), child: content) // 给图标添加右下内边距,实现右下角定位 let iconInset = ASInsetLayoutSpec(insets: UIEdgeInsets(top: 0, left: 0, bottom: sizeToUse, right: sizeToUse), child: savedIcon) // 用OverlaySpec将图标叠加在内容上 let finalLayout = ASOverlayLayoutSpec(child: contentInset, overlay: iconInset) return finalLayout } // 移除手动修改frame的layout方法 }
关键修改说明
- 替换
ASAbsoluteLayoutSpec为ASOverlayLayoutSpec:将内容布局作为底层,图标作为叠加层,不影响内容的宽度撑满逻辑 - 给图标添加
ASInsetLayoutSpec:通过右下内边距直接定位到单元格右下角,无需手动修改frame - 内容布局的
ASStackLayoutSpec因justifyContent: .spaceBetween会自动撑满可用宽度,无需额外设置flexGrow
内容的提问来源于stack exchange,提问作者sudoExclamationExclamation
相关产品推荐
相关产品推荐

