appcues / appcues-ios-sdk

The Appcues iOS SDK
https://www.appcues.com/mobile
MIT License
8 stars 2 forks source link

Ensure FrameView margins are added to the content size to avoid unnecessary scrolling #476

Closed mmaatttt closed 11 months ago

mmaatttt commented 11 months ago

Margins work properly for the UIKit implementation of embeds, but not SwiftUI, React Native, and Flutter. Why? Each of the broken options wrap the AppcuesFrameView in a UIViewController and set the content size according to the preferredContentSize of that UIViewController.

The margins on an embed were being set by way of constant values on constraints. The AppcuesFrameView had a child view that constrained to be inset by the margins. The issue is that the preferredContentSize that gets reported up the view controller chain doesn't include these margin values which are outside the frame of the UIViewController.view.

So for example, if the preferredContentSize is 300x150 and there's 10px margins, the total frame size should be 320x170, but instead the margins were getting applied inside the 300x150 box, resulting in 280x130 for content, and thus a scrollbar (note the scrollbar on the broken image below).

The solution is to use the directionalLayoutMargins of the AppcuesFrameView which is a property that can then be read by the wrapping UIViewController and added to preferredContentSize to get the final content size.

Fixed Broken
Simulator Screenshot - iPhone 14 Pro - 2023-11-28 at 11 53 49 Simulator Screenshot - iPhone 14 Pro - 2023-11-28 at 11 55 08

Notes

  1. Unfortunately the changes to AppcuesFrame.swift will need to be made to the React Native and Flutter implementations. I looked long and hard for a solution that could be self contained, but that would require wrapping every frame in another UIViewController just to do the logic.
  2. viewRespectsSystemMinimumLayoutMargins is necessary because otherwise I was getting non-zero horizontal margins when none were specified. I initially thought to add this to the AppcuesFrameView just before viewController.embedChildViewController(...), but for the UIKit case where viewController is actually the VC in the customers app and not an VC created by the SDK, that could potentially cause layout issues.