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 UILabel. Este 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