Ti Highlight Label: An AttributedString widget

Titanium 3.2 introduces Attributed String, an API for creating rich text labels in iOS with different styles in the same label, something long asked for by the community. While some modules exist around there with basic html and styles support, this API is officially supported in the Titanium SDK and its performance really deserves to use it.

This article discusses a basic label component with highlighting capability: it will highlight any word or sentence contained in its text as we type them in a searchBar. You can download this demo video.

The component, called TiHighlightLabel, is implemented as an Alloy Widget for easy reuse.

The Widget

Our widget consists on a Label defined in the widget.xml view file and enhanced with the AttributedString API in the controller file.

The first thing to do in the controller is to pass the arguments to the Label, so it can be used as a common Ti Label from the Project code.

var args = arguments[0]||{};

$.label.applyProperties(args);

Our label has an highlight() public method and a couple of private methods cleanLabel() – to clean all the label styles when there are no matches or there’s no text to search – and findMatches() to find all the positions where the match occurs. And that’s all.

//public methods
$.highlight = function(match){
    
    var match = (match || '').toUpperCase();
    var text = ($.label.text || '').toUpperCase();
    var positions = [], machLength = 0, attributes = [];
    
    if(text.indexOf(match) == -1 || !match){    //no results or empty string?
        cleanLabel();
        return;
    }
    
    positions = findMatches(match, text);
    matchLength = match.length;

    for(var i = 0, j = positions.length; i < j; i++){
        
        //background color
        attributes.push( {
            type: Titanium.UI.iOS.ATTRIBUTE_BACKGROUND_COLOR,
            value: $.label.highlight.backgroundColor || 'yellow',
            range: [positions[i], matchLength]
        });
                
        //font
        attributes.push( {
            type: Titanium.UI.iOS.ATTRIBUTE_FONT,
            value: $.label.highlight.font || {fontWeight:'bold'},
            range: [positions[i], matchLength]
        }); 

    }

    //initialize it everytime because text property might change
    var formater = Titanium.UI.iOS.createAttributedString({text:$.label.text, attributes:attributes});

    $.label.attributedString = formater;

};

The style file widget.tss contains some basic properties that can be overwrite from the Project and defines some default values for a custom property I called ‘highlight’ that defines how the highlights will look by default.

"Label": {
    color: '#000',
    font: {
        fontSize: 18
    },
    height: Ti.UI.SIZE,
    width: Ti.UI.SIZE,
    highlight:{     //set default values to avoid undefined properties
        color:'#00f',
        backgroundColor:'yellow',
        font: {
            fontSize: 18
        },
    },
}

AttributedString

createAttributedString method receives the text contained in the label and an array of attributes to format our text. It’s worth noting that we can change the attributes later after creating the AttributeString object, BUT the text property must be set in its initialization and can’t be changed later.

For this reason, and for simplicity in this article, I create a new AttributeString everytime the highlight method is called, but if you are worried by performance in long texts you may prefer to have a global (in the widget scope) AttributeString object that only changes its attributes when the highlight method is called and a public setter to change the label text and initialize the AttributeString with the new text every time it changes. In my example, as it is initialized everytime highlight() is called, you could change the label text directly accessing the label property and the widget will keep working.

In this example, I just support background color and font properties as styles to customize, but have a look to the ATTRIBUTE_* properties in the Ti.UI.iOS API to see more options.

The Project

The project code is quite self explanatory. You could set the text label in the index.tss style file, but as I wanted to use a long text I read it from a text file included. In order to set the text from the controller, I first access to the widget view (which returns the label component) and then I set its text:

function initLabel(){
    var file = Ti.Filesystem.getFile(Ti.Filesystem.resourcesDirectory, 'data/demo.txt');
    
    //text property may be initialized in index.tss, but from code we need to access to the view property
    //or implement a setter for it. 
    $.hlabel.getView().text = file.read().text; 
}

You may prefer to create a public setter method in the widget to set the text, as I explained before.

The index.tss initializes the widget with some basic styles for the default text and the highlighted text. As I said before, I just supported backgroundColor and font properties, but you may want to add support for more styles.

"#hlabel":{
    left:20,
    right:20,
    top:20,
    font:{
        fontFamily:'HelveticaNeue',
        fontSize:12
    },
    color:'#333',
    highlight:{
        color:'#00f',
        backgroundColor:'yellow',
        font:{
            fontFamily:'HelveticaNeue-Bold',
            fontSize:12
        },
    }
}

Conclusion & download

This basic example show how easy is to create a wrapper to enhance the default behaviour of the Ti Label component. From this point, many other components could be created, as an html/xml wrapper or anything you may imagine. Enjoy!

Download the project in github

Javier is a freelance Mobile Developer working full time with Titanium since 2010 under the commercial brand Criteria Studio. Is TCAD and TCD Certified developer and uses extensively all the Appcelerator technologies, including Titanium, Alloy and ACS. Collaborates in international teams developing enterprise and public apps for Android and iOS. He lives in Madrid (Spain)


Comments