192 lines
7.9 KiB
Swift
192 lines
7.9 KiB
Swift
///
|
|
/// @brief - Class responsible for applying all upcoming updates to SplitView.
|
|
///
|
|
/// This class is synchronizing UISplitViewController configuration props which are affecting the SplitView appearance with props passed to RNSSplitViewHostComponentView from the ElementTree.
|
|
///
|
|
class RNSSplitViewAppearanceApplicator {
|
|
///
|
|
/// @brief Function responsible for applying all updates to SplitView in correct order
|
|
///
|
|
/// It requests calling proper callbacks with batched SplitView updates on the AppearanceCoordinator object
|
|
///
|
|
/// @param splitView The view representing JS component which is sending updates.
|
|
/// @param splitViewController The controller associated with the SplitView component which receives updates and manages the native layer.
|
|
/// @param appearanceCoordinator The coordinator which is checking whether the update needs to be applied and if so, it executes the callback passed by this class.
|
|
///
|
|
public func updateAppearanceIfNeeded(
|
|
_ splitView: RNSSplitViewHostComponentView,
|
|
_ splitViewController: RNSSplitViewHostController,
|
|
_ appearanceCoordinator: RNSSplitViewAppearanceCoordinator
|
|
) {
|
|
appearanceCoordinator.updateIfNeeded(.generalUpdate) { [weak self] in
|
|
guard let self = self
|
|
else {
|
|
return
|
|
}
|
|
|
|
self.updateSplitViewConfiguration(for: splitView, withController: splitViewController)
|
|
}
|
|
|
|
appearanceCoordinator.updateIfNeeded(.secondaryScreenNavBarUpdate) { [weak self] in
|
|
guard let self = self
|
|
else {
|
|
return
|
|
}
|
|
|
|
splitViewController.refreshSecondaryNavBar()
|
|
}
|
|
|
|
appearanceCoordinator.updateIfNeeded(.displayModeUpdate) { [weak self] in
|
|
guard let self = self
|
|
else {
|
|
return
|
|
}
|
|
|
|
self.updateSplitViewDisplayMode(for: splitView, withController: splitViewController)
|
|
}
|
|
|
|
appearanceCoordinator.updateIfNeeded(.orientationUpdate) { [] in
|
|
RNSScreenWindowTraits.enforceDesiredDeviceOrientation()
|
|
}
|
|
}
|
|
|
|
///
|
|
/// @brief Function that applies all basic updates.
|
|
///
|
|
/// It calls all setters on RNSSplitViewHostController that doesn't require any custom logic and conditions to be met.
|
|
///
|
|
/// @param splitView The view representing JS component which is sending updates.
|
|
/// @param splitViewController The controller associated with the SplitView component which receives updates and manages the native layer.
|
|
///
|
|
private func updateSplitViewConfiguration(
|
|
for splitView: RNSSplitViewHostComponentView,
|
|
withController splitViewController: RNSSplitViewHostController
|
|
) {
|
|
// Step 1 - general settings
|
|
splitViewController.displayModeButtonVisibility = splitView.displayModeButtonVisibility
|
|
splitViewController.preferredSplitBehavior = splitView.preferredSplitBehavior
|
|
splitViewController.presentsWithGesture = splitView.presentsWithGesture
|
|
splitViewController.primaryEdge = splitView.primaryEdge
|
|
splitViewController.showsSecondaryOnlyButton = splitView.showSecondaryToggleButton
|
|
|
|
// Step 2.1 - validating column constraints
|
|
validateColumnConstraints(
|
|
minWidth: splitView.minimumPrimaryColumnWidth,
|
|
maxWidth: splitView.maximumPrimaryColumnWidth)
|
|
|
|
validateColumnConstraints(
|
|
minWidth: splitView.minimumSupplementaryColumnWidth,
|
|
maxWidth: splitView.maximumSupplementaryColumnWidth)
|
|
|
|
#if compiler(>=6.2)
|
|
if #available(iOS 26.0, *) {
|
|
validateColumnConstraints(
|
|
minWidth: splitView.minimumInspectorColumnWidth,
|
|
maxWidth: splitView.maximumInspectorColumnWidth)
|
|
}
|
|
#endif
|
|
|
|
// Step 2.2 - applying updates to columns
|
|
if splitView.minimumPrimaryColumnWidth >= 0 {
|
|
splitViewController.minimumPrimaryColumnWidth = splitView.minimumPrimaryColumnWidth
|
|
}
|
|
|
|
if splitView.maximumPrimaryColumnWidth >= 0 {
|
|
splitViewController.maximumPrimaryColumnWidth = splitView.maximumPrimaryColumnWidth
|
|
}
|
|
|
|
if splitView.preferredPrimaryColumnWidthOrFraction >= 0
|
|
&& splitView.preferredPrimaryColumnWidthOrFraction < 1
|
|
{
|
|
splitViewController.preferredPrimaryColumnWidthFraction =
|
|
splitView.preferredPrimaryColumnWidthOrFraction
|
|
} else if splitView.preferredPrimaryColumnWidthOrFraction >= 1 {
|
|
splitViewController.preferredPrimaryColumnWidth =
|
|
splitView.preferredPrimaryColumnWidthOrFraction
|
|
}
|
|
|
|
if splitView.minimumSupplementaryColumnWidth >= 0 {
|
|
splitViewController.minimumSupplementaryColumnWidth =
|
|
splitView.minimumSupplementaryColumnWidth
|
|
}
|
|
|
|
if splitView.maximumSupplementaryColumnWidth >= 0 {
|
|
splitViewController.maximumSupplementaryColumnWidth =
|
|
splitView.maximumSupplementaryColumnWidth
|
|
}
|
|
|
|
if splitView.preferredSupplementaryColumnWidthOrFraction >= 0
|
|
&& splitView.preferredSupplementaryColumnWidthOrFraction < 1
|
|
{
|
|
splitViewController.preferredSupplementaryColumnWidthFraction =
|
|
splitView.preferredSupplementaryColumnWidthOrFraction
|
|
} else if splitView.preferredSupplementaryColumnWidthOrFraction >= 1 {
|
|
splitViewController.preferredSupplementaryColumnWidth =
|
|
splitView.preferredSupplementaryColumnWidthOrFraction
|
|
}
|
|
|
|
#if compiler(>=6.2)
|
|
if #available(iOS 26.0, *) {
|
|
if splitView.minimumSecondaryColumnWidth >= 0 {
|
|
splitViewController.minimumSecondaryColumnWidth = splitView.minimumSecondaryColumnWidth
|
|
}
|
|
|
|
if splitView.preferredSecondaryColumnWidthOrFraction >= 0
|
|
&& splitView.preferredSecondaryColumnWidthOrFraction < 1
|
|
{
|
|
splitViewController.preferredSecondaryColumnWidthFraction =
|
|
splitView.preferredSecondaryColumnWidthOrFraction
|
|
} else if splitView.preferredSecondaryColumnWidthOrFraction >= 1 {
|
|
splitViewController.preferredSecondaryColumnWidth =
|
|
splitView.preferredSecondaryColumnWidthOrFraction
|
|
}
|
|
|
|
if splitView.minimumInspectorColumnWidth >= 0 {
|
|
splitViewController.minimumInspectorColumnWidth = splitView.minimumInspectorColumnWidth
|
|
}
|
|
|
|
if splitView.maximumInspectorColumnWidth >= 0 {
|
|
splitViewController.maximumInspectorColumnWidth = splitView.maximumInspectorColumnWidth
|
|
}
|
|
|
|
if splitView.preferredInspectorColumnWidthOrFraction >= 0
|
|
&& splitView.preferredInspectorColumnWidthOrFraction < 1
|
|
{
|
|
splitViewController.preferredInspectorColumnWidthFraction =
|
|
splitView.preferredInspectorColumnWidthOrFraction
|
|
} else if splitView.preferredInspectorColumnWidthOrFraction >= 1 {
|
|
splitViewController.preferredInspectorColumnWidth =
|
|
splitView.preferredInspectorColumnWidthOrFraction
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Step 2.3 - manipulating with inspector column
|
|
splitViewController.toggleSplitViewInspector(splitView.showInspector)
|
|
}
|
|
|
|
///
|
|
/// @brief Function that updates `preferredDisplayMode` property on SplitView.
|
|
///
|
|
/// `preferredDisplayMode` needs to have a dedicated flag to prevent updates from the JS, when other props updates the appearance.
|
|
/// It is crucial in the case, when `preferredDisplayMode` has changed due to some transition that was executed natively, e. g. after showing/hiding a column by a swipe.
|
|
/// In that case, any prop update incoming, would reset `preferredDisplayMode` to the state from JS, what doesn't look good.
|
|
///
|
|
/// @param splitView The view representing JS component which is sending updates.
|
|
/// @param splitViewController The controller associated with the SplitView component which receives updates and manages the native layer.
|
|
///
|
|
func updateSplitViewDisplayMode(
|
|
for splitView: RNSSplitViewHostComponentView,
|
|
withController splitViewController: RNSSplitViewHostController
|
|
) {
|
|
splitViewController.preferredDisplayMode = splitView.preferredDisplayMode
|
|
}
|
|
|
|
func validateColumnConstraints(minWidth: CGFloat, maxWidth: CGFloat) {
|
|
assert(
|
|
minWidth <= maxWidth,
|
|
"[RNScreens] SplitView column constraints are invalid: minWidth \(minWidth) cannot be greater than maxWidth \(maxWidth)"
|
|
)
|
|
}
|
|
}
|