Skip to content

LayoutComputer becomes nil during if statement layout, falling back to DefaultEngine #665

@Dark-Existed

Description

@Dark-Existed

I implemented a custom layout similar to the original ZStack to help diagnose the issue. It prints the dimensions during sizeThatFits.

struct MyCustomZStack: Layout {
    public var alignment: Alignment

    @inlinable
    public init(alignment: Alignment = .center) {
        self.alignment = alignment
    }
    
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        guard !subviews.isEmpty else {
            return CGSize.zero
        }
        let maxPriority =
            subviews.lazy
            .map { $0.priority }
            .max() ?? 0.0
        return subviews.lazy
            .filter { $0.priority == maxPriority }
            .map {
                let dimension = $0.dimensions(in: proposal)
                print(dimension)
                return dimension
            }
            .reduce(CGSize.zero) { result, dimension in
                CGSize(
                    width: max(result.width, dimension.width),
                    height: max(result.height, dimension.height)
                )
            }
    }
    
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        let maxPriority =
            subviews.lazy
                .map { $0.priority }
                .max() ?? 0.0
        let alignmentSize = subviews.lazy
            .filter { $0.priority == maxPriority }
            .map { $0.dimensions(in: ProposedViewSize(bounds.size)) }
            .reduce(CGSize(width: -.infinity, height: -.infinity)) { result, dimension in
                CGSize(
                    width: max(result.width, dimension[alignment.horizontal]),
                    height: max(result.height, dimension[alignment.vertical])
                )
            }
        
        for subview in subviews {
            let dimensions = subview.dimensions(in: ProposedViewSize(bounds.size))
            let horizontalAlignmentValue = dimensions[alignment.horizontal]
            let verticalAlignmentValue = dimensions[alignment.vertical]
            let origin = CGPoint(
                x: alignmentSize.width - horizontalAlignmentValue + bounds.origin.x,
                y: alignmentSize.height - verticalAlignmentValue + bounds.origin.y
            )
            subview.place(at: origin, proposal: proposal)
        }
    }
}

Test Case

struct TestExample: View {
    @State private var showed = true

    var body: some View {
       MyCustomZStack {
            if showed {
                Color.red
                    .frame(width: 300, height: 300)
            }
            Color.blue
                .frame(width: 200, height: 200)
                .onAppear {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                        first.toggle()
                    }
                }
        }
    }
}

Expected Behavior

SwiftUI Output

ViewDimensions(guideComputer: SwiftUI.LayoutComputer(box: SwiftUI.(unknown context at $1d4dfec58).LayoutEngineBox<SwiftUI.(unknown context at $1d4df00ec).UnaryLayoutEngine<SwiftUI._FrameLayout>>, seed: 0), size: SwiftUI.ViewSize(value: (300.0, 300.0), _proposal: (440.0, 860.0)))
ViewDimensions(guideComputer: SwiftUI.LayoutComputer(box: SwiftUI.(unknown context at $1d4dfec58).LayoutEngineBox<SwiftUI.(unknown context at $1d4df00ec).UnaryLayoutEngine<SwiftUI._FrameLayout>>, seed: 0), size: SwiftUI.ViewSize(value: (200.0, 200.0), _proposal: (440.0, 860.0)))
ViewDimensions(guideComputer: SwiftUI.LayoutComputer(box: SwiftUI.(unknown context at $1d4dfec58).LayoutEngineBox<SwiftUI.(unknown context at $1d4df00ec).UnaryLayoutEngine<SwiftUI._FrameLayout>>, seed: 1), size: SwiftUI.ViewSize(value: (200.0, 200.0), _proposal: (440.0, 860.0)))
ViewDimensions(guideComputer: SwiftUI.LayoutComputer(box: SwiftUI.(unknown context at $1d4dfec58).LayoutEngineBox<SwiftUI.(unknown context at $1d4df00ec).UnaryLayoutEngine<SwiftUI._FrameLayout>>, seed: 1), size: SwiftUI.ViewSize(value: (200.0, 200.0), _proposal: (440.0, 860.0)))

Observed Behavior

OpenSwiftUI Output

ViewDimensions(guideComputer: OpenSwiftUICore.LayoutComputer(box: OpenSwiftUICore.(unknown context at $1054ca2e8).LayoutEngineBox<OpenSwiftUICore.(unknown context at $1054ca7b0).UnaryLayoutEngine<OpenSwiftUICore._FrameLayout>>, seed: 0), size: OpenSwiftUICore.ViewSize(value: (300.0, 300.0), _proposal: (440.0, 821.6666666666667)))
ViewDimensions(guideComputer: OpenSwiftUICore.LayoutComputer(box: OpenSwiftUICore.(unknown context at $1054ca2e8).LayoutEngineBox<OpenSwiftUICore.(unknown context at $1054ca7b0).UnaryLayoutEngine<OpenSwiftUICore._FrameLayout>>, seed: 0), size: OpenSwiftUICore.ViewSize(value: (200.0, 200.0), _proposal: (440.0, 821.6666666666667)))
ViewDimensions(guideComputer: OpenSwiftUICore.LayoutComputer(box: OpenSwiftUICore.(unknown context at $1054ca2e8).LayoutEngineBox<OpenSwiftUICore.(unknown context at $1054ca7b0).UnaryLayoutEngine<OpenSwiftUICore._FrameLayout>>, seed: 1), size: OpenSwiftUICore.ViewSize(value: (200.0, 200.0), _proposal: (440.0, 821.6666666666667)))
ViewDimensions(guideComputer: OpenSwiftUICore.LayoutComputer(box: OpenSwiftUICore.(unknown context at $1054ca2e8).LayoutEngineBox<OpenSwiftUICore.LayoutComputer.DefaultEngine>, seed: 0), size: OpenSwiftUICore.ViewSize(value: (440.0, 821.6666666666667), _proposal: (440.0, 821.6666666666667)))

Related

The layoutComputer will be nil, and use the DefaultEngine.

package struct LayoutProxy: Equatable {

    ...

    package var layoutComputer: LayoutComputer {
        guard let layoutComputer = attributes.$layoutComputer else {
            return .defaultValue
        }
        return context[layoutComputer]
    }

    ...

    package func dimensions(in proposedSize: _ProposedSize) -> ViewDimensions {
        let computer = layoutComputer
        return ViewDimensions(
            guideComputer: computer,
            size: computer.sizeThatFits(proposedSize),
            proposal: _ProposedSize(
                width: proposedSize.width ?? .nan,
                height: proposedSize.height ?? .nan
            )
        )
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions