SwipeView

API Reference: UI.SwipeView

Basics

SwipeView is a view that holds a page list inside so that users can traverse those pages via swiping over.

Performance on Android

onLoad method of a SwipeView's page is triggered when swipe occurs on Android. On iOS, onLoad method of all pages are triggered at the beginning.

const extend = require("js-base/core/extend");
const Page = require("sf-core/ui/page");
const FlexLayout = require('sf-core/ui/flexlayout');
const SwipeView = require('sf-core/ui/swipeview');
const Label = require('sf-core/ui/label');
const Color = require('sf-core/ui/color');
var Page1 = extend(Page)(
function(_super, params) {
_super(this, params);
this.onLoad = function() {
this.layout.backgroundColor = Color.RED;
}.bind(this);
}
);
var Page2 = extend(Page)(
function(_super, params) {
_super(this, params);
this.onLoad = function() {
this.layout.backgroundColor = Color.YELLOW;
}.bind(this);
}
);
var Page3 = extend(Page)(
function(_super, params) {
_super(this, params);
this.onLoad = function() {
this.layout.backgroundColor = Color.BLUE;
}.bind(this);
}
);
var Page4 = extend(Page)(
function(_super, params) {
_super(this, params);
this.onLoad = function() {
this.layout.backgroundColor = Color.GREEN;
}.bind(this);
}
);
var SwipeViewPage = extend(Page)(
function(_super) {
var self = this;
_super(this);
this.onLoad = function() {
Application.statusBar.visible = false;
self.headerBar.visible = false;
self.layout.flexDirection = FlexLayout.FlexDirection.COLUMN;
self.layout.justifyContent = FlexLayout.JustifyContent.FLEX_START;
self.layout.alignItems = FlexLayout.AlignItems.CENTER;
var swipeView = new SwipeView({
page: self,
width:300, maxHeight:300, marginTop:50,
pages: [Page1, Page2, Page3, Page4],
onStateChanged: function(state) {
if (SwipeView.State.IDLE === state) {
labelState.text = "State: IDLE";
} else {
labelState.text = "State: DRAGGING";
}
}
});
self.layout.addChild(swipeView);
var labelState = new Label({
width:200, height:65,
text: "Waiting for State",
});
self.layout.addChild(labelState);
self.layout.applyLayout();
};
}
);
module.exports = SwipeViewPage;

Swipe Speed

Gesture actions have performed differently in the examples. This is why swiping at left is slow.

Swipe Indicator

It is a very common practice to use page indicators with SwipeView. Indicators might display following feats.

  • Showing active page

  • Press to change active page

  • Animate during the swipe

  • Should not be enabled during the swipe

Consideration

Changing of the pages can be triggered from two different sources:

  1. User page swipe

  2. Indicator press

Each of them affecting each other. In order to prevent recursively circular calls, keeping track of the action is needed. To keep the track, in this example a property to the swipeView is added: indicatorScrollTriggered

Showing active page

SwipeView.onPageScrolled event can be used for indicate which page is active.

With Sliding indicator

In this case, as the swipe action is taken by the user, the indicator bar will slide accordingly following the finger movement.

Showing Active Page
Showing Active Page
swipeView.onPageScrolled = function onPageScrolledHandler(index, offset){
if(swipeView.indicatorScrollTriggered)
return; //prevent circular call
var left = ((swipeView.width/PAGE_COUNT) * index) + offset/PAGE_COUNT;
if(indicator.left != left){
indicator.left = left;
page.layout.applyLayout();
}
};

Indicator dot

In this case, dots are used to indicate the active page. Dot style will be changed as the page is changed.

Dots Indicator
Dots Indicator
const pushClassNames = require("@smartface/contx/lib/styling/action/pushClassNames");
const removeClassName = require("@smartface/contx/lib/styling/action/removeClassName");
swipeView.onPageScrolled = function onPageScrolledHandler(index, offset){
indicators.forEach(indicator, indicatorIndex) => {
if(indicatorIndex === index)
indicator.dispatch(pushClassNames(ACTIVE_CLASS_NAME));
else
indicator.dispatch(removeClassName(ACTIVE_CLASS_NAME));
});
};

Changing the active swipe page

The user can press the indicators or buttons to set the "active" of the swipe page. In that case, if a sliding indicator is used, animation of the indicator has to be taken care of with checking the circular check.

Setting the Active Page
Setting the Active Page
const Animator = require('sf-core/ui/animator');
const System = require('sf-core/device/system');
swipeHeaderButton1.onPress = setActivePage.bind(page, 0);
swipeHeaderButton2.onPress = setActivePage.bind(page, 1);
swipeHeaderButton3.onPress = setActivePage.bind(page, 2);
function setActivePage(index) {
var left = ((swipeView.width/PAGE_COUNT) * index)/PAGE_COUNT;
var animationRootView = System.OS === "iOS" ? page.layout : indicator.getParent;
if(indicator.left != left){
indicatorScrollTriggered = true;
Animator.animate(animationRootView, 500, ()=> {
indicator.left = left;
}).complete(()=>{indicatorScrollTriggered = false;});
}
}

Touch Management during changes

Touches can be managed via SwipeView. onStateChanged event

Touch Management
Touch Management
swipeView.onStateChanged = function(state) {
if (SwipeView.State.IDLE === state) {
swipeHeaderButton1.touchEnabled = swipeHeaderButton2.touchEnabled = swipeHeaderButton3.touchEnabled = true;
} else {
swipeHeaderButton1.touchEnabled = swipeHeaderButton2.touchEnabled = swipeHeaderButton3.touchEnabled = false;
}
};

Data Binding

SwipeView is using page classes to display the content. In that sense, if the pages are similar, the developer does not want to create duplicate pages. For that approach using extended pages is recommended.

Extending Swipe Page
Swipe Page Template
Extending Swipe Page
const extend = require("js-base/core/extend");
const SwipeView = require('sf-core/ui/swipeview');
const SwipePageTemplate = require("swipePageTemplate"); //This page might be designed via UI editor
function SwipePageFactory(index) {
const SwipePage = extend(SwipePageTemplate)(
function(_super, params) {
_super(this, params);
this.pageIndex = index;
}
);
return SwipePage;
}
function onLoad(superOnLoad) {
superOnLoad();
const page = this;
var swipeView = new SwipeView({
page,
maxHeight: 300,
marginTop: 10,
pages: [SwipePageFactory(0), SwipePageFactory(1),
SwipePageFactory(2), SwipePageFactory(3)]
});
page.layout.addChild(swipeView);
}
Swipe Page Template
const PgSwipePageTemplateDesign = require('ui/ui_pgSwipePageTemplateDesign');
const extend = require("js-base/core/extend");
const PgSwipe = extend(PgSwipeDesign)(
function(_super) {
_super(this);
this.onShow = onShow.bind(this, this.onShow.bind(this));
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
});
function onShow(superOnShow) {
superOnShow();
//This event is fired evertime page is shown, when page swipe index changes
//Do dynamic data binding here
//Android onShow is triggered after change animation is finished, iOS is fired before. For that reason the content on swipePage might updated later in Android
bindDataToPage(this, this.pageIndex); //pageIndex is given when this page is extended
}
function onLoad(superOnLoad) {
superOnLoad();
//This event is fired once when page is loaded to swipeView
//Do static data binding here
bindDataToPage(this, this.pageIndex); //pageIndex is given when this page is extended
}

Pushing data to the swipe page from outside

There is no interaction or relation between the swipe page instances and the swipeView. If a data updates on the main page, swipe pages can be via several ways

Update via shared state management

A separate state module JS code can be created. We recommend using redux for state management. Both the main page and the swipe pages are going to subscribe to same state manager. This is helpful when the main page is a singleton and no other routes are created for it.

Main Page
SwipePage 1
SwipePage 2
Main Page
const PgMainPageDesign = require('ui/ui_pgMainPageDesign');
const stateStore = require("stateStore");
const extend = require("js-base/core/extend");
const SwipeView = require('sf-core/ui/swipeview');
const SwipePage1 = require("swipePage1");
const SwipePage2 = require("swipePage2");
const SwipePage3 = require("swipePage3");
const PgMain = extend(PgMainPageDesign)(
function(_super) {
_super(this);
this.onShow = onShow.bind(this, this.onShow.bind(this));
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
});
function onShow(superOnShow) {
superOnShow();
}
function onLoad(superOnLoad) {
superOnLoad();
const page = this;
var swipeView = new SwipeView({
page,
pages: [SwipePage1, SwipePage2, SwipePage3]
});
page.layout.addChild(swipeView);
stateStore.subscribe(function(action) {
var state = stateStore.getState();
// Do logic
});
page.btn.onPress = () => {
state.dispatch({
type: "Update From Main",
action: "logic"
};
}
SwipePage 1
const extend = require("js-base/core/extend");
const Page = require("sf-core/ui/page");
const Color = require('sf-core/ui/color');
const stateStore = require("stateStore");
const SwipePage1 = extend(Page)(
function(_super, params) {
_super(this, params);
this.onLoad = function() {
this.layout.backgroundColor = Color.RED;
stateStore.subscribe(function(action) {
var state = stateStore.getState();
// Do logic
});
}.bind(this);
}
);
module.exports = exports = SwipePage1;
SwipePage 2
const extend = require("js-base/core/extend");
const Page = require("sf-core/ui/page");
const Color = require('sf-core/ui/color');
const stateStore = require("stateStore");
const Button = require('sf-core/ui/button');
const SwipePage2 = extend(Page)(
function(_super, params) {
_super(this, params);
this.onLoad = function() {
this.layout.backgroundColor = Color.YELLOW;
stateStore.subscribe(function(action) {
var state = stateStore.getState();
// Do logic
});
this.layout.add(new Button({
text: "UpdateState",
onPress: ()=> {
state.dispatch({
type: "Update From Main",
action: "logic"
};
}
});
}.bind(this);
}
);
module.exports = exports = SwipePage2;

Pass the main page instance

This is quite similar to passing the index to the swipe page. In this case, in addition to the index, main page instance is passed too.

Main Page
Swipe Page Template
Main Page
const PgMainPageDesign = require('ui/ui_pgMainPageDesign');
const extend = require("js-base/core/extend");
const SwipeView = require('sf-core/ui/swipeview');
const SwipePageTemplate = require("swipePageTemplate"); //This page might be designed via UI editor
function SwipePageFactory(index) {
const mainPage = this;
const SwipePage = extend(SwipePageTemplate)(
function(_super, params) {
_super(this, params);
this.pageIndex = index;
this.mainPage = mainPage;
}
);
return SwipePage;
}
const PgMain = extend(PgMainPageDesign)(
function(_super) {
_super(this);
this.onShow = onShow.bind(this, this.onShow.bind(this));
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
this.swipePages = []; //Swipe Pages will register them selves to this property. Later they can be accessed using this.
});
function onShow(superOnShow) {
superOnShow();
}
function onLoad(superOnLoad) {
superOnLoad();
const page = this;
const modifiedSwipePageFactory = SwipePageFactory.bind(this);
var swipeView = new SwipeView({
page,
maxHeight: 300,
marginTop: 10,
pages: [modifiedSwipePageFactory(0), modifiedSwipePageFactory(1),
modifiedSwipePageFactory(2), modifiedSwipePageFactory(3)]
});
page.layout.addChild(swipeView);
}
Swipe Page Template
const PgSwipePageTemplateDesign = require('ui/ui_pgSwipePageTemplateDesign');
const extend = require("js-base/core/extend");
const PgSwipe = extend(PgSwipeDesign)(
function(_super) {
_super(this);
this.onShow = onShow.bind(this, this.onShow.bind(this));
this.onLoad = onLoad.bind(this, this.onLoad.bind(this));
});
function onShow(superOnShow) {
superOnShow();
}
function onLoad(superOnLoad) {
superOnLoad();
this.mainPage.swipePages[this.pageIndex] = this; //Registers it self to the Main Page
// main page can be accessed using this.mainPage
// other swipe pages can be accessed using this.mainPage.swipePages[otherIndex]
}

Performance

SwipeView is a heavy component. It is performing low (especially on Android) when the number swipe pages are high. Try to limit the number of pages below 20, bellow 15 is better. In order to change the number of pages, assignment of the pages property of the swipeView is required. This is causing recreating all of the swipe pages (again).