Guardar imágenes en la galería de Android




Si quieres que las fotos o vídeos de tu aplicación aparezcan en la galería de Android, copia y pega el siguiente código:


 
/**
 * Save image to gallery
 */
 public static void SaveToGallery(String path, Context context)
 {
    ContentValues v = new ContentValues();
    v.put(Images.Media.TITLE, "MIFOTO");
    v.put(Images.Media.DISPLAY_NAME, "MIFOTO.jpg");
    v.put(Images.Media.DESCRIPTION, "Una foto de mi app");
    v.put(Images.Media.MIME_TYPE, "image/*");
    v.put(Images.Media.ORIENTATION, 0);

    File f = new File(path);
    File parent = f.getParentFile();
    String fpath = parent.toString().toLowerCase(Locale.getDefault());
    String name = parent.getName().toLowerCase(Locale.getDefault());
    v.put(Images.ImageColumns.BUCKET_ID, fpath.hashCode());
    v.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, name);
    v.put(Images.Media.SIZE, f.length());
    f = null;

    v.put("_data", path);
    ContentResolver c = context.getContentResolver();
    c.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, v);
}

Después, sólo hay que pedirle al sistema que refresque el contenido multimedia:


// update gallery
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, 
   Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
Leer más >>

XCode: 4 formas de ordenar un NSArray


Tenemos un NSArray o un NSMutableArray que queremos ordenar. Lo primero es conocer NSComparisonResult, es un "enum" que indica si el resultado de una comparación entre dos elementos es ascendente (a < b), descendente (a > b) o ambos son iguales (a == b). Hay varias clases de uso común que la implementan (p.ej. NSString). 

Supongamos que queremos ordenar un "array" de personas por su nombre:

@interface Persona

@property NSString *nombre;

@end

// Y luego tenemos declarado
@property NSArray *personas

Hay varias formas de hacerlo:

-(NSComparisonResult)compare:

- (NSComparisonResult)compare:(Persona *)other {
  
  return [self.nombre compare:other.nombre];
}

// Y luego en otro sitio
NSArray *sortedArray;
sortedArray = [originalArray sortedArrayUsingSelector:@selector(compare:)];

NSSortDescriptor

// Y luego en otro sitio
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"nombre" ascending:YES];

NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

NSArray *sortedArray = [personas sortedArrayUsingDescriptors:sortDescriptors];

Blocks

NSArray *sortedArray;
sortedArray = [personas sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
    NSString *nombreA = [(Persona*)a nombre];
    NSString *nombreB = [(Persona*)b nombre];
    return [nombreA compare:nombreB];
}];

sortUsingFunction:context:

Este metodo sólo es válido para NSMutableArray's, pero tiene la ventaja de que en el context le podemos enviar el parámetro que queramos (útil por ejemplo para ordenar objetos geolocalizados).

NSComparisonResult compare(Persona *a, Persona *b, void *context) {
  return [a.nombre compare:b.nombre];
}

// Y luego en otra parte
NSMutableArray *sortedArray = [NSMutableArray arrayWithArray:personas];
[sortedArray sortUsingFunction:compare context:nil];
Leer más >>

XCode: SQLite en 5 minutos



Esta es la continuación de éste otro post de introducción a SQLite. En esta ocasión vamos a utilizar la base de datos creada en una App para iPhone.

Aún no tengo ningún post de como crear una App en iOS desde cero, pero si os interesa dejad un comentario. Así que vamos a suponer que sabéis como crear un simple proyecto. Además vamos a utilizar ARC, que para quien no lo sepa, en pocas palabras significa que nos podemos olvidar de los "alloc" y los "dealloc" de Objective-C. Si no tocáis ningún ajuste y utilizáis una versión medianamente moderna de XCode, los proyectos utilizan ARC por defecto.

Crear el proyecto

Creamos un nuevo proyecto en XCode que podemos llamar Highscores. Vamos a crear un proyecto con una sola vista y una tabla para mostrar los datos.




Añadimos en "Frameworks" la librería "libsqlite3.dylib".

Modelo

Creamos algunas entradas más en el archivo de base de datos del post anterior ("ABC.sqlite") y lo añadimos al proyecto (clic derecho y "Add Files to X" dejando marcada la casilla de "Copy items into destination group..."):



Creamos la clase "Puntuacion" donde guardaremos los datos. Con ARC y las últimas versiones de XCode y de iOS, no hace falta que toquemos el ".m"para añadir las sentencias "@synthesize" ya que se generan de forma automática. Añadimos a Puntuacion.h:

#import <Foundation/Foundation.h>

@interface Puntuacion : NSObject

@property NSNumber *mode;
@property NSString *name;
@property NSNumber *value; @end

Creamos la clase "BaseDeDatos". Esta clase será un Singleton que nos permitirá comunicarnos con la base de datos desde cualquier punto de la App. El archivo "BaseDeDatos.h" debería de quedar tal que así:

#import <Foundation/Foundation.h>
#import <sqlite3.h>

@interface BaseDeDatos : NSObject

+(id)sharedInstance;

-(NSArray*)puntuacionesOrdenadasAscendentemente;
-(NSArray*)puntuacionesOrdenadasPorNombre;
-(NSArray*)puntuacionesOrdenadasPorModo;

@end

Y "BaseDeDatos.m" tal que así:

#import "BaseDeDatos.h"

#import "Puntuacion.h"

@interface BaseDeDatos()

@property sqlite3 *database;

-(NSArray*)performQuery:(NSString*)query;

@end

@implementation BaseDeDatos

static BaseDeDatos *_sharedInstance;

+(id)sharedInstance {
  if (_sharedInstance == nil) {
    _sharedInstance = [[BaseDeDatos alloc] init];
  }
  return _sharedInstance;
}

- (id)init {
  if ((self = [super init])) {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"ABC"
                                                         ofType:@"sqlite"];
    
    if (sqlite3_open([path UTF8String], &_database) != SQLITE_OK) {
      NSLog(@"¡Error al abrir la base de datos!");
    }
  }
  
  return self;
}

-(NSArray*)performQuery:(NSString*)query {
  NSMutableArray *values = [NSMutableArray array];
  
  sqlite3_stmt *statement;
  if (sqlite3_prepare_v2(_database, [query UTF8String], -1, &statement, nil)
      == SQLITE_OK) {
    while (sqlite3_step(statement) == SQLITE_ROW) {
      
      char *name = (char *) sqlite3_column_text(statement, 0);
      int value = (int) sqlite3_column_int(statement, 1);
      int mode = (int) sqlite3_column_int(statement, 2);
      
      Puntuacion *puntuacion = [[Puntuacion alloc] init];
      puntuacion.name = [NSString stringWithUTF8String:name];
      puntuacion.value = @(value);
      puntuacion.mode = @(mode);
      
      [values addObject:puntuacion];
    }
    
    sqlite3_finalize(statement);
  }
  
  return values;
}

-(NSArray*)puntuacionesOrdenadasAscendentemente {
  return [self performQuery:@"select * from puntuaciones order by puntuacion asc"];
}

-(NSArray*)puntuacionesOrdenadasPorNombre  {
  return [self performQuery:@"select * from puntuaciones order by nombre asc"];
}

-(NSArray*)puntuacionesOrdenadasPorModo {
  return [self performQuery:@"select * from puntuaciones order by modo asc"];
}

@end

Vista

Vamos al Storyboard y añadimos un UITableView y tres botones a nuestra vista. La App va a ser muy sencilla, va a mostrar los datos en la tabla y los botones van a servir para ordenarlos según un campo tal y como definimos en la base de datos.





Añadimos el siguiente código a ViewController.h:

#import 

@interface ViewController : UIViewController<UITableViewDataSource>
@property IBOutlet UITableView *tableView;

- (IBAction)orderByName:(id)sender;
- (IBAction)orderByMode:(id)sender;
- (IBAction)orderByScore:(id)sender;

@end



Volvemos a nuestro Storyboard y conectamos los botones con las acciones y la referencia del "tableView" y el datasource:



Controlador

Y por último en el "ViewController.m":

#import "ViewController.h"

#import "BaseDeDatos.h"
#import "Puntuacion.h"

@interface ViewController ()

@property NSArray *puntuaciones;

@end

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];
  self.puntuaciones = [[BaseDeDatos sharedInstance] puntuacionesOrdenadasAscendentemente];
  [self.tableView reloadData];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  if (self.puntuaciones != nil) {
    return self.puntuaciones.count;
  }
  
  return 0;
}

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  Puntuacion *puntuacion = self.puntuaciones[indexPath.row];
  UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"puntuacion"];
  
  cell.textLabel.text = puntuacion.name;
  cell.detailTextLabel.text = [NSString stringWithFormat:@"%@:%@", puntuacion.mode, puntuacion.value];
  
  return cell;
}

- (IBAction)orderByName:(id)sender {
  self.puntuaciones = [[BaseDeDatos sharedInstance] puntuacionesOrdenadasPorNombre];
  [self.tableView reloadData];
}

- (IBAction)orderByMode:(id)sender {
  self.puntuaciones = [[BaseDeDatos sharedInstance] puntuacionesOrdenadasPorModo];
  [self.tableView reloadData];
}

- (IBAction)orderByScore:(id)sender {
  self.puntuaciones = [[BaseDeDatos sharedInstance] puntuacionesOrdenadasAscendentemente];
  [self.tableView reloadData];
}

@end

Resultado 

Y el resultado totalmente funcional (cambia el orden al pulsar en cada botón según el criterio):


Esto evidentemente no es una App, es un punto de partida con algunos de los conceptos básicos de SQLite en iOS. Y ahora, ¡a programar!




Leer más >>

Android + Guava + ProGuard


Si habéis llegado a este post no ha sido por casualidad: ¿Cómo utilizar ProGuard en una App de Android que use Guava? Es un infierno mal documentado que vamos a solucionar en 4+1 pasos:
  1. Descargad la librería javax.inject-*.jar ("*" es la versión, bajaros la última claro está).
  2. Descargad la librería jsr305-*.jar
  3. Copiad las librerías en la carpeta libs de vuestro proyecto.
  4. Añadid lo siguiente a vuestro archivo "proguard.cfg"
  5. -libraryjars libs/guava-14.0.1.jar
    -libraryjars libs/jsr305-2.0.1.jar
    -libraryjars libs/javax.inject-1.jar
    -dontwarn sun.misc.Unsafe
    -dontwarn com.google.common.collect.MinMaxPriorityQueue
    
  6. Dad gracias a MVNRepository por los JARs.
Leer más >>

SQLite en 5 minutos


En este post hablamos de datos y Apps. 

Siempre he pensado que lo más importante en el desarrollo de Apps es el contenido. De nada vale usar las últimas novedades del sistema operativo ni efectos visuales espectaculares si tu App no tiene contenido. Y de igual forma, hay auténticos "truños" (p.ej. las primeras versiones de cierta red social...) cuyas descargas se cuentan por millones, ¿por qué? por que tiene fotos, contactos, mensajes... de millones de usuarios, es decir contenido.

En Software, los contenidos son datos y los datos hay que almacenarlos y gestionarlos. Cuando necesitas almacenar una alta cantidad de datos y acceder a ellos de forma rápida y eficiente tecnologías como SharedPreferences (Android) y NSUserDefaults (iOS) ya no son suficientes. Necesitamos más artillería, y esta lleva el nombre de SQLite. 

Es una tecnología sencilla (los archivos de bases de datos consisten en archivos planos autocontenidos), conocida (un par de nociones de SQL y ya se puede hacer mucho), multiplataforma (la misma base de datos os servirá para Android e iOS) y con las ventajas de trabajar con una base de datos (operaciones simples como buscar, insertar, modificar filas y/o columnas se realizan de forma muy sencilla) y además ofrece una alta capacidad de almacenamiento (cadenas de texto, fechas, números... difícilmente serán un problema).

Como he comentado, una base de datos SQLite consiste en un único archivo autocontenido, es decir toda la información de la base de datos se guarda en ese mismo archivo y no hacen falta archivos adicionales. 

Si trabajáis en Windows tenéis varias posibilidades (en orden): formatear e instalar alguna distro de Linux, instalar cygwin, bajaros el precompilado de SQLite, o utilizar alguna herramienta gráfica de terceros.
En cualquier caso vamos a considerar que tenéis correctamente instalada y configurada la versión 3 de SQLite y un Terminal (OSX), un Shell (Linux) o cualquier tipo de consola de línea de comandos. Es decir podemos ejecutar y obtenemos:

$ sqlite3
SQLite version 3.7.17

Antes que otra cosa, posibles causas de error  y pequeñas recomendaciones:

  • No olvidar el ; (punto y coma) al final de las sentencias.
  • El apóstrofe (') que sale es el que normalmente en un teclado español aparece en la tecla "?" (a la derecha del "0").
  • El prompt ($) puede variar dependiendo de vuestro Shell y sesión. Cuando estáis dentro de la sesión de SQLite el prompt cambia a "sqlite>" (ah, y el prompt es lo que aparece en la línea donde introducís las órdenes).
  • Los datos que devuelve el Shell, están en cursiva para que los distingáis fácilmente.


Vamos a crear una simple base de datos para guardar las puntuaciones de un juego llamado ABC:
  1. Crear base de datos (la extensión no es necesaria pero ayuda a reconocer el tipo de archivo):

  2. $ sqlite3 ABC.sqlite3
    SQLite version 3.7.17
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";" 
    

  3. Creamos la tabla de puntuaciones (cada entrada tendrá un campo para guardar el nombre y otro campo para guardar la puntuación)

  4. sqlite> create table puntuaciones(nombre varchar(10), puntuacion smallint);
    

  5. Insertamos algunas puntuaciones:

  6. sqlite> insert into puntuaciones values('Pepe',10);
    sqlite> insert into puntuaciones values('Juan', 20);
    

  7. Leemos las puntuaciones:

  8. sqlite> select * from puntuaciones;
    Pepe|10
    Juan|20
    

  9. Añadimos un nuevo jugador y leemos las puntuaciones de forma ordenada ("asc" de menor a mayor y "desc" de mayor a menor):

  10. sqlite> insert into puntuaciones values('Eva', 15);
    sqlite> select * from puntuaciones order by puntuacion asc;
    Pepe|10
    Eva|15
    Juan|20
    

  11. Obtenemos la puntuación de un jugador:

  12. sqlite> select puntuacion from puntuaciones where nombre='Eva';
    15
    

  13. Imaginemos que el juego tiene varios modos de dificultad (0,1,2...), creamos una columna para el modo y actualizamos las entradas al modo en el que consiguieron la puntuación:

  14. sqlite> alter table puntuaciones add column modo smallint;
    sqlite> update puntuaciones set modo=0 where nombre='Pepe';
    sqlite> update puntuaciones set modo=2 where nombre='Eva';
    sqlite> update puntuaciones set modo=0 where nombre='Juan';
    sqlite> select * from puntuaciones;
    Pepe|10|0
    Juan|20|0
    Eva|15|2
    

  15. Y finalmente para salir de la sesión:
  16. sqlite> .exit
    

Ya tenéis creada vuestra base de datos con las puntuaciones del juego en el archivo "ABC.sqlite3" (o como lo hayáis nombrado). Evidentemente son ejemplos sencillos fácilmente adaptables a vuestras necesidades.

Más cosas

Tabla con los típicos campos id "autoincrement" y timestamp con la fecha por defecto de inserción:

CREATE TABLE nombre_de_tabla (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  t TIMESTAMP
  DEFAULT CURRENT_TIMESTAMP
);

Ver las tablas que hay en una base de datos:

sqlite> .tables

Obtener información de una tabla (varios métodos):

sqlite> .schema nombre_de_tabla
sqlite> pragma table_info(nombre_de_tabla);  

Borrar una tabla:

sqlite> drop table hombre_de_tabla;      

Supongamos que tenemos la tabla "clientes" (con campos "id" y "nombre") y la tabla "compras" (con campos "cliente_id" y "precio"). Y queremos obtener todos los nombres de clientes que hayan realizado una compra de más de 50$:

sqlite> select X.* from clientes X inner join compras Y on X.id = Y.cliente_id where Y.precio>=50;

SQLite admite múltiples tipos de datos en su sintaxis, pero todos los convierte a alguno de estos tipos básicos según unas reglas definidas.

    • NULL
    • INTEGER
    • REAL
    • TEXT
    • BLOB



Leer más >>

Principios de Diseño Universal



En este post voy a tratar un tema con el que me ha tocado trabajar últimamente. Es el tema de la accesibilidad para los usuarios con algún tipo de discapacidad o handicap en el uso de aplicaciones.

Me parece un tema muy importante, pero que a priori no genera mucho interés entre nuestra comunidad de desarrolladores (me temo que este post no será de los que tengan más visitas :( ). De hecho y sin ir más lejos, el propio autor de este post al principio tenía poco o casi ningún interés. Sin embargo hay cosas técnicamente muy interesantes que se han desarrollado al respecto y a poco que te introduces en este tema, si que te vas interesando.

En otros posts hablaré sobre la accesibilidad en iOS y Android, pero en este os voy a hablar de un documento, poco conocido en español, pero que hemos tenido que utilizar en uno de nuestros últimos proyectos para obtener una certificación de la App que se desarrollaba.

Principios de Diseño Universal

  1. Uso equitativo
    1. Proveer los mismos medios a todos los usuarios: de forma idéntica cuando sea posible; de forma equivalente cuando no.
    2. Evitar segregar o estigmatizar a cualquier usuario.
    3. Lo dispuesto para la privacidad, la seguridad debe garantizar el acceso a todos los usuarios.
    4. Hacer el diseño atractivo para todos los usuarios.

  2. Flexibilidad en el Uso
    1. Proporcionar elección en los métodos de uso.
    2. Acomodar el acceso y el uso diestro y zurdo.
    3. Facilitar la exactitud y la precisión del usuario.
    4. Proporcionar capacidad de adaptación al ritmo del usuario.

  3. Uso Simple e Intuitivo
    1. Eliminar la complejidad innecesaria.
    2. Ser consistente con la intuición y las expectativas del usuario.
    3. Adaptar a una amplia gama de conocimientos y habilidades lingüísticas.
    4. Organizar la información de acuerdo con su importancia.
    5. Ofrecer indicaciones e información relevante durante y después de la finalización de las tareas.

  4. Información Perceptible
    1. Usar diferentes modos (gráficos, verbales, táctiles) para la presentación redundante de información esencial.
    2. Ofrecer un adecuado contraste entre la información esencial y sus alrededores.
    3. Maximizar la "legibilidad" de información esencial.
    4. Diferenciar los elementos de formas que puedan ser descritas (p.ej. facilitar dar instrucciones o direcciones).
    5. Proporcionar compatibilidad con una variedad de técnicas o dispositivos usados por personas con discapacidades sensoriales.

  5. Tolerancia al Error
    1. Organizar los elementos para minimizar los riesgos y errores: los elementos más usados, más accesibles; los elementos peligrosos eliminados, aislados o asegurados.
    2. Proporcionar advertencias de riesgos y errores.
    3. Proporcionar características de seguridad ante fallos.
    4. Desalentar las acciones inconscientes en tareas que requieran vigilancia.

  6. Esfuerzo Físico Bajo
    1. Permitir que el usuario mantenga una posición corporal neutra.
    2. Uso razonable de las fuerzas operativas.
    3. Minimizar las acciones repetitivas.
    4. Minimizar el esfuerzo físico sostenido.

  7. Tamaño y Espacio para el Acceso y el Uso
    1. Proporcionar una línea de visión clara hacia los elementos importantes, para cualquier usuario que se encuentre sentado o de pie.
    2. Facilitar el acceso a todos los componentes para cualquier usuario que se encuentre sentado o de pie.
    3. Adaptarse a las variaciones en la mano y el tamaño de la empuñadura.
    4. Proporcionar un espacio adecuado para el uso de dispositivos de ayuda o asistencia personal.
Documento original: “The Center forUniversal Design (1997). The Principles of Universal Design, Version 2.0. Raleigh,NC: North Carolina State University.”  “Copyright © 1997 NC State University, The Center for Universal Design.” 
Leer más >>

Gadget de entradas con navegador para Blogger



Este gadget es muy práctico. Te permite navegar por todo el blog viendo las entradas con un resumen y una foto sin cambiar de página. El resultado es algo así:



Si lo quieres en tu blog, añade un nuevo gadget  HTML/Javascript desde Diseño.
Copia el siguiente código:


<div class="widget-content">
<style type="text/css">
#mas-terbaru{border:1px solid #585858;width:100%;margin:0 auto}
#terbaru{margin:0px}
.mas-elemen{border:1px solid #ccc;margin:5px 0;padding:5px;height:79px}
.mas-elemen img{background:#999;padding:4px;float:left;height:70px;margin-right:8px;width:70px}
.mas-elemen h6,.mas-elemen h6 a{font-size:12px!important;font-weight:700!important;margin:0;color:#111}
.mas-elemen:hover{background-color:#c3c3c3}
.mas-elemen p{font:14px PT Sans Narrow;text-align:justify;color:#555;line-height:14px;margin:5px 0}
#mas-loading{color:#888;font-family:Tahoma;font-size:100px;letter-spacing:-10px;text-align:center;text-shadow:-5px 0 1px #444;background:#141414 url(http://1.bp.blogspot.com/-vBDcLG_CXzU/UBqEMTknKcI/AAAAAAAAIHE/pZTfBZLbPwY/s1600/loading.gif) no-repeat 50% 50%;height:470px;border:1px solid #c3c3c3}
#mas-navigasifeed{border:1px solid #c3c3c3;color:#bbb;font-family:Verdana;font-size:12px;text-align:center;margin:0px}
#mas-navigasifeed:hover{background-color:#c3c3c3}
#mas-navigasifeed a{color:#141414!important;font-family:Tahoma!important;font-size:12px!important;font-weight:400!important;display:block;padding:5px 10px}
#mas-navigasifeed span{padding:5px 10px}
#mas-navigasifeed .next{float:right}
#mas-navigasifeed .previous{float:left}
#mas-navigasifeed .home{text-align:center}
#mas-navigasifeed a:hover,#mas-navigasifeed span.noactived{color:transparant!important}
</style>
<script type="text/javascript">
//<![CDATA[
var numfeed = 4;
var startfeed = 0;
var urlblog = "http://MIBLOG.blogspot.com/";
var charac = 60;
var urlprevious, urlnext;

function maskolisfeed(johny,banget){
var showfeed = johny.split("<");
for(var i=0;i<showfeed.length;i++){
if(showfeed[i].indexOf(">")!=-1){
showfeed[i] = showfeed[i].substring(showfeed[i].indexOf(">")+1,showfeed[i].length);
}
}
showfeed =  showfeed.join("");
showfeed = showfeed.substring(0,banget-1);
return showfeed;
}
function showterbaru(json) {
var entry, posttitle, posturl, postimg, postcontent;
var showblogfeed = "";
urlprevious = "";
urlnext = "";
for (var k = 0; k < json.feed.link.length; k++) {
if (json.feed.link[k].rel == 'previous') {
urlprevious = json.feed.link[k].href;
}
if (json.feed.link[k].rel == 'next') {
urlnext = json.feed.link[k].href;
}
}
for (var i = 0; i < numfeed; i++) {
if (i == json.feed.entry.length) { break; }
entry = json.feed.entry[i];
posttitle = entry.title.$t;
for (var k = 0; k < entry.link.length; k++) {
if (entry.link[k].rel == 'alternate') {
posturl = entry.link[k].href;
break;
}
}
if ("content" in entry) {
postcontent = entry.content.$t;
} else if ("summary" in entry) {
postcontent = entry.summary.$t;
} else {
postcontent = "";
}
if ("media$thumbnail" in entry) {
postimg = entry.media$thumbnail.url;
} else {
postimg = "http://2.bp.blogspot.com/-uitX7ROPtTU/Tyv-G4NA_uI/AAAAAAAAFBY/NcWLPVnYEnU/s1600/no+image.jpg";
}
showblogfeed += "<div class='mas-elemen'>";
showblogfeed += "<a href='" + posturl + "' target='_blank'><img src='" + postimg + "' /></a>";
showblogfeed += "<h6>
<a href='" + posturl + "'>" + posttitle + "</a></h6>
";
showblogfeed += "<p>
" + maskolisfeed(postcontent,charac) + "...</p>
";
showblogfeed += "</div>
";
}
document.getElementById("terbaru").innerHTML = showblogfeed;
showblogfeed = "";
if(urlprevious) {
showblogfeed += "<a href='javascript:navigasifeed(-1);' class='previous'>&#9668; Previous</a>";
} else {
showblogfeed += "<span class='noactived previous'>&#9668; Previous</span>";
}
if(urlnext) {
showblogfeed += "<a href='javascript:navigasifeed(1);' class='next'>Next &#9658;</a>";
} else {
showblogfeed += "<span class='noactived next'>Next &#9658;</span>";
}
showblogfeed += "<a href='javascript:navigasifeed(0);' class='home'>Home</a>";
document.getElementById("mas-navigasifeed").innerHTML = showblogfeed;
}

function navigasifeed(url){
var p, parameter;
if(url==-1) {
p = urlprevious.indexOf("?");
parameter = urlprevious.substring(p);
} else if (url==1) {
p = urlnext.indexOf("?");
parameter = urlnext.substring(p);
} else {
parameter = "?start-index=1&max-results=" + numfeed + "&orderby=published&alt=json-in-script"
}
parameter += "&callback=showterbaru";
incluirscript(parameter);
}
function incluirscript(parameter) {
if(startfeed==1) {removerscript();}
document.getElementById("terbaru").innerHTML = "<div id='mas-loading'>
</div>
";
document.getElementById("mas-navigasifeed").innerHTML = "";
var archievefeed = urlblog + "/feeds/posts/default"+ parameter;
var terbaru = document.createElement('script');
terbaru.setAttribute('type', 'text/javascript');
terbaru.setAttribute('src', archievefeed);
terbaru.setAttribute('id', 'MASLABEL');
document.getElementsByTagName('head')[0].appendChild(terbaru);
startfeed = 1;
}
function removerscript() {
var elemen = document.getElementById("MASLABEL");
var parent = elemen.parentNode;
parent.removeChild(elemen);
}
onload=function() { navigasifeed(0); }
//]]>
</script>
<br />
<div id="terbaru">
</div>
<div id="mas-navigasifeed">
</div>
</div>

Cambia MIBLOG por el nombre de tu blog.
Puedes cambiar el número de entradas que quieres que aparezca y la longitud del resumen. Vamos, te puedes entretener en personalizar el gadget.

Sólo una advertencia, y es que consume mucho tiempo de carga :( , por eso no lo ves es este blog.
Leer más >>

XCode: UIPageControl


¿Conocéis el típico interfaz de iOS con varios puntitos y que os permite hacer scroll sobre varias vistas? He aquí un tutorial rápido de como conseguirlo:

1. Añadís un UIViewController en vuestro Storyboard y cambiáis el nombre de la clase por PageViewController:


Añadís un UIScrollView, desactiváis las barras de scroll y habilitáis el paging:



Añadís un UIPageControl (y ajustáis el color de los puntos...). Hay que tener cuidado de no añadirlo dentro del UIScrollView, tienen que estar ambos al mismo nivel dentro del UIView:




Creáis un nuevo UIViewController al que llamáis PageViewController:


Añadís el siguiente código a PageViewController.h:


#import 

@interface PageViewController : UIViewController 

@property (strong, nonatomic) IBOutlet UIScrollView* scrollView;
@property (strong, nonatomic) IBOutlet UIPageControl* pageControl;

-(IBAction)pageControlValueChanged:(id)sender;

@end


Añadís el siguiente código a PageViewController.m:

#import "PageViewController.h"

@interface PageViewController ()

@end

@implementation PageViewController

- (void)viewDidLoad
{
  [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  
  int count = 0;
  float width = self.scrollView.frame.size.width;
  float height = self.scrollView.frame.size.height;
  
  NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"PageViews" owner:self options:nil];
  for (UIView *view in nibViews) {
    [view setFrame:CGRectMake(width*count++, 0, width, height)];
    [self.scrollView addSubview:view];
  }
  
  self.scrollView.contentSize = CGSizeMake(width*count, height);
  self.pageControl.currentPage = 0;
  self.pageControl.numberOfPages = count;
}

- (void)didReceiveMemoryWarning
{
  [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(IBAction)pageControlValueChanged:(id)sender {
  
    // First, determine which page is currently visible
  CGFloat pageWidth = self.scrollView.frame.size.width;
  
  self.scrollView.contentOffset = CGPointMake(self.pageControl.currentPage*pageWidth, 0);
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  
    // First, determine which page is currently visible
  CGFloat pageWidth = self.scrollView.frame.size.width;
  NSInteger page = (NSInteger)floor((self.scrollView.contentOffset.x * 2.0f + pageWidth) / (pageWidth * 2.0f));
  
    // Update the page control
  self.pageControl.currentPage = page;
}

@end

Conectáis los IBOutlets, delegate del UIScrollView y el IBAction del UIPageControl en Interface Builder:




Y ahora vamos a añadir las vistas, pero de un modo bastante práctico. Vamos a crear un NIB con todas ellas y se van a cargar automáticamente. Creamos un NIB que llamaremos PageViews:



Y le añadimos todas las vistas que queramos de modo secuencial:


Aconsejo renombrar las vistas (tontería, siempre lo aconsejo, mientras más descriptivo el software mejor) porque en el orden en que aparezcan en la columna de la izquierda (en Objects) es como luego aparecerán.

Y el resultado:


Podéis moveros entre las diferentes vistas con sólo deslizar el dedo y los puntitos se actualizarán solos. Además los puntitos también responden y podéis ir de una a otra pantalla al tocarlos.
Leer más >>