uicore-ts
Version:
UICore is a library to build native-like user interfaces using pure Typescript. No HTML is needed at all. Components are described as TS classes and all user interactions are handled explicitly. This library is strongly inspired by the UIKit framework tha
128 lines (127 loc) • 5 kB
TypeScript
/**
* UILayoutCycleTracer
*
* A development-only utility that detects and reports layout cycles in the
* UIView layout system.
*
* A layout cycle occurs when layouting a view causes another setNeedsLayout()
* call on the same view (or an ancestor) within the same layout pass, causing
* the loop in layoutViewsIfNeeded() to iterate multiple times.
*
* Usage:
* UILayoutCycleTracer.enable() — start tracing
* UILayoutCycleTracer.disable() — stop tracing
* UILayoutCycleTracer.isEnabled — check current state
*
* When a cycle is detected, a detailed report is printed to the console
* including:
* - Which view was re-queued
* - Its full superview chain
* - A state snapshot (isVirtualLayouting, frame size, bounds size, class name)
* - The full history of all previous cycle events for the same view this pass,
* so the entire oscillation pattern is visible in one place
* - The call stack at the point of the re-queue (setNeedsLayout call)
* - How many times the view has been laid out in this pass
*
* Integration:
* Call UILayoutCycleTracer.willBeginLayoutPass() at the start of
* layoutViewsIfNeeded(), UILayoutCycleTracer.didLayoutView(view) after each
* view is laid out, and UILayoutCycleTracer.viewDidCallSetNeedsLayout(view)
* from setNeedsLayout() while a pass is active.
*/
interface UILayoutCycleViewSnapshot {
isVirtualLayouting: boolean;
frameWidth: number;
frameHeight: number;
boundsWidth: number;
boundsHeight: number;
className: string;
elementID: string;
}
interface UILayoutCycleEvent {
occurrenceIndex: number;
iteration: number;
layoutCountAtTime: number;
callerFunction: string;
snapshot: UILayoutCycleViewSnapshot;
cleanStack: string;
}
export declare class UILayoutCycleTracer {
static _isEnabled: boolean;
static _isPassActive: boolean;
static _layoutCountsThisPass: Map<any, number>;
static _eventsThisPass: Map<any, UILayoutCycleEvent[]>;
static _currentIteration: number;
static _totalReportsThisPass: number;
static reportThreshold: number;
static maxReportsPerPass: number;
static _noiseFramePrefixes: string[];
static get isEnabled(): boolean;
static enable(reportThreshold?: number): void;
static disable(): void;
/**
* Called at the very start of each layoutViewsIfNeeded() invocation.
*/
static willBeginLayoutPass(): void;
/**
* Called after each iteration increment in the layoutViewsIfNeeded() while loop.
*/
static willBeginIteration(iteration: number): void;
/**
* Called after a view's layoutIfNeeded() completes.
*/
static didLayoutView(view: any): void;
/**
* Called from setNeedsLayout() when a view enters the queue.
* If a layout pass is currently active, this is a potential cycle.
*/
static viewDidCallSetNeedsLayout(view: any): void;
/**
* Called at the end of a layout pass.
*/
static didFinishLayoutPass(iterationCount: number): void;
/**
* Strips the "Error" header line and any leading framework/tracer noise
* frames from a raw Error.stack string, so the first frame shown is always
* the application code that triggered the re-queue.
*/
static _cleanStack(rawStack: string): string;
/**
* Extracts the name of the first application function from a cleaned stack string.
* Returns something like "UIButton.layoutSubviews" or "CellView.layoutSubviews".
*/
static _extractCallerFunctionName(cleanStack: string): string;
/**
* Captures a diagnostic snapshot of a view's current layout state.
* Safe to call at any point — gracefully handles nil/missing properties.
*/
static _snapshotView(view: any): UILayoutCycleViewSnapshot;
static _formatSnapshotInline(snapshot: UILayoutCycleViewSnapshot): string;
static _viewIdentifier(view: any): string;
static _superviewChain(view: any): string;
/**
* Prints a single event row inside the history section.
* Past events are collapsed sub-groups (stack accessible but not noisy).
* The current event is expanded so it's immediately visible.
*/
static _printHistoryEvent(event: UILayoutCycleEvent, isCurrent: boolean): void;
static _reportCycle(view: any, history: UILayoutCycleEvent[]): void;
/**
* Manually prints a full diagnostic report for any view on demand.
* Call from the browser console: layoutReport(someView)
*
* Shows:
* - Current state snapshot (isVirtualLayouting, frame, bounds)
* - Superview chain with state at each level
* - Full cycle history for this view from the most recent pass, if any
* - The raw view object for live inspection
*/
static printViewReport(view: any): void;
}
declare global {
interface Window {
UILayoutCycleTracer?: typeof UILayoutCycleTracer;
layoutReport: (view: any) => void;
}
}
export {};