How to Debug Split Screen Issues in Mobile Apps

How to Debug Split Screen Issues in Mobile Apps

June 11, 2026 · 16 min read · Common Issues

How to Debug Split Screen Issues in Mobile Apps

Understanding Split Screen Behavior on Mobile

Split‑screen mode lets users run two apps side‑by‑side (Android) or in a pane (iPadOS/iOS Split View). When an app enters this mode the system delivers a new window‑size configuration that can differ dramatically from the full‑screen baseline. The UI is laid out again, resources may be re‑selected, and lifecycle callbacks such as onConfigurationChanged (Android) or viewWillTransition(to:with:) (iOS) fire. If the app assumes a fixed screen width, hard‑codes padding, or relies on orientation‑specific resources, the new constraints can expose layout bugs, clipped controls, or even crashes.

On Android the relevant API is WindowMetrics (API 30+) or the deprecated Display#getSize. On iPadOS the trait collection provides horizontalSizeClass and verticalSizeClass. Both platforms also send a configuration change when the divider moves, meaning the app may receive multiple size updates while the user drags the split‑screen separator. Understanding this flow is the first step to diagnosing why a UI that works in full‑screen breaks when the window is resized.

Android Multi‑Window Fundamentals

iPadOS/iOS Split View Fundamentals

Common Root Causes of Split Screen Issues

Most split‑screen bugs trace back to a few recurring assumptions. Recognizing these patterns lets you target the fix rather than chasing symptoms.

CategoryTypical SymptomUnderlying Assumption
Hard‑coded dimensionsViews overflow or disappear when width < 360dpUI designed for a single minimum width (e.g., 360dp)
Fixed padding/marginButtons clipped at the split‑screen dividerPadding defined in dimens.xml without sw* qualifiers
Orientation‑locked layoutLandscape‑only assets appear in portrait splitManifest android:screenOrientation="landscape" or UISupportedInterfaceOrientations
Improper handling of onConfigurationChangedState loss, duplicated UI fragmentsMissing super call or forgetting to re‑inflate views
Resource qualifier misuseWrong drawable/layout loadedUsing layout-w600dp but forgetting layout-sw600dp for smallest width
Inaccessible touch targetsTouch area < 48dp after shrinkAssuming original size remains constant
Animation jankUI stutters while dragging dividerLayout passes triggered on every size change without RecyclerView setItemViewCacheSize

Layout Assumptions

Developers often lock a layout to a specific width using match_parent on a parent that itself has a fixed layout_width. In split‑screen the parent may receive a width of 400dp while the child still tries to occupy 720dp, causing overflow. The fix is to let children use 0dp weight in a LinearLayout or ConstraintLayout chains, or to use % dimensions in ConstraintLayout.

Resource Qualifier Pitfalls

Qualifiers like sw600dp (smallest width) are more reliable than w600dp (current width) because they survive rotation and divider movement. A common mistake is placing a layout in layout-w600dp expecting it to apply when the app is in the left pane of a split screen; if the device is held in portrait, the width may be 410dp but the smallest width is still 410dp, so the qualifier fails and the fallback layout is used, leading to inconsistent UI.

Lifecycle Missteps

If an activity declares android:configChanges="orientation|screenSize" but omits smallestScreenSize, a change from width = 410dp to width = 410dp with a different smallest width (e.g., due to split‑screen) will still trigger a recreation. The resulting flicker or state loss can be mistaken for a layout bug when it is actually a lifecycle issue.

Reproducing Split Screen Issues Reliably

A bug that appears only when the user resizes the divider is hard to catch with ad‑hoc testing. A repeatable reproduction matrix saves time and enables automated verification.

Manual Reproduction Steps

  1. Enable developer options – On Android, turn on “Force activities to be resizable” if the app targets API < 24.
  2. Launch split‑screen – Open recent apps, drag the app’s title bar to the top/bottom (Android) or drag an app from the dock to the side (iPadOS).
  3. Adjust divider – Move the separator to 30 %, 50 %, and 70 % of the screen width, pausing at each position.
  4. Observe UI – Look for clipped views, missing content, misaligned text, or performance spikes.

Automated Reproduction via ADB

You can script the resize using adb shell wm commands. The following Bash snippet sets the app window to a specific width and height, then restores the original bounds:


# Replace com.example.app with your package
PACKAGE=com.example.app
# Get current bounds
ORIG=$(adb shell dumpsys window windows | grep -E "mCurrentFocus|mFocusedApp" | awk '{print $NF}' | sed 's/}//')
# Set split‑screen left pane to 400dp width, full height
adb shell am start -n $PACKAGE/.MainActivity
adb shell wm size 1080x2280   # ensure base resolution
adb shell wm density 420
adb shell wm overscan 0,0,0,0
adb shell wm resize 400 2280   # width x height
# Wait for UI to settle
sleep 2
# Capture screenshot for visual diff
adb shell screencap -p /sdcard/split_left.png
adb pull /sdcard/split_left.png .
# Restore
adb shell wm size reset
adb shell wm density reset

For iPadOS you can use xcrun simctl with a resizable simulator:


xcrun simctl boot "iPad Pro (12.9‑inch) (5th generation)"
xcrun simctl openurl booted "https://example.com"
# Resize the simulated window to 1/3 width
xcrun simctl resize booted --width 400 --height 1024
# Run your UI tests here
xcrun simctl shutdown booted

Test Matrix

Device / EmulatorOS VersionSplit‑Screen RatioOrientationExpected BehaviorObserved Result
Pixel 5 (API 33)Android 1330 % / 70 %PortraitUI scales, no clippingButton clipped at 30 %
Pixel 5 (API 33)Android 1350 % / 50 %LandscapeTwo‑panel layout showsOverlap in middle
iPad Air (sim)iPadOS 1733 % / 66 %PortraitSidebar visibleSidebar missing
iPad Pro (sim)iPadOS 1750 % / 50 %LandscapeEqual panesLayout jumps

Running this matrix on every CI build (via Firebase Test Lab or a local device farm) catches regressions early.

Tools and Signals for Diagnosis

When a split‑screen defect appears, you need concrete data to pinpoint the cause. The following tools provide complementary signals: logs, metrics, and visual inspection.

Logcat (Android)

Example command to capture a timed log while dragging the divider:


adb logcat -v time | grep -E "WindowManager|ViewRootImpl|CONFIG" > split_log.txt

Systrace / Perfetto


python systrace.py --time=10 -o split_trace.html gfx view wm

Layout Inspector (Android Studio)

GPU Profiler / Overdraw

Accessibility Scanner

iOS Instruments


os_signpost(.begin, log: .ui, name: "SplitScreenResize")
view.setNeedsLayout()
view.layoutIfNeeded()
os_signpost(.end, log: .ui, name: "SplitScreenResize")

Visual Regression

Step‑by‑Step Diagnosis Workflow

Below is a repeatable process you can follow when a split‑screen bug is reported. Each step narrows the scope until the root cause is isolated.

1. Triage with a Symptom Checklist

SymptomLikely CategoryQuick Check
View disappears at a specific widthHard‑coded dimensionSearch for fixed dp values in layouts
UI jumps when divider movesMissing onConfigurationChanged handlingVerify manifest/configChanges
Overlap or z‑order issuesIncorrect ConstraintLayout chains or FrameLayout stackingInspect hierarchy in Layout Inspector
Jank > 16 ms per moveExpensive layout passEnable Systrace view tag
Touch target too smallMissing responsive scalingRun Accessibility Scanner
Crash on resizeNull pointer after config changeCheck logcat for Exception stack trace

If more than one symptom appears, treat them as separate issues unless they share a common trigger (e.g., a missing android:configChanges).

2. Capture Baseline Metrics

3. Reproduce at a Specific Ratio

Use the ADB snippet from the Reproducing section to lock the window to the width where the bug appears. This eliminates the variability of dragging the divider and lets you focus on a static state.

4. Enable Targeted Logging

Add temporary log statements in your activity/fragment:


override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    Log.d("SplitDebug", "New width=${newConfig.screenWidthDp}dp, smallest=${newConfig.smallestScreenWidthDp}dp")
    // Log layout parameters of root view
    val params = window.decorView.rootView.layoutParams
    Log.d("SplitDebug", "Root layoutParams: width=${params.width}, height=${params.height}")
}

For iOS:


override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    print("Will transition to size \(size)")
}

5. Inspect Layout Attributes

6. Verify Resource Selection

7. Measure Layout Pass Cost

8. Validate Touch Targets

9. Confirm Fix and Regression Test

10. Document the Resolution

Fixes for Common Causes

Now that we have a diagnosis flow, let’s look at concrete remedies for each frequent cause.

1. Replace Fixed Dimensions with Responsive Constraints

Before (XML)


<Button
    android:id="@+id/confirm"
    android:layout_width="120dp"
    android:layout_height="wrap_content"
    android:text="Confirm"
    android:layout_marginStart="24dp"
    android:layout_marginTop="16dp"/>

Problem – At 30 % width (≈ 250 dp on a 1080 px screen) the button’s left margin pushes it off‑screen.

After (ConstraintLayout)


<Button
    android:id="@+id/confirm"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:text="Confirm"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_marginStart="8dp"
    app:layout_marginEnd="8dp"
    app:layout_marginTop="16dp"/>

2. Use Smallest‑Width Qualifiers for Layouts

Create res/layout-sw600dp/activity_main.xml for tablets and split‑screen panes that are at least 600 dp wide. Keep the default layout/ for narrower screens.

If you need a three‑column layout only when the pane is ≥ 800 dp, add layout-sw800dp.

3. Handle Configuration Changes Properly

In the manifest, include all relevant flags:


<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"/>

In the activity:


override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)
    // Re‑apply any UI state that depends on width
    updateUiForWidth(newConfig.screenWidthDp)
}

Avoid re‑inflating the entire layout unless necessary; instead, adjust specific views.

4. Guard Against Orientation‑Locked Manifests

If you locked orientation for a reason (e.g., a camera preview), consider making that restriction conditional:


<activity
    android:name=".CameraActivity"
    android:screenOrientation="behind"/> <!-- inherits from parent -->

Or handle the preview in a separate fragment that can be recreated without affecting the UI.

5. Fix Resource Qualifier Misplacements

Move any layout that should appear only when the *smallest* width meets a threshold into the sw* folder.

6. Ensure Touch Targets Remain ≥ 48 dp

Define a dimension resource:


<!-- res/values/dimens.xml -->
<dimen name="min_touch_target">48dp</dimen>

Use it as a minimum width/height:


<Button
    android:id="@+id/cancel"
    android:layout_width="@dimen/min_touch_target"
    android:layout_height="@dimen/min_touch_target"
    android:text="Cancel"/>

If the button’s content is an icon, wrap it in a FrameLayout with padding to hit the minimum size while keeping the icon centered.

7. Reduce Layout Pass Overhead

8. Address iOS Specific Issues


override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    if traitCollection.horizontalSizeClass != previousTraitCollection?.horizontalSizeClass {
        updateLayoutForWidth(traitCollection.horizontalSizeClass)
    }
}

Prevention Strategies and Best Practices

Fixing bugs after they appear is costly. Embedding split‑screen awareness into your development workflow reduces regressions.

1. Design with Breakpoints Early

2. Automated Split‑Screen Tests

Add an instrumentation test that programmatically changes the window size and asserts UI properties.

Kotlin (Espresso)


@Test fun splitScreenLayout_buttonsVisible() {
    // Set window to 400dp width
    val activityScenario = ActivityScenario.launch(MainActivity::class.java)
    activityScenario.onActivity { activity ->
        val params = activity.window.attributes
        params.width = 400 * Resources.getSystem().displayDensity.toInt()
        activity.window.attributes = params
    }

    // Verify that the primary action button is at least 50% visible
    onView(withId(R.id.confirm)).check(matches(isAtLeastHalfVisible()))
}

// Custom matcher
fun isAtLeastHalfVisible(): Matcher<View> {
    return object : TypeSafeMatcher<View>() {
        override fun matchesSafely(item: View): Boolean {
            val location = IntArray(2)
            item.getLocationOnScreen(location)
            val viewRect = Rect(location[0], location[1],
                location[0] + item.width, location[1] + item.height)
            val windowRect = Rect(0, 0,
                Resources.getSystem().displayMetrics.widthPixels,
                Resources.getSystem().displayMetrics.heightPixels)
            val intersection = Rect()
            intersection.setIntersect(viewRect, windowRect)
            val visibleArea = intersection.width() * intersection.height()
            val totalArea = item.width * item.height.toLong()
            return visibleArea >= totalArea / 2
        }

        override fun describeTo(description: Description) {
            description.appendText("at least half of the view is visible on screen")
        }
    }
}

Swift (XCUITest)


func testSplitScreenButtonVisible() {
    let app = XCUIApplication()
    app.launch()

    // Simulate iPad split-screen: set width to one‑third
    let coordinate = app.windows.firstMatch.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
    let target = coordinate.withOffset(CGVector(dx: 0, dy: 0))
    // Use XCUICoordinate to press and drag the divider (simplified)
    // For brevity, assume we have a helper that sets the window size via Xcode’s simulator control
    // XCUIDevice.shared.orientation = .landscapeLeft
    // XCUIScreen.main.bounds provides the current size; we assert after resize

    let button = app.buttons["Confirm"]
    XCTAssertTrue(button.frame.width >= 40, "Button too narrow in split-screen")
}

Integrate these tests into your CI pipeline (GitHub Actions, Bitrise, etc.) on a device farm that supports resize (Firebase Test Lab, AWS Device Farm).

3. Lint Rules for Hard‑Coded Dimensions

Create a custom lint detector (Android) or SwiftLint rule that flags any dp or pt value greater than 24 used as a width or height without a weight or % alternative.

Example Android lint rule (pseudo‑code):


if (attribute.name == "layout_width" && attribute.value.endsWith("dp") &&
    attribute.value.replace(Regex("[^0-9]"), "").toIntOrNull() ?: 0 > 24) {
    report("Fixed width >24dp may break split-screen", scope)
}

4. Runtime Assertions in Debug Builds

Add a debug‑only check that logs when a view’s measured width falls below a threshold:


if (BuildConfig.DEBUG) {
    view.viewTreeObserver.addOnGlobalLayoutListener {
        if view.measuredWidth < 48 {
            Log.w("SplitScreen", "View ${view.id} too narrow: ${view.measuredWidth}dp")
        }
    }
}

5. Documentation and On‑Boarding Checklist

Add a short checklist to your project’s CONTRIBUTING.md:

6. Leverage Platform‑Specific Tools

How Autonomous Exploration Surfaces Split Screen Issues Early

Modern QA platforms can exercise an app without any test scripts, automatically discovering problems that only appear in unusual window configurations. SUSATest (the autonomous QA platform offered by SUSATest) does exactly this:

  1. Session‑based exploration – After installing an APK or pointing to a web URL, the agent builds a state graph of screens, gestures, and inputs.
  2. Persona‑driven variation – Each virtual user (curious, impatient, novice, etc.) applies its own interaction profile, which includes trying to resize windows, drag split‑screen dividers, and rotate the device.
  3. Automatic window‑size manipulation – The agent issues the same wm resize commands we scripted manually, cycling through a matrix of widths (320 dp, 480 dp, 600 dp, 800 dp) and heights while keeping the app in the foreground.
  4. Signal collection – Logcat, GPU metrics, accessibility events, and uncaught exceptions are streamed back to the service. A spike in frame‑render time or a layout‑inspector mismatch triggers a flag.
  5. Early verdict – If a button becomes invisible or a crash occurs on a specific width, the platform marks the run as *FAIL* for the “Split Screen Resize” scenario and provides a reproducible script (ADB commands + UI actions) that a developer can replay locally.

Because the agent repeats the exploration on every build, regressions are caught before they reach a manual QA cycle. Moreover, the cross‑session memory means the agent remembers which widths caused a layout thrash and focuses subsequent runs on those boundaries, accelerating feedback.

Integrating SUSATest into a CI pipeline is as simple as adding a step:


pip install susatest-agent
susatest run --app-path ./app-release.apk --scenario split-screen-resize --output ./susatest-report.json

The resulting JSON contains a pass/fail flag, a list of observed violations (e.g., “View id=confirm width < 48dp at 400dp window”), and a link to a video of the failing interaction. Teams can treat this as another unit test in their gate, ensuring split‑screen robustness evolves alongside feature work.

Quick Reference Checklist

AreaItemHow to Verify
Manifestandroid:configChanges includes `orientationscreenSizesmallestScreenSizescreenLayout``aapt dump xmltree AndroidManifest.xmlgrep configChanges`
LayoutsNo fixed dp/pt widths > 24dp without weight/%Run custom lint or search layout_width/layout_height for \d+dp
Resource QualifiersWidth‑specific layouts in sw* folders`find res -type f -name "*.xml"grep -E "layout-sw[0-9]+"`
StateonConfigurationChanged calls super and updates UISet breakpoint or log in method
Touch TargetsMinimum 48 dp after resizeAccessibility Scanner on resized build
PerformanceUI thread < 16 ms per resizeSystrace view tag; look for performLayout > 2 ms
VisualNo clipping, overlapping, or missing contentScreenshot diff at each breakpoint (320, 480, 600, 800 dp)
AutomatedCI includes split‑screen resize testVerify test job runs and passes on device farm
MonitoringProduction metrics flag window‑size anomaliesObserve custom metrics (e.g., screen_width_dp histogram) for outliers

Closing Takeaways

Split‑screen bugs are deceptive because they hide behind assumptions that work perfectly in full‑screen but break the moment the system hands the app a new window size. The most effective defense combines three habits:

  1. Design responsively from the start – Use 0dp weights, % dimensions, and smallest‑width qualifiers so layouts fluidly adapt to any width the system may give you.
  2. Validate early and often – Scripted window‑size changes, automated UI tests, and autonomous explorers like SUSATest catch regressions before they reach users.
  3. Instrument and observe – Log window metrics, track layout pass duration, and run accessibility checks on every resize candidate. When something looks off, the data you’ve collected points directly to the offending view or lifecycle method.

By embedding these practices into your development cycle—manifest checks, lint rules, CI tests, and occasional manual spot‑checks—you turn split‑screen from a source of mysterious crashes into a routine, verified part of your app’s supported configurations. The payoff is fewer embarrassing UI glitches in the wild, happier power users who rely on multitasking, and a QA process that spends less time chasing layout ghosts and more time delivering new features.

---

*Feel free to copy the checklist, test snippets, and lint rules into your own repositories. With them in place, your app will stay solid whether the user is watching a video, replying to a message, or juggling two apps side‑by‑side.*

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free