#199 Mobile Devices
- Download:
- source codeProject Files in Zip (198 KB)
- mp4Full Size H.264 Video (22.4 MB)
- m4vSmaller H.264 Video (14.4 MB)
- webmFull Size VP8 Video (38.2 MB)
- ogvFull Size Theora Video (28.5 MB)
Cada vez es más y más frecuente navegar por sitios web utilizando dispositivos móviles, que tienen capacidades de pantalla mucho más limitadas que sus primos los equipos portátiles o de escritorio. Debido a esto es muy importante probar nuestras aplicaciones web en estos dispositivos para comprobar cómo funcionan.
La mejor forma de hacerlo es probar la aplicación en el dispositivo real. Por supuesto, no podemos navegar a "localhost" con un móvil así que tendremos o bien que utilizar la IP de la máquina en la que corre nuestra aplicación o bien su nombre de dominio local, asumiendo que el móvil se encuentra en la misma red local. Por ejemplo, la máquina en la que estamos ejecutando la aplicación que veremos en este episodio se llama noonoo
por lo que podemos visitarla desde cualquier otra máquina de la misma red utilizando http://noonoo.local:3000/
.
Si no tenemos acceso a un dispositivo físico para pruebas podríamos descargar un simulador de este dispositivo. Si queremos testear en un iPhone podemos descargar el simulador que está disponible en el sitio para desarrolladores de Apple. Igualmente existe un emulador disponible como parte del SDK para Palm Pré.
Otra alternativa para hacer pruebas en iPhone es iPhoney. Proporciona una emulación casi perfecta de un iPhone -aunque no tan perfecta como la del simulador de Apple- con la ventaja de que no tenemos que descargar todo el SDK de iPhone para poder utilizarlo. Si lo usamos para probar un sitio web que debe tener un comportamiento diferente para dispositivos móviles tendremos que acordarnos de configurar el User Agent adecuado en el menú de iPhoney para que se muestre el contenido correcto.
Nuestra aplicación en iPhoney.
La apariencia de nuestra aplicación es francamente mejorable en estos dispositivos móviles y durante el resto de este episodio veremos cómo hacerlo.
Para empezar añadiremos al fichero de layout de nuestra aplicación una hoja de estilos que sólo se incluirá si accedemos a la aplicación desde un dispositivo móvil.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title><%= h(yield(:title) || "Untitled") %></title> <%= stylesheet_link_tag 'application' %> <%= stylesheet_link_tag 'mobile' if mobile_device? %> <%= yield(:head) %> </head> <body> <!-- content omitted --> </body> </html>
La nueva hoja de estilos para móviles sólo se incluira si cierto método denominado mobile_device?
devuelve true
. Podríamos haber usado el atributo media
del elemento link
para restringir qué dispositivos podrían hacer uso de esta hoja de estilos, pero los navegadores de móviles a veces interpretan este atributo de diferentes maneras. Si utilizamos nuestro propio método para determinar si incluimos o no esta hoja de estilos podremos controlar qué dispositivos la incluyen simplemente leyendo la cadena de User Agent enviada por el navegador.
Por supuesto nuestra próxima tarea es escribir el método mobile_device?
. Lo pondremos en ApplicationController
para que todos los controladores puedan acceder a este método.
class ApplicationController < ActionController::Base helper :all protect_from_forgery private def mobile_device? request.user_agent =~ /Mobile|webOS/ end helper_method :mobile_device? end
En el método comprobamos la cadena de agente de usuario para ver si contiene o bien la palabra "Mobile" (lo cual detectará dispositivos iPhone y Android) o bien la palabra "webOS" (que detectará dispositivos Palm Pré). Por supuesto podemos personalizar la expresión regular para detectar cualquier otro dispositivo al que le queramos mostrar la hoja de estilos alternativa (existe un listado exhaustivo de cadenas que podemos buscar para detectar otros dispositivos móviles). Por último, marcamos el método como helper para que pueda ser utilizado desde las vistas.
Ahora que hemos escrito este método podemos crear la hoja de estilos que se enviará a los dispositivos móviles.
<p> <% if mobile_device? %> <%= link_to "Full Site", :mobile => 0 %> <% else %> <%= link_to "Mobile Site", :mobile => 1 %> <% end %> </p>
Con esto veremos un enlace a la versión completa del sitio si estamos viendo la versión móvil, y viceversa. Este enlace redirige a la página actual con un parámetro llamado mobile
que determinará qué versión del sitio mostrar.
En nuestro ApplicationController
podemos añadir un before_filter
que establecerá una variable de sesión de forma que una vez que se haya hecho clic en el enlace se siga mostrando la versión escogida al usuario cuando continúe su navegación por nuestro sitio. El método before_filter
establecerá una variable de sesión si aparece el parámetro mobile
en la cadena de la petición. Vamos también a cambiar nuestro método mobile_device?
de forma que compruebe si existe dicha variable de sesión y actúe en consecuencia.
class ApplicationController < ActionController::Base helper :all protect_from_forgery before_filter :prepare_for_mobile private def mobile_device? if session[:mobile_param] session[:mobile_param] == "1" else request.user_agent =~ /Mobile|webOS/ end end helper_method :mobile_device? def prepare_for_mobile session[:mobile_param] = params[:mobile] if params[:mobile] end end
Si ahora recargamos la página veremos que hay un enlace a la versión completa del sitio y si hacemos clic en él veremos la versión completa aún cuando estamos accediendo utilizando un navegador cuya cadena de User Agent contiene la cadena "mobile".
Esta preferencia será persistente de forma que si se sigue cualquier enlace seguiremos estando en la versión completa de la aplicación.
Vistas separadas para dispositivos móviles
Lo que hemos hecho hasta ahora nos funcionará para los casos en los que queramos hacer algunos retoques a la aplicación cuando se vea en dispositivos móviles. Pero, ¿qué pasa si tenemos planes más ambiciosos y lo que queremos es cambiar la aplicación de forma que tenga un aspecto y un comportamiento similares a una aplicación nativa cuando se acceda desde un dispositivo móvil? Para hacerlo tendríamos que cambiar prácticamente cada una de las vistas de nuestra aplicación...
El truco aquí es crear un nuevo tipo MIME para nuestra aplicación, y Rails proporciona el sitio adecuado para hacerlo en el fichero config/initializers/mime_types.rb
. Dicho fichero contiene un ejemplo comentado de cómo proporcionar un nuevo tipo iPhone
en el que nos basaremos para crear uno llamado mobile
, que nos devolverá un formato HTML alternativo para dispositivos móviles.
# Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: # Mime::Type.register "text/richtext", :rtf # Mime::Type.register_alias "text/html", :iphone Mime::Type.register_alias "text/html", :mobile
Aún tenemos que establecer dicho tipo MIME y para hacerlo tenemos que regresar al before_filter
de nuestra aplicación y establecer el formato MIME a :mobile
si hay que mostrar la versión móvil del sitio.
def prepare_for_mobile session[:mobile_param] = params[:mobile] if params[:mobile] format :mobile if mobile_device? end
Una vez que tengamos esto podemos utilizarlo en las acciones de nuestros controladores para cambiar el comportamiento de cada acción utilizando respond_to
, tal y como vemos en la acción index
del controlador de proyectos.
def index @projects = Project.all respond_to do |format| format.html format.mobile end end
Sin embargo, el bloque respond_to
no es necesario si lo que estamos haciendo es únicamente proporcionar una vista alternativa basada en el formato de la petición. En este caso tan sólo tenemos que escribir una nueva plantilla con el nombre del formato donde normalmente aparecería html
Para la vista de índice crearemos un fichero llamado /app/views/projects/index.mobile.erb
y para empezar simplemente escribiremos en él algún texto.
This is a mobile version!
Si visitamos la versión móvil de la página ahora veremos que la vista se está mostrando con tipo MIME mobile
.
Basándonos en esto ahora podemos crear una interfaz de usuario que dará una aspecto mucho más parecido al de una aplicación nativa. Hay un par de librerías que podemos utilizar para hacerlo más fácil, iui y jQTouch, nosotros utilizaremos esta última. jQTouch hace que sea mucho más fácil crear una aplicación web que se comporte como una aplicación iPhone nativa.
Después de descargar y descomprimir jQTouch tendrá una estructura de carpetas como:
Para que nos sea más fácil trabajar con jQTouch vamos a mover las carpetas de temas y extensiones a la carpeta jqtouch
y luego arrastraremos dicha carpeta al directorio public
de nuestra aplicación.
A continuación crearemos un nuevo fichero de layout para la versión móvil de nuestro sitio.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title><%= h(yield(:title) || "Untitled") %></title> <%= stylesheet_link_tag "/jqtouch/jqtouch.min.css", "/jqtouch/themes/apple/theme.min.css" %> <%= javascript_include_tag "/jqtouch/jquery.1.3.2.min.js", "/jqtouch/jqtouch.min.js", "mobile" %> <%= yield(:head) %> </head> <body> <div class="current"> <%- if show_title? -%> <div class="toolbar"> <%= link_to "Back", nil, :class => "back" unless current_page? root_path %> <h1><%=h yield(:title) %></h1> <%= yield(:toolbar) %> </div> <%- end -%> <% unless flash.empty? %> <div class="info"> <%- flash.each do |name, msg| -%> <%= content_tag :div, msg, :id => "flash_#{name}" %> <%- end -%> </div> <% end %> <%= yield %> </div> </body> </html>
En este fichero incluimos referencias a un par de hojas de estilo que proporciona jQTouch así como al JavaScript de jQuery y de la propia jQTouch. También se hace referencia a un archivo mobile.js
que escribiremos más adelante. En el layout el contenido de la página está rodeado de un div
con la clase current
. Si la página debe tener un título lo mostraría rodeado por otro div
con la clase toolbar
. Así mismo si hay cualquier texto informativo que visualizar lo colocaríamos en otro div
esta vez con la clase info
. Después de ésto, simplemente se hace un yield
mostrar lo que venga en la plantilla actual.
A continuación crearemos el nuevo fichero mobile.js
. Lo que tenemos que hacer aquí es invocar al inicializador de jQTouch.
$.jQTouch({});
La función de inicialización recibe un hash de opciones aunque nosotros no configuraremos ninguna. Cuando recarguemos la página móvil de proyectos esta vez veremos que se aplican los estilos de jQTouch, si bien la página tendrá un aspecto poco atractivo porque aún no tenemos ningún contenido en la plantilla de la página.
Si volvemos al código de la vista para móviles de la página podemos reemplazar lo que teníamos con el código necesario para listar todos los proyectos y un contador del número de tareas que tiene cada uno de ellos, así como un enlace para crear un nuevo proyecto.
<% title "Projects" %> <ul> <% for project in @projects %> <li class="arrow"> <%= link_to h(project.name), project %> <small class="counter"><%= project.tasks.size %></small> </li> <% end %> </ul> <ul><li class="arrow"><%= link_to "New Project", new_project_path %></li></ul>
Si recargamos la página otra vez veremos un interfaz totalmente equiparable al de una aplicación nativa.
Obviamente la interfaz será mucho más estrecha cuando se vea en un iPhone de verdad, pero funcionará correctamente en un navegador de escritorio.
Habrá que hacer una versión para móvil de todas y cada una de las vistas de nuestra aplicación. Se trata de demasiado código como para mostrarlo aquí, pero los archivos están disponibles para su descarga en la página de GitHub de Ryan Bates. Una vez que lo hagamos tendremos una aplicación para móvil totalmente funcional que tiene el aspecto de una aplicación nativa.
Si bien hemos mejorado mucho el aspecto de la versión móvil de nuestro sitio hemos perdido la posibilidad de volver a la versión completa. Añadiremos un botón a la derecha de la barra superior de herramientas para volver a tener esta funcionalidad.
La barra de herramientas está definida en el fichero de layout y es muy fácil añadir nuevos controles en ella porque los elementos en jQTouch se definen con etiquetas HTML. Podemos añadir un nuevo botón creando un nuevo enlace dentro de la barra de herramientas con la clase button
.
<div class="toolbar"> <%= link_to "Back", nil, :class => "back" unless current_page? root_path %> <h1><%=h yield(:title) %></h1> <%= link_to "Full Site", root_url(:mobile => 0), :class => "button", :rel => "external" %> <%= yield(:toolbar) %> </div>
También tenemos que proporcionar el atributo rel
con valor external
para que jQTouch trate el enlace como un enlace a otro sitio, si no hiciésemos así lanzaría una petición AJAX, que no es lo que pretendemos.
Si recargamos la página por última vez tendremos un botón en cada página que nos permite volver a la versión completa de nuestra aplicación.