feat(ios): Ti.UI.scrollView contentInsets / scrollIndicatorInsets#14475
Draft
mbender74 wants to merge 50 commits into
Draft
feat(ios): Ti.UI.scrollView contentInsets / scrollIndicatorInsets#14475mbender74 wants to merge 50 commits into
mbender74 wants to merge 50 commits into
Conversation
…View Add iOS-only properties contentInset and scrollIndicatorInsets to Ti.UI.ScrollView proxy. Values are dictionaries with top/left/bottom/right keys, parsed via existing TiUtils contentInsets:. No new dependencies, no categories — direct property getter/setter on the SDK proxy.
The property name uses the plural form 'contentInsets' to match the native UIScrollView API naming convention.
Enhance Ti.UI.ScrollView.contentInsets with optional parameters: - animated: Boolean to enable smooth transitions (default: false) - duration: Animation duration in seconds (default: 0.2s) - safearea: Bottom offset for iOS tab bars (default: 34px) Auto-scrolls to bottom when content exceeds bounds and safe area is configured.
Replace setValue:forKey: with replaceValue:forKey:notification:NO to avoid KVC-triggered setter recursion. The previous implementation caused 47k+ recursive calls and stack overflow crash.
…rollIndicatorInsets iOS automatically adjusts both contentInset and scrollIndicatorInsets based on safe area layout, overriding any manual values. This fix sets: - contentInsetAdjustmentBehavior = Never - automaticallyAdjustsScrollIndicatorInsets = NO BEFORE applying the user-provided inset values.
…illOpen Properties set during initialization are lost because the view is not yet attached. This fix re-applies them after the first layout when safe area insets are calculated.
…orInsets Add detailed documentation for the new iOS-only properties including: - Property description and usage - Option parameters (animated, duration, safearea) - iOS-specific behavior notes (automatic adjustment disabled) - Code examples for animated transitions
Remove incorrect statement that scrollIndicatorInsets must be smaller than contentInsets. Both properties are independent and can be set separately.
Remove nested 'insets' object — animation options (animated, duration, safearea)
are now directly in the dictionary alongside top/left/bottom/right.
Before:
scrollView.contentInsets = { insets: {...}, animated: true, duration: 0.3 };
After:
scrollView.contentInsets = { top: 20, left: 10, bottom: 20, right: 10, animated: true, duration: 0.3 };
This is more consistent with Titanium API conventions and easier to use.
Contributor
Author
|
- Apply safeAreaOffset to insets.bottom in setContentInsets
- Convert duration from ms to seconds in setScrollIndicatorInsets
- Guard setContentInsets and setScrollIndicatorInsets against nil/NSNull
- Remove obsolete setContentInsets:value:options: overload
- Remove redundant else {} blocks
- Add tests for contentInsets and scrollIndicatorInsets properties
The code uses default 0 for safearea offset, not 34. Updated docs to match.
…ontal - verticalScrollIndicatorInsets: left/right position vertical scrollbar - horizontalScrollIndicatorInsets: top/bottom position horizontal scrollbar - Both accept full top/left/bottom/right dictionary - Update apidoc and tests accordingly
Uses tintColor on UIImageView subviews to color both vertical and horizontal scroll indicators. Works on iOS 9+.
…ollIndicatorColor - verticalScrollIndicatorInsets: apply top/bottom/left/right in windowWillOpen - horizontalScrollIndicatorInsets: apply top/bottom/left/right in windowWillOpen - scrollIndicatorColor: recursively search for UIImageView subviews
… only Remove recursive search, only look at direct subviews of UIScrollView
Add NSLog statements to debug why scrollIndicatorColor is not working
The color setter is called before view is attached, so re-apply it after windowWillOpen when the view hierarchy is ready.
…atorInsets properties
- setContentInsets: drop NSDictionary literal that crashed when a partial inset dict (any of top/left/bottom/right missing) was passed; route through [TiUtils contentInsets:] like the other setters. - windowWillOpen: re-apply contentInsets via TiUtils and re-add the saved safearea offset to bottom (previously lost on re-attach); same for vertical/horizontalScrollIndicatorInsets (previously used raw floatValue, inconsistent with the setter). - scrollIndicatorColor getter: return the saved value instead of iterating subviews for UIImageView, which missed the private _UIScrollViewScrollIndicator class on iOS 16+ and returned NSNull. - scrollIndicatorColor setter: resolve via OpaqueColorFromColor which handles non-RGB colorspaces (grayscale via getWhite:alpha:, pattern returned as-is) instead of leaving red/green/blue uninitialized. - scrollIndicatorColor: replace the racy toggle + dispatch_async indicator-recreation with a layoutSubviews hook on TiUIScrollViewImpl that deterministically applies the configured color whenever UIKit lays out (and lazily (re)creates the indicators). Removes applyScrollIndicatorColorToScrollView: from the proxy. - Deduplicate the three inset setters via a shared applyInsetsValue:usingAssignment:safeAreaToBottom: helper.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add contentInsets, verticalScrollIndicatorInsets, horizontalScrollIndicatorInsets, and scrollIndicatorColor properties to Ti.UI.ScrollView (iOS)
Description:
This PR adds native contentInsets, verticalScrollIndicatorInsets, horizontalScrollIndicatorInsets, and scrollIndicatorColor properties to Ti.UI.ScrollView for iOS, eliminating the
need for external modules like TableViewExtension for basic inset management. These properties provide direct control over content positioning, scroll indicator placement, and
indicator coloring within the scroll view, with optional animation support.
Motivation
Previously, developers needed external modules (e.g., TableViewExtension) to set content and scroll indicator insets on scroll views. This PR brings this functionality natively
into the Titanium SDK, providing:
scrollIndicatorColorImplementation Details
Modified Files
iphone/Classes/TiUIScrollViewProxy.m
setContentInsets:,setVerticalScrollIndicatorInsets:,setHorizontalScrollIndicatorInsets:, andsetScrollIndicatorColor:getter/setter methodsreplaceValue:forKey:notification:) to survive initialization before view attachmentwindowWillOpenafter first layout (safe area insets are calculated at that point)contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNeverautomaticallyAdjustsScrollIndicatorInsets = NOanimated,duration(ms, default 300),safearea(contentInsets only, default 0)verticalScrollIndicatorInsetsandhorizontalScrollIndicatorInsetsfor independent controlscrollIndicatorColor(alpha values are ignored)scrollViewDidScroll:apidoc/Titanium/UI/ScrollView.yml
verticalScrollIndicatorInsets(left/right) andhorizontalScrollIndicatorInsets(top/bottom) are independentscrollIndicatorColor(only slight tinting due to private_UIScrollViewScrollIndicatorclass)Key Design Decisions
Usage Examples
Technical Notes
Initialization Timing
Properties set during _initWithProperties: (before the view is attached to a window) are stored via KVC and re-applied in windowWillOpen after the first layout. This ensures safe
area insets are calculated correctly.
Automatic Inset Adjustment
iOS automatically adjusts content and scroll indicator insets based on safe area layout. To allow manual control:
scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
scrollView.automaticallyAdjustsScrollIndicatorInsets = NO;
These are set BEFORE applying manual values to prevent iOS from overriding them.
Animation Implementation
if (animated && duration > 0) {
[UIView animateWithDuration:duration / 1000.0 animations:updateInsets];
} else {
updateInsets();
}
Duration is specified in milliseconds by the API but converted to seconds for UIView animateWithDuration:.
Getter Behavior
When the view is not attached or insets haven't been set, getters return a dictionary with zero values:
if ([self viewAttached]) {
UIEdgeInsets insets = [(TiUIScrollView *)[self view] scrollView].contentInset;
return @{
@"top": @(insets.top),
@"left": @(insets.left),
@"bottom": @(insets.bottom),
@"right": @(insets.right)
};
}
return [NSNull null];
}
Scroll Indicator Color Implementation
On iOS <16, the scroll indicator is a UIImageView and can be tinted via tintColor and imageWithRenderingMode(AlwaysTemplate).
On iOS 16+, the scroll indicator is the private _UIScrollViewScrollIndicator class. The color is applied via backgroundColor and layer.backgroundColor. Due to the private class's
internal rendering, the color may only slightly tint the indicator.
The scrollIndicatorColor property forces full opacity (alpha values are ignored) to ensure visibility.
Performance Optimizations
Testing
Verified with iOS simulator (iPhone 17 Pro Max, iOS 26.2):
Without:

With contentInsets:

With scrollIndicatorInsets:

Android PR is already finished, will follow up after PR is accepted