Layouting in iOS

The layouting cycle (measure and arrange) in Uno on iOS is a mingling of native layouting logic and logic in managed code. These interactions are summarized in the diagram below. This information is primarily intended to help when debugging Uno, but may also be useful when attempting to incorporate non-Uno views into the visual tree.

flowchart TD %% ios layout flow basesetneedslayout{{"base.SetNeedsLayout()"}} layoutsublayers{{"CALayer.LayoutSublayers"}} layoutsubviews["(override) FrameworkElement.LayoutSubviews()"] subgraph Measure Invalidation invalidate-arrange["UIElement.InvalidateArrange()"] invalidate-measure["IFrameworkElementHelper.InvalidateMeasure()"] setneedslayout["(override) FrameworkElement.SetNeedsLayout()"] setsuperviewneedslayout["FrameworkElement.SetSuperviewNeedsLayout()"] native.sizethatfits{{".SizeThatFits() called from parent element"}} invalidate-arrange ==> invalidate-measure invalidate-measure ==> setneedslayout setneedslayout --> setsuperviewneedslayout setsuperviewneedslayout -. on parent element.-> setneedslayout setneedslayout ==> basesetneedslayout end subgraph Measure Phase sizethatfits["(override) FrameworkElement.SizeThatFits(availableSize)"] xamlmeasure["FrameworkElement.XamlMeasure(availableSize)"] layouter.measure["Layouter.Measure(availableSize)"] layouter.measureoverride["Layouter.MeasureOverride(availableSize)"] measureoverride["FrameworkELement.MeasureOverride(availableSize)<br><em>overridden by element</em>"] appmeasureoverrride[["local implementation of MeasureOverride(availableSize)<br>"]] subgraph Subelement measuring measureelement["FrameworkElement.MeasureElement(child)"] layouter.measureelement["Layouter.MeasureElement(child)"] measurechildoverride["Layouter.MeasureChildOverride"] measureelement ==> layouter.measureelement layouter.measureelement ==> measurechildoverride end sizethatfits ==> xamlmeasure xamlmeasure ==> layouter.measure layouter.measure ==> layouter.measureoverride layoutsubviews ==> xamlmeasure layouter.measureoverride ==> measureoverride measureoverride ==> appmeasureoverrride appmeasureoverrride -- for child elements --> measureelement end basesetneedslayout -. "schedule for next loop (native)" .-> layoutsublayers layoutsublayers ==> layoutsubviews native.sizethatfits ==> sizethatfits measurechildoverride ==> sizethatfits frame -. "internal scheduling for next loop (native)" .-> layoutsublayers subgraph Arrange Phase layouter.arrange["Layouter.Arrange(finalRect)"] arrangeoverride["FrameworkElement.ArrangeOverride(finalSize)"] frame{{".Frame property set"}} subgraph for child elements arrangeelement["Framework.ArrangeElement(child)"] layouter.arrangeelement app.arrangeoverride[["(override) ArrangeOverride(finalSize)"]] layouter.arrangechild layouter.arrangechildoverride end layouter.arrange ==> arrangeoverride arrangeoverride ==> app.arrangeoverride app.arrangeoverride ==> arrangeelement arrangeelement ==> layouter.arrangeelement layouter.arrangeelement ==> layouter.arrangechild layouter.arrangechild ==> layouter.arrangechildoverride layouter.arrangechildoverride ==> frame layoutsubviews ==> layouter.arrange end subgraph legend direction LR uno-legend["Uno methods"] native-legend{{"Native (iOS) methods"}} application-legend[["Application/Framework implementation"]] end