From 6acad54cd44535a5efcb1556a8cce9e73032fb65 Mon Sep 17 00:00:00 2001 From: Vijay Vikram Singh Date: Thu, 27 Aug 2020 08:18:05 -0700 Subject: [PATCH] feat(ios): expose new APIs to customize paging control (#11825) Fixes TIMOB-28012 --- apidoc/Titanium/UI/ScrollableView.yml | 56 +++++++++++++ iphone/Classes/TiUIScrollableView.h | 2 +- iphone/Classes/TiUIScrollableView.m | 25 ++++++ iphone/Classes/TiUIScrollableViewProxy.m | 15 ++++ .../TitaniumKit/Sources/API/TiBlob.m | 1 - tests/Resources/ti.ui.scrollableview.test.js | 81 +++++++++++++++++-- 6 files changed, 173 insertions(+), 7 deletions(-) diff --git a/apidoc/Titanium/UI/ScrollableView.yml b/apidoc/Titanium/UI/ScrollableView.yml index 7cd59edaf6c..343dde76c1e 100644 --- a/apidoc/Titanium/UI/ScrollableView.yml +++ b/apidoc/Titanium/UI/ScrollableView.yml @@ -93,6 +93,24 @@ methods: summary: An integer index or object to set as the current page. type: [Number, Titanium.UI.View] + - name: setIndicatorImageForPage + summary: Sets the indicator image for the specified page. + description: | + Override the indicator image for a specific page. Symbol images are recommended. + Use to get system-supplied symbol images. See examples section for usage. + parameters: + - name: image + summary: | + The image for the indicator, defined using a local filesystem path, + or a `Blob` object containing image data. Resets to the default if image is null. + type: [String, Titanium.Blob] + - name: pageNo + summary: Page number to set the image. + type: Number + since: 9.2.0 + platforms: [iphone, ipad] + osver: {ios: {min: "14.0"}} + events: - name: dblclick summary: Fired when the device detects a double click against the view. @@ -281,6 +299,17 @@ properties: default: "black" platforms: [iphone,ipad] + - name: preferredIndicatorImage + summary: | + The preferred image for indicators, defined using a local filesystem path, or a `Blob` object containing image data. + description: | + Symbol images are recommended. Use to get system-supplied symbol images. + See examples section for usage. + type: [String, Titanium.Blob] + platforms: [iphone,ipad] + since: "9.2.0" + osver: {ios: {min: "14.0"}} + - name: pagingControlHeight summary: Height of the paging control, in pixels. type: Number @@ -490,3 +519,30 @@ examples: ``` + + - title: Scrollable View with indicator image (iOS only) + example: | + Create three views and assign them as pages to a scrollable view. Assign preferred indicator image. + After window opens, update second page indicator image. + + ``` js + var win = Ti.UI.createWindow({ extendSafeArea: false }); + + var view1 = Ti.UI.createView({ backgroundColor:'#123' }); + var view2 = Ti.UI.createView({ backgroundColor:'#246' }); + var view3 = Ti.UI.createView({ backgroundColor:'#48b' }); + var backwardImage = Ti.UI.iOS.systemImage('backward'); + var scrollableView = Ti.UI.createScrollableView({ + views:[view1,view2,view3], + showPagingControl:true, + preferredIndicatorImage: backwardImage + }); + + win.add(scrollableView); + win.addEventListener('open', function () { + var forwardImage = Ti.UI.iOS.systemImage('forward'); + scrollableView.setIndicatorImageForPage(forwardImage, 1); + }); + + win.open(); + ``` diff --git a/iphone/Classes/TiUIScrollableView.h b/iphone/Classes/TiUIScrollableView.h index 1a33b923e5f..7f633406464 100644 --- a/iphone/Classes/TiUIScrollableView.h +++ b/iphone/Classes/TiUIScrollableView.h @@ -49,7 +49,7 @@ - (void)addView:(id)viewproxy; - (void)removeView:(id)args; - (void)refreshScrollView:(CGRect)visibleBounds readd:(BOOL)readd; - +- (void)setIndicatorImage:(UIImage *)image forPage:(NSInteger)page; @end #endif diff --git a/iphone/Classes/TiUIScrollableView.m b/iphone/Classes/TiUIScrollableView.m index 3b0bdd0bd3b..bb1b2b83547 100644 --- a/iphone/Classes/TiUIScrollableView.m +++ b/iphone/Classes/TiUIScrollableView.m @@ -650,6 +650,31 @@ - (void)setPagingControlAlpha_:(id)args } } +#if IS_SDK_IOS_14 +- (void)setPreferredIndicatorImage_:(id)args +{ + if (![TiUtils isIOSVersionOrGreater:@"14.0"]) { + DebugLog(@"[WARN] Supported on iOS 14.0+"); + return; + } + + if (showPageControl) { + [[self pagecontrol] setPreferredIndicatorImage:[TiUtils toImage:args proxy:self.proxy]]; + } +} + +- (void)setIndicatorImage:(UIImage *)image forPage:(NSInteger)page +{ + if (page > [self pagecontrol].numberOfPages) { + DebugLog(@"[WARN] Page no. can not be greater than total no of pages"); + return; + } + if (showPageControl) { + [[self pagecontrol] setIndicatorImage:image forPage:page]; + } +} +#endif + - (void)setPagingControlOnTop_:(id)args { #ifdef TI_USE_AUTOLAYOUT diff --git a/iphone/Classes/TiUIScrollableViewProxy.m b/iphone/Classes/TiUIScrollableViewProxy.m index 32895f35165..5f67a56170e 100644 --- a/iphone/Classes/TiUIScrollableViewProxy.m +++ b/iphone/Classes/TiUIScrollableViewProxy.m @@ -361,6 +361,21 @@ - (void)willChangeLayout [super willChangeLayout]; } +#if IS_SDK_IOS_14 +- (void)setIndicatorImageForPage:(id)args +{ + if (![TiUtils isIOSVersionOrGreater:@"14.0"]) { + DebugLog(@"[WARN] Supported on iOS 14.0+"); + return; + } + ENSURE_ARRAY(args); + ENSURE_ARG_COUNT(args, 2); + ENSURE_TYPE(args[1], NSNumber) + UIImage *image = [TiUtils toImage:args[0] proxy:self]; + NSInteger page = [args[1] integerValue]; + [(TiUIScrollableView *)self.view setIndicatorImage:image forPage:page]; +} +#endif @end #endif diff --git a/iphone/TitaniumKit/TitaniumKit/Sources/API/TiBlob.m b/iphone/TitaniumKit/TitaniumKit/Sources/API/TiBlob.m index 0f7fc34fee8..aa88203b6b6 100644 --- a/iphone/TitaniumKit/TitaniumKit/Sources/API/TiBlob.m +++ b/iphone/TitaniumKit/TitaniumKit/Sources/API/TiBlob.m @@ -11,7 +11,6 @@ #import "UIImage+Alpha.h" #import "UIImage+Resize.h" #import "UIImage+RoundedCorner.h" - //NOTE:FilesystemFile is conditionally compiled based on the filesystem module. #import "TiFilesystemFileProxy.h" diff --git a/tests/Resources/ti.ui.scrollableview.test.js b/tests/Resources/ti.ui.scrollableview.test.js index 4ef795d9a60..7b0fe15fdbb 100644 --- a/tests/Resources/ti.ui.scrollableview.test.js +++ b/tests/Resources/ti.ui.scrollableview.test.js @@ -1,9 +1,10 @@ /* * Appcelerator Titanium Mobile - * Copyright (c) 2011-2018 by Appcelerator, Inc. All Rights Reserved. + * Copyright (c) 2011-Present by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Apache Public License * Please see the LICENSE included with this distribution for details. */ +/* globals OS_VERSION_MAJOR */ /* eslint-env mocha */ /* eslint no-unused-expressions: "off" */ 'use strict'; @@ -27,14 +28,14 @@ describe('Titanium.UI.ScrollableView', function () { } }); - it('apiName', function () { - const scrollableView = Ti.UI.createScrollableView({}); + it('apiName', () => { + const scrollableView = Ti.UI.createScrollableView(); should(scrollableView).have.readOnlyProperty('apiName').which.is.a.String(); should(scrollableView.apiName).be.eql('Ti.UI.ScrollableView'); }); - it('views', function () { - const bar = Ti.UI.createScrollableView({}); + it('views', () => { + const bar = Ti.UI.createScrollableView(); should(bar.views).be.an.Array(); // iOS returns undefined should(bar.getViews).be.a.Function(); should(bar.views).be.empty; @@ -162,4 +163,74 @@ describe('Titanium.UI.ScrollableView', function () { }); win.open(); }); + + it.ios('preferredIndicatorImage', function (finish) { + if (OS_VERSION_MAJOR < 14) { + return finish(); + } + + const view1 = Ti.UI.createView({ id: 'view1', backgroundColor: '#836' }); + const view2 = Ti.UI.createView({ id: 'view2', backgroundColor: '#246' }); + const backwardImage = Ti.UI.iOS.systemImage('backward'); + const forwardImage = Ti.UI.iOS.systemImage('forward'); + const scrollableView = Ti.UI.createScrollableView({ + preferredIndicatorImage: backwardImage, + views: [ view1, view2 ], + showPagingControl: true + }); + + // must set a bg color so don't have full alpha, or else image compare doesn't work as intended + win = Ti.UI.createWindow({ extendSafeArea: false, backgroundColor: 'orange' }); + win.addEventListener('postlayout', function listener () { + win.removeEventListener('postlayout', listener); + try { + const preferredBackwardImage = win.toImage(); + scrollableView.preferredIndicatorImage = forwardImage; + should(win).not.matchImage(preferredBackwardImage, 0); + + scrollableView.preferredIndicatorImage = backwardImage; + should(win).matchImage(preferredBackwardImage, 0); + } catch (error) { + return finish(error); + } + + finish(); + }); + + win.add(scrollableView); + win.open(); + }); + + it.ios('setIndicatorImageForPage', function (finish) { + if (OS_VERSION_MAJOR < 14) { + return finish(); + } + const view1 = Ti.UI.createView({ id: 'view1', backgroundColor: '#836' }); + const view2 = Ti.UI.createView({ id: 'view2', backgroundColor: '#246' }); + const image = Ti.UI.iOS.systemImage('backward'); + const scrollableView = Ti.UI.createScrollableView({ + views: [ view1, view2 ], + showPagingControl: true, + }); + + // must set a bg color so don't have full alpha, or else image compare doesn't work as intended + win = Ti.UI.createWindow({ extendSafeArea: false, backgroundColor: 'orange' }); + win.addEventListener('postlayout', function listener () { + win.removeEventListener('postlayout', listener); + try { + const defaultImage = win.toImage(); + scrollableView.setIndicatorImageForPage(image, 1); + should(win).not.matchImage(defaultImage, 0); + + scrollableView.setIndicatorImageForPage(null, 1); // null will change to default + should(win).matchImage(defaultImage, 0); + } catch (error) { + return finish(error); + } + finish(); + }); + + win.add(scrollableView); + win.open(); + }); });