iOS: Gráfico de barras sencillo en Swift con UIStackViews

Muchas veces tenemos la necesidad de incluir un gráfico sencillo en una aplicación de iOS y nos vamos a librerías de terceros que nos solucionen el problema.

Puede ser que la configuración de la librería te sirva en este caso o que por temas de diseño tengas que hacer modificaciones sobre ella o aplicar varios workaround de configuración para tenerla a punto. Entonces piensas que lleva más tiempo y que solución es más mantenible para mi aplicación.

Esto me sucedió hace poco en un proyecto y me propuse investigar si era muy difícil hacerlo yo mismo para la solución de diseño que necesitaba. Y voilá usando objetos UIStackView pude generarlo en poco tiempo.

¿Que es un UIStackView?

Es una interfaz optimizada para mostrar una colección de vistas en una columna o en una fila. Para el caso que nos ocupa la manera más fácil de verlo es que cada barra de un gráfico es un objeto UIView. Si podemos añadir una colección de estos objetos a un UIStackView en disposición de fila con los elementos con una separación equivalente ya tenemos un gráfico simple.

Nuestro ejemplo: Running App

 

Para el objeto de este ejemplo vamos a representar en un gráfico el tiempo realizado haciendo running en los últimos seis meses. Este gráfico se podría utilizar en la sección de estadísticas de una app de entrenamiento.

Vamos a crear un gráfico de barras sencillo como el de la imagen, con una leyenda inferior y un indicador de valor en cada barra.

Realmente este gráfico lo forman dos objetos tipo UIStackView que son contenedores a los que se les aplican ciertas políticas. En nuestro caso van a ser muy similares. El stack1, que llamaremos GraphStackView, va a contener los gráficos y el stack 2, que llamaremos IndexStackView, va a contener las UILabels que serán la leyenda inferior.

El GraphStackView que va a contener los gráficos tiene la peculiaridad que a su vez cada elemento será un stack vertical que va a contener una UIView de un color y una UILabel que indica el tiempo.

Tanto el GraphStackView y el IndexStackView los vamos a definir en el Interface Builder y los stacks contenidos dentro del GraphStackView los generamos de manera programática.

Comenzamos con el Interface Builder

En una vista de un Storyboard vamos a crear dos  objetos de tipo UIStackView horizontales.

El IndexGraphView va a ser un UIStackView de disposición horizontal, con una separación entre elementos de 20px y una distribución equitativa, lo que quiere decir que todos los elementos tendrán el mismo ancho.

 

El GraphStackView tiene las mismas propiedades que el IndexStackView aunque con una altura mayor.

Tipo de datos

Para representar el tiempo consumido cada mes vamos a utilizar una estructura simple que vamos a llamar: TimeGraphData

struct TimeGraphData {
    var order: Int
    var amount: String
    var month: String
    var percentage: Double
    
    init (order: Int, amount: String, month: String, percentage: Double) {
        self.order = order
        self.amount = amount
        self.month = month
        self.percentage = percentage
    }
}

Cada elemento TimeGraphData va a representar un elemento del gráfico. En el ejemplo final recorreremos una colección de estos objetos e iremos creando con ellos los stacks o elementos de stack correspondientes.

IndexStackView

Como hemos dicho el IndexStackView será el indice de tiempo inferior. Dentro de este stack vamos a añadir cada una de las UILabels que indicarán el nombre del mes.

Este es el código para crear cada elemento UILabel del IndexStackView. Cada UILabel contenido fijara una altura dentro delIndexStackView con la propiedad “heightAnchor”.

private func addIndexElement (timeGraphData: TimeGraphData) {
        let monthLabelHeight: CGFloat = 13.0
        
        let monthLabel = UILabel()
        monthLabel.text = timeGraphData.month
        monthLabel.font = UIFont.systemFont(ofSize: monthLabelHeight)
        monthLabel.textAlignment = .center
       
        monthLabel.heightAnchor.constraint(equalToConstant: monthLabelHeight).isActive = true
        
        indexStackView.addArrangedSubview(monthLabel)
        indexStackView.translatesAutoresizingMaskIntoConstraints = false;
}

GraphStackView

Crear el contenido de este stack será un poco más complejo que en el IndexStackView. El GraphStackView contendrá cada una de las stacks creados programaticamente que serán un UIView y un UILabelEste Stack se generará con disposición vertical en vez de horizontal.

Este es el código para añadir cada elemento:

private func addGraphElement (timeGraphData: TimeGraphData) {

        let amountLabelFontSize: CGFloat = 9.0
        let amountLabelPadding: CGFloat = 15.0
        let height = heightPixelsDependOfPercentage(percentage: timeGraphData.percentage)
        let totalHeight = height + amountLabelPadding
        
        let verticalStackView: UIStackView = UIStackView()
        verticalStackView.axis = .vertical
        verticalStackView.alignment = .fill
        verticalStackView.distribution = .fill
        verticalStackView.spacing = 8.0
        
        let amountLabel = UILabel()
        amountLabel.text = timeGraphData.amount
        amountLabel.font = UIFont.systemFont(ofSize: amountLabelFontSize)
        amountLabel.textAlignment = .center
        amountLabel.textColor = UIColor.darkText
        amountLabel.adjustsFontSizeToFitWidth = true
        amountLabel.heightAnchor.constraint(equalToConstant: amountLabelFontSize).isActive = true
        
        let view = UIView()
        view.backgroundColor = graphColor
        view.heightAnchor.constraint(equalToConstant: height).isActive = true
        
        verticalStackView.addArrangedSubview(amountLabel)
        verticalStackView.addArrangedSubview(view)
        
        verticalStackView.heightAnchor.constraint(equalToConstant: totalHeight).isActive = true
        verticalStackView.translatesAutoresizingMaskIntoConstraints = false;
        
        graphStackView.addArrangedSubview(verticalStackView)
        graphStackView.translatesAutoresizingMaskIntoConstraints = false;
}

El método “heighPixelDependOfPercentage” nos va a dar la altura del Stack vertical contenido en el “graphStackView”. 

Creamos el “verticalStackView” con disposición vertical, distribución repartida en todo el Stack y un espaciado de 8 pixeles.

Al igual que en el stack anterior tanto la UILabel como la UIView fijaremos la altura dentro del stack usando la propiedad “heightAnchor”.

Descarga el código completo

En este artículo he indicado las partes claves para hacer este gráfico pero aquí tenéis el proyecto completo para que lo podáis ver funcionando con vuestro Xcode.

Conclusión

Con este código de manera sencilla podemos integrar un gráfico en nuestra app sin tener que añadir una librería de terceros. Si le dedicamos un poco de tiempo también lo podemos modificar al gusto o añadir más elementos e incluso hacerlo seleccionable.

Y tú, ¿de que eres más?, de utilizar una librería de terceros o de programar tus propios controles.

 

Artículos relacionados:

Usar Swift en clases Objetive – C

Llega iOS 11, analizamos lo más destacado de la WWDC 2017

Introducción a la programación orientada a protocolos con Swift

Mejora la calidad de tus apps aplicando testing

Deja un comentario

Responsable » Solidgear.
Finalidad » Gestionar los comentarios.
Legitimación » Tu consentimiento.
Destinatarios » Los datos que me facilitas estarán ubicados en los servidores SolidgearGroup dentro de la UE.
Derechos » Podrás ejercer tus derechos, entre otros, a acceder, rectificar, limitar y suprimir tus datos.

¿Necesitas una estimación?

Calcula ahora