Same Element in several TabWindows

You must Login before you can answer or comment on any questions.

Hello,

I have several Tabs which all lead to different windows. I want to display one MapView in every tab, but as soon as I switch to an other tab, the map disappears. Is there a way to make an element global for all the tabs?

Best Regards, Martin

6 Answers

Accepted Answer

Hi Martin

Unfortunately no - you need (and should) have one per window given how scope works in the apps.

However - you can make you life much easier by;

You can make the code you use re-usable by wrapping everything in a CommonJS format or a function and by passing in parameters and returning the map view.

By using the CommonJS approach (rather than function) to sections of functionality you have much better memory management and ease of coding.

Hope this helps you.

— answered 10 months ago by Malcolm Hollingsworth
answer permalink
4 Comments
  • Hi Malcolm, I am already using commonjs. I have several functions like a searchfunction or a navigationfunction, which all have their own tab. they are eg producing placemarks on that map and as soon as I switch the tabs and create a new map, the placemarks will disappear... is there no way that when switching the tab, only the controls change that are manipulating the map??

    — commented 10 months ago by Martin Golpashin

  • OK your doing things correctly - that helps as it might actually be a conceptual change rather than a force a solution.

    If you are using the tabs to allow actions on a central map, then you are actually using a solution designed to segregate functionality and visually. This MAY not be the best solutions as the TABs as used in Titanium may be too heavy for your needs.

    What if you had your extra functionality in separate views that could slide (or appear a different way) over the top of the map. You could activate them by your own 'fake' tab buttons but in reality will be a button bar equivalent. This way you only need a single map (as Aaron notes - a restriction in Android) and the user stays in the same place as the map.

    I do this method to swap between a table and map view - both have search boxes that perform the searches specifically to the needs of map or list, they are swapped in and out in the same window.

    — commented 10 months ago by Malcolm Hollingsworth

  • Thanks Malcolm, I'm gonna use this solution if there is no way using tabs with these functionality. I was using the android menubutton to switch between different views, but since there is no equivalent button on iOS, I had to look for an other solution.

    — commented 10 months ago by Martin Golpashin

  • Show 1 more comment

Unless I am missing something your map code is not correctly adding the map to the window in the first place.

//create MapView
Ti.App.map = nfm.createMapView({
    mapType : Titanium.Map.SATELLITE_TYPE,
    region : {
        latitude : 49.293512,
        longitude : 8.642024,
        latitudeDelta : 0.002,
        longitudeDelta : 0.002
    },
    animate : true,
    regionFit : true,
    userLocation : false
});
should be
//create MapView
var mapView = nfm.createMapView({
    mapType : Titanium.Map.SATELLITE_TYPE,
    region : {
        latitude : 49.293512,
        longitude : 8.642024,
        latitudeDelta : 0.002,
        longitudeDelta : 0.002
    },
    animate : true,
    regionFit : true,
    userLocation : false
});
win.add(mapView); //swap win to the window it should appear in .
You should then reference mapView in the future.

You have a number of views that 'pollute' (horrible phrase but correct) the Ti/Titanium name space, your app should have its own.

An example;

var app = {};
app.map = nfm.createMapView({
    mapType : Titanium.Map.SATELLITE_TYPE,
    region : {
        latitude : 49.293512,
        longitude : 8.642024,
        latitudeDelta : 0.002,
        longitudeDelta : 0.002
    },
    animate : true,
    regionFit : true,
    userLocation : false
});
win.add(app.map); //swap win to the window it should appear in .
 
//also
app.someFunction = function (fn, ln) {
  return fn + ' ' + ln;
}
alert(app.someFunction('Some', 'One'));
Helps reduce memory issues and the pollution. Sorry if this last bit taught you to suck eggs, but thought worthwhile pointing out.

— answered 10 months ago by Malcolm Hollingsworth
answer permalink
4 Comments
  • The problem is that I have to access the map via different files. So how can I do this without using Ti.App?

    — commented 10 months ago by Martin Golpashin

  • The solution given by malcolm should fix your problem.

    The var app = {}; is availible throughout your app, if you create if before you load your other files.

    I for one, have create a "ui.js" class which is loaded at the start of the app, using this method you can re-use multiple elements and even acces elements you created before. Using this method your code will be alot cleaner and easier to maintain. ( Thrust me, i have had an app without this method, it is a pain in the ... )

    — commented 10 months ago by Kami -

  • Are you sure about that, Kami? You're putting app into the global namespace. Such variables are not accessible from within CommonJS modules (well, they are in iOS due to a faulty implementation, but they're not in android, and in future versions of Ti, they may not be available in iOS either).

    I guess maybe it doesn't matter if you're not using CommonJS modules, but you probably should be using CommonJS. Certainly anybody who's just getting started and has basic questions should be pushed in the direction of using CommonJS and no globals.

    — commented 10 months ago by Jason Priebe

  • Show 1 more comment

You are heading in the right direction, and you should use commonjs but you dont have to. If you are using Android, please be aware that you can only have one mapView so it must be a global object.

maybe if you provide some code, it might be easier to diagnose your problem?

Here is some of the code. searchRoomWindow is the first active tab and shows the map. as soon as I switch to eg navigationWindow.js, the map disappears...

app.js

Ti.API.info('app.js');
 
var initializer = require('initialize');
var search = require('search');
var database = require('database');
var floorSwitch = require('floorSwitch');
var nfm = require('netfunctional.mapoverlay');
 
//create MapView
Ti.App.map = nfm.createMapView({
    mapType : Titanium.Map.SATELLITE_TYPE,
    region : {
        latitude : 49.293512,
        longitude : 8.642024,
        latitudeDelta : 0.002,
        longitudeDelta : 0.002
    },
    animate : true,
    regionFit : true,
    userLocation : false
});
 
var tabGroup = Ti.UI.createTabGroup();
 
var roomWin = Titanium.UI.createWindow({
    url:'windows/searchRoomWindow.js'
});
var roomTab = Ti.UI.createTab({
    active:true,
    title:'Search Room',
    window: roomWin
})
 
var officeWin = Titanium.UI.createWindow({
    url:'windows/searchOfficeWindow.js'
});
tabGroup.addTab(roomTab);
var officeTab = Ti.UI.createTab({
    title:'Search Office',
    window: officeWin
})
tabGroup.addTab(officeTab);
 
var poiWin = Titanium.UI.createWindow({
    url:'windows/showPoiWindow.js',
});
var poiTab = Ti.UI.createTab({
    title:'Show POIs',
    window: poiWin
})
tabGroup.addTab(poiTab);
 
var navigationWin = Titanium.UI.createWindow({
    url:'windows/navigationWindow.js'
});
var navigationTab = Ti.UI.createTab({
    title:'Navigation',
    window: navigationWin
})
tabGroup.addTab(navigationTab);
 
tabGroup.addEventListener('open',function()
{
    // set background color back to white after tab group transition
    Titanium.UI.setBackgroundColor('#fff');
});
tabGroup.setActiveTab(0);
 
tabGroup.open();
searchRoomWindow.js
var nfm = require("netfunctional.mapoverlay");
var search = require('../search');
 
// add map to the window
Titanium.UI.currentWindow.add(Ti.App.map);
 
Ti.App.searchBar = Ti.UI.createSearchBar({
    barColor : '#000',
    showCancel : false,
    width : '80%',
    height : '100%',
    top : 0,
    left : 0,
    hintText : "e.g. WDF01,CE.10"
})
Ti.App.searchBar.blur();
 
Ti.App.searchButton = Ti.UI.createButton({
    title : 'Go',
    top : 0,
    height : '100%',
    width : '20%',
})
 
var searchView = Ti.UI.createView({
    layout : 'horizontal',
    top : 0,
    width : '100%',
    height : 70,
    left : 0
});
 
searchView.add(Ti.App.searchBar);
searchView.add(Ti.App.searchButton);
 
Titanium.UI.currentWindow.add(searchView);
navigationWindow.js
Ti.API.info('open Tab: Navigation')
var search = require('../search');
var dijkstra = require('../dijkstra');
 
// add map to the window
Titanium.UI.currentWindow.add(Ti.App.map);
 
navigationBarFrom = Ti.UI.createSearchBar({
    barColor : '#000',
    showCancel : false,
    width : '80%',
    height : '50%',
    top : 0,
    left : 0,
    hintText : "From"
})
navigationBarFrom.blur();
 
navigationBarTo = Ti.UI.createSearchBar({
    barColor : '#000',
    showCancel : false,
    width : '80%',
    height : '50%',
    top : '50%',
    left : 0,
    hintText : "To"
})
navigationBarTo.blur();
 
var navigateButton = Ti.UI.createButton({
    title : 'Go',
    top : 0,
    right : 0,
    height : '100%',
    width : '20%',
})
 
var navigationView = Ti.UI.createView({
    top : 0,
    width : '100%',
    height : 140,
});
 
navigationView.add(navigationBarFrom);
navigationView.add(navigationBarTo);
navigationView.add(navigateButton);
 
Titanium.UI.currentWindow.add(navigationView);

You can build a CommonJS module to manage your mapview. Keep in mind that you can only have one mapview per app in android. It's a difficult restriction to live with, but it can be done if you're careful.

So you build a CommonJS class that has a static member variable that is the single instance of the mapview. Then you can add it to views and remove it from views.

Here's just a little to get you started:

MyMapView.js:

var _mapview = null;
 
function MyMapView ()
{
    if (_mapview == null)
    {
        _mapview = Ti.Map.createView ({...});
    }
 
    return _mapview;
}
 
module.exports = MyClass;
Now in your code, any time you need to access the single instance, do this:
var MyMapView = require ('MyMapView');
var mv = new MyMapView();
You'll get back that same single instance everywhere. All without using global variables.

If you're doing it on tabs, it gets trickier, because you have to listen for events when the user changes tabs, and then remove the mapview from the previous tab and put it on the new tab. Again, it's tricky, but it can be done. Use the 'focus' event on the tabgroup to capture this event.

To read more about CommonJS and OO design in Titanium, I recommend these articles

— answered 10 months ago by Jason Priebe
answer permalink
10 Comments
  • Sorry -- the last line in MyMapView.js should be

    module.exports = MyMapView;

    — commented 10 months ago by Jason Priebe

  • thanks for your answer. But I have a problem with the eventlistener. The Event has no property "previousTab" and "tab" like in the documentation. There is only the "source"- Attribute.

    tabGroup.addEventListener('focus', function(e) {
        //remove mapView from former Tab
    })
    Your advice using Singleton-Pattern was a great idea too

    — commented 10 months ago by Martin Golpashin

  • okay I guess that is because the events are partitially fired by the searchbars in a tab. So I have to check, if it is a tab that fired the event

    — commented 10 months ago by Martin Golpashin

  • Show 7 more comments

I tried Jason's solution: So in order to protect the mapView, I wrote an Eventlistener that removes the mapView everytime when a FocusEvent on a Tab gets fired. The Problem is: When switching to another Tab, the content of the Tab loads first, then the event gets fired. I need to switch this sequence...

Your Answer

Think you can help? Login to answer this question!