domingo, 15 de diciembre de 2013

Postal navideña brote brote.

Como última entrada al blog del año, os quiero comentar una de las últimas Apps que he desarrollado para iPhone, brotebroteXmas. Esta App es un complemento del cuento brote brote, el cual ya comentamos en entradas anteriores. Esta aplicación nos permite crear una postal navideña con los personajes del cuento.

Podemos realizar una foto en ese instante y editarla o simplemente tomar una de la galería (en próximos artículos explicaré el funcionamiento del UIImagePickerController). Una vez seleccionado el Travelino con la vestimenta que queramos, y al personaje que queramos sustituir su rostro por el nuestro, podremos compartir con nuestros amigos a través del email o de las redes sociales nuestra postal navideña brote brote. 

Aquí os dejo los enlaces de las Apps en Google Play y en iTunes para su descarga.


Ahora toca recargar pilas y disfrutar de la familia y amigos. Prometo continuar con el blog en 2014. Muchas gracias a todos los que mandáis vuestro apoyo y seguís el blog. Felices fiestas a tod@s.

martes, 5 de noviembre de 2013

URL Schemes

En este artículo voy a escribir acerca de los URL Schemes, y de cómo a partir de unos determinados path de URLs podremos conseguir interactuar con el iPhone desde nuestra App para realizar llamadas telefónicas, enviar un SMS o iMessage, interactuar con mapas, ver un vídeo de youtube, …… y lo que mucha gente desconoce "abrir otra aplicación desde nuestra App".
  1.  Abrir una web desde Safari.
  2.  Abrir el mail.
  3.  Llamar por teléfono.
  4.  Abrir la App de Mapas.
  5.  Abrir iTunes.
  6.  Mandar un SMS.
  7.  Llamar a través de Facetime
  8.  Abrir otra App desde nuestra App.

1 Abrir una web en Safari

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://www.ohmyapps.es.es"]];


2 Abrir la aplicación Mail

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto:info@ohmyapps.es"]];

3 Llamar por teléfono

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel:968813626"]];


4 Abrir la aplicación de Mapas

[[UIApplication sharedApplicationopenURL:[NSURL URLWithString:@"http://maps.apple.com/?q=murcia"]];

5 Abrir la aplicación de iTunes

[[UIApplication sharedApplication] openURL:[NSURL URLWithString@"https://itunes.apple.com/es/album/dont-stop-believin/id169003304?i=169003415"]];

6 Mandar un SMS

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:666813626"]];

7 Llamar a través de FaceTime

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"facetime://info@ohmyapps.es"]];

8 Abrir una App desde otra App en iOS.

Este apartado es el más complicado (en comparación con los anteriores) y que menos se conoce por lo que será explicado más detalladamente.

Lo primero es añadir a la App que queramos abrir la key en las propiedades de la App URL Types y seguir la siguiente estructura,


Posteriormente ejecutamos la aplicación que queramos abrir desde nuestra App.

Y finalmente utilizamos el siguiente código,

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"padelphone://com.ohmyapps.es"]];

Al ejecutar la acción, podemos observar como desde una App iOS, abrimos otra App iOS.

Aquí os dejo el enlace del código fuente https://github.com/ManuelManzanera/iOSURLSchemes




miércoles, 16 de octubre de 2013

Aplicación para generar iconos en diferentes formatos compatibles con dispositivos iOS

Aquellos que llevéis más tiempo dedicado al desarrollo de Apps para iOS, habréis notado como ha crecido el número de imágenes de precarga e iconos requeridos por Apple para la aprobación de las aplicaciones. Para ahorrar algo de tiempo en esta tediosa tarea, escribiré acerca de la herramienta para generar iconos compatibles con los diferentes dispositivos iOS, Prepo . Prepo es una App con versión gratuita (suficiente para generar iconos) y una versión de pago por tan sólo 4,49 euros que nos permite realizar algunas funciones más que interesantes. 

Una vez descargada la App de Prepo, tan sólo hay que añadir el icono de nuestra App con un tamaño de 1024x1024. Después seleccionamos en las pestañas de arriba, las diferentes opciones que necesitemos. Si es una App universal o sólo para un tipo de dispositivo, para que versión de OS la necesitamos, si la queremos con brillo, etc. 


Ahora sólo tendremos que seleccionar en nuestro proyecto en la sección "App Icons" la opción "Use Asset Catalog"


Y finalmente sólo tendremos que arrastrar las imágenes con su tamaño correspondiente exportadas desde Prepo a nuestro proyecto.






viernes, 11 de octubre de 2013

brote brote

En esta entrada os quiero presentar una aplicación muy especial en la que hemos estado trabajando este verano. El cuento interactivo de brote brote. Un proyecto innovador que ha salido adelante gracias a su creadora, Miriam López. Ya que este blog trata sobre iOS, os comentaré un poco la parte técnica de la aplicación brote brote para iPad (también la tenéis disponible para Android en este enlace de GooglePlay).

El primer problema técnico al que nos tuvimos que enfrentar fue al multi idioma en tiempo de ejecución de la App. Al final nos decidimos por la solución aportada en la entrada anterior.  Y así conseguimos leer y escuchar el cuento en español, inglés, alemán y chino (de momento).




Además de leer y escuchar el cuento, los niños pueden aprender a comer bien y relacionar alimentos con dos mini juegos incluidos en la App. Aquí se nos planteó la siguiente cuestión. ¿Cocos 2d, HTML 5 & JAVAScript, ...? Y finalmente,  optamos por desarrollarlos con los frameworks nativos de cada plataforma ( por supuesto como Chuck Norris hubiese hecho). 


Espero poder hacer el siguiente mini juego de la versión 2.0 con el maravillosos framework SpriteKit, y así preparar un buen manual en español.

Ya que es un cuento interactivo y se publica en el AppStore, para el paso de páginas se optó por la utilización de UIPageViewController . Este controlador lo explicaré detenidamente en siguientes entradas. Consigue un maravilloso efecto de pasado de página.


Como buen cuento interactivo, está plagado de animaciones y posibilidad de interactuar con múltiples objetos. Ya que era condición indispensable que el desarrollo se realizase para dispositivos a partir de iOS 5, la mejor solución fue utilizar CABasicAnimation y CAKeyFrameAnimation para las pricipales animaciones.

Por último sólo quería agradecer a Miriam la confianza depositada en nosotros para llevar el proyecto adelante, y a mi compañero Mr R. encargado de la parte de Android por el magnífico trabajo que ha realizado. Aquí os dejo de nuevo los enlaces de las Apps y otros de interés.

Esperamos que vuestros hijos, nietos, sobrinos y hermanos pequeños disfruten con Travelino tanto como nosotros hemos disfrutado dándole vida en plataformas multimedia.







domingo, 22 de septiembre de 2013

Múltiples idiomas en tiempo de ejecución de la App.

Aunque no es muy común, en alguna ocasión puede que necesitemos que el usuario cambie el idioma de la App sin necesidad de cambiar el idioma del dispositivo. Hay varias soluciones en diferentes foros pero yo voy a explicar la que he utilizado en un proyecto recientemente, el cuento infantil brote brote (cuando esté publicada dejaré el link de la App). Es imprescindible que el proyecto tenga los diferentes Localizable.strings con las traducciones.

1. Creamos un contexto para la App. En esta ocasión, yo lo he incluido en un singleton de contexto ya que me influye en otros factores que no vienen al caso, pero se puede hacer perfectamente en NSUserDefault o en el mismo manager de idoma. Copio el archivo de cabecera seguido del de implementación.


#import <Foundation/Foundation.h>

@interface AppContext : NSObject

@property (nonatomic, strong) NSString *language;

+ (AppContext *)sharedInstance;

@end

A continuación el código de implementación:


#import "AppContext.h"

static AppContext *appContext = nil;

@implementation AppContext

@synthesize language;

+ (void)initialize
{
    NSAssert(self == [AppContext class],@"Esto es un singleton. Se explicará en posteriores artículos");
    appContext = [AppContext new];
}

+ (AppContext *)sharedInstance
{
    return appContext;
}

@end


2. Creamos nuestro manager de idioma. Será el encargado de decirle al path de la App qué carpeta de recurso quiere para el idioma.


#import <Foundation/Foundation.h>

@interface LanguageManager : NSObject

+(NSString*)languageSelectedStringForKey:(NSString*)key;

@end


A continuación el código de implementación:


#import "LanguageManager.h"
#import "AppContext.h"  

@implementation LanguageManager

+(NSString*)languageSelectedStringForKey:(NSString*)key
{
NSString *path;
    path = [[NSBundle mainBundle] pathForResource:[[AppContext sharedInstance] language] ofType:@"lproj"];
NSBundle* languageBundle = [NSBundle bundleWithPath:path];
NSString* string=[languageBundle localizedStringForKey:key value:@"" table:nil];
return string;
}

@end

3. Por último en el controlador encargado de modificar el idioma añadimos el método:

- (void)selectLanguage
{
    if(isLanguageSelected)
    {
        if([[[AppContext sharedInstance] language] isEqualToString:SPANISH])
            [[AppContext sharedInstance] setLanguage:ENGLISH];
        else if([[[AppContext sharedInstance] language] isEqualToString:ENGLISH])
            [[AppContext sharedInstance]setLanguage:DUTCH];
        else if ([[[AppContext sharedInstance] language] isEqualToString:DUTCH])
            [[AppContext sharedInstance] setLanguage:CHINESE];
        else
            [[AppContext sharedInstance] setLanguage:SPANISH];
            
    }
    else
    {
        NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
        
        if([language isEqualToString:@"es"])
        {
            [[AppContext sharedInstance] setLanguage:SPANISH];
        }
        else if ([language isEqualToString:@"de"])
        {
            [[AppContext sharedInstance] setLanguage:DUTCH];
        }
        else if([language isEqualToString:@"zh"])
        {
            [[AppContext sharedInstance] setLanguage:CHINESE];
        }
        else
        {
            [[AppContext sharedInstance] setLanguage:ENGLISH];
        }
    }
}

En nuestro caso como en nuestro mismo controlador queremos que se modifiquen algunas etiquetas

- (void)changeLanguage
{
    isLanguageSelected = TRUE;
    [self selectLanguage];
    
    [authorLabel setText:[LanguageManager languageSelectedStringForKey:@"authorLabel"]];
    [auxLabel setText:[LanguageManager languageSelectedStringForKey:@"titleAuxLabel"]];
    [titleLabel setText:[LanguageManager languageSelectedStringForKey:@"titleLabel"]];
    [subTitleLabel setText:[LanguageManager languageSelectedStringForKey:@"subTitleLabel"]];
    [languageButtonLabel setText:[LanguageManager languageSelectedStringForKey:@"languageTitle"]];
    [startButtonLabel setText:[LanguageManager languageSelectedStringForKey:@"startButton"]];
    [playButtonLabel setText:[LanguageManager languageSelectedStringForKey:@"playButton"]];
}





miércoles, 24 de julio de 2013

Descarga asíncrona de imágenes desde una URL

Una de las reglas que debes seguir a rajatabla en la descarga de datos es la siguiente:
"Las descargas de datos siempre serán ASÍNCRONAS"
Si yo fuese responsable de la contratación de algún desarrollador no haría las estúpidas preguntas a las que estamos acostumbrados como ¿ Cuánto te gustaría ganar? o  ¿ Cómo te ves dentro de 5 años? . Sin embargo, cogería un Mac, abriría el XCode y pediría que me hiciese una descarga de datos a través de alguna petición http. Si el candidato escribiese el siguiente código y me dijese que ya está hecho, la entrevista terminaría en ese preciso instante.

NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://www.ohmyapps.es/images/xxxxxx.xxx"]];

Como no quiero escribir artículos largos dejaré para otro día el artículo acerca de NSURLConnection para descargas asíncronas y me centraré en un código que aparece a menudo en diferentes búsquedas por la red y puede resultar algo complicado ya que utiliza la tecnología de GCD (Grand Central Dispatch) para dispositivos multinúcleos y bloques. Este código sólo es válido para aplicaciones superiores a iOS 4.x (aunque ya no creo que nadie desarrolle para esas versiones). Deberemos seguir los siguientes pasos:

1. Copiamos el siguiente código en el archivo de cabecera:


void UIImageFromURL(NSURL * URL, void (^imageBlock)(UIImage * image), void (^errorBlock)(void));

2. Copiamos el siguiente código en el archivo de implementación:


void UIImageFromURL(NSURL * URL, void (^imageBlock)(UIImage * image), void (^errorBlock)(void) )
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^(void)
        {
            NSData * data = [[NSData alloc] initWithContentsOfURL:URL];
            UIImage * image = [[UIImage alloc] initWithData:data];
            dispatch_async( dispatch_get_main_queue(), ^(void){
                if( image != nil )
                {
                    imageBlock( image );
                }
                else
                {
                    errorBlock();
                }
            });
        });
}

Algún observador dirá ,"Pero si utilizas el mismo código que dices que no se debe utilizar nunca", a lo cual yo contestaré, sí pero con la salvedad de el método dispatch_async(dispatch_queue_t queue, ^(void)block);. Y qué hace este método os preguntaréis. Pues simplemente le pasa un bloque de código para su ejecución asíncrona en una cola. 

3. Finalmente utilizamos el método donde lo necesitemos


UIImageFromURL( [NSURL URLWithString:@"http://www.ohmyapps.es/images/xxxxxx.xxx"], ^( UIImage * image )
                   {
                       //Aquí ejecutamos lo que queramos con nuestra imagen
                   }, ^(void){
                       NSLog(@"error!");
                       //Bloque para ejecutar en caso de error
                   });
    }


Para las próximas entradas veremos otra manera de descarga de datos asíncrona y el uso de alguna librería interesante.

miércoles, 3 de julio de 2013

Añadir una fuente externa

Más tarde o más temprano, nos encontraremos con que un diseñador (un cliente no suele entrar a ese nivel detalle) nos pedirá un tipo de fuente poco habitual. Si todavía no somos muy experimentados se nos puede ocurrir la feliz idea de añadir la fuente como otra cualquiera,

UIFont *font = [UIFont fontWithName:@"Fortheloveofhate" size:17.];

Si además, no es una fuente muy extravagante y estamos un poco verdes en todo el tema relacionado con diseño no sabremos si se ha tomado correctamente, ya que iOS nos muestra una fuente por defecto.

Por lo tanto para añadir una fuente externa debemos seguir los siguientes pasos:

1.   Añadimos el ttf a una carpeta de recursos. En el .plist del proyecto añadimos una fila con el key Fonts provided by application de tipo NSArray. A éste, le añadiremos un item de tipo string y value el nombre de la fuente, en este caso Fortheloveofhate.ttf.





2. Ahora simplemente tenemos que crear la fuente y añadirla donde queramos:

    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.,0.,150.,50.)];
    [label setBackgroundColor:[UIColor clearColor]];
    [label setText:@"Oh My Apps!"];
    
    UIFont *font = [UIFont fontWithName:@"Fortheloveofhate" size:17.];
    [label setFont:font];
    [label setCenter:self.view.center];


NOTA: Si queremos conocer todas las fuentes que podemos utilizar en nuestro dispositivo podemos añadir el siguiente código:

    NSArray *fonts = [UIFont familyNames];
    for(UIFont *font in fonts)
    {
        NSLog(@"%@",font);
    }


viernes, 28 de junio de 2013

Clases sin ARC en un proyecto ARC.

No hace mucho tiempo, en el desarrollo para iOS, los desarrolladores debíamos tener el control de la memoria de los objetos creados. Pese al rechazo inicial de algunos desarrolladores (yo me incluía) la experiencia me ha demostrado que pese a conseguir dominar la gestión de la memoria en mis Apps, desde que migré a ARC, mis preocupaciones han disminuido. Aún así, hay una gran cantidad de clases y códigos que ya tenía creados y por cualquier motivo no me interesaba migrar a ARC. Si quisiera incluir esos códigos en un proyecto ARC y los ejecutase me aparecerían los siguientes errores.



Para solucionar este problema, basta con añadir la instrucción -fno-objc-arc, en el archivo de implementación que deseemos que no compile con ARC, como muestro en la siguiente imagen.


Ahora nuestro código se ejecutará sin ningún problema.


jueves, 27 de junio de 2013

Icono sin efecto de brillo.

Sólo hay que echar un vistazo a la pantalla de nuestro dispositivo iOS para ver como algunos iconos de aplicaciones aparecen con un efecto de brillo y otros no. Este efecto lo añade Apple por defecto y para eliminarlo tenemos que seguir los siguientes pasos:

1. Seleccionamos el target de nuestro proyecto, y pulsamos sobre la pestaña de info. Añadimos al plist que nos aparece una fila.


2. Esta fila será de tipo booleano con la key Icon already includes gloss effects y el value YES.



3. Y si volvemos a ejecutar el proyecto podremos ver el icono de nuestra App sin el brillo por defecto.

Icono con brillo
Icono con efecto brillo

Icono sin efecto brillo


NOTA: es recomendable eliminar la App si ya está instalada antes de ejecutar para asegurarnos de que pinta correctamente el icono.

miércoles, 26 de junio de 2013

Controladores nativos de Facebook y Twitter en iOS 6.x. Social framework segunda parte.

La integración de este controlador  ha sido muy sencilla (ver primera parte). Pero tanto facebook como Twitter nos ofrecen otra serie de funciones como seguir, compartir, etc. Para ello necesitaremos acceder a los datos de nuestra cuenta desde nuestras aplicaciones nativas sociales. Seguiremos los siguientes pasos:

1. Adjuntamos el framework Accounts a nuestro proyecto.



2. Importamos las cabeceras a nuestro archivo de cabecera.

#import <Accounts/Accounts.h>

3. Añadimos el siguiente código en el método donde queramos implementar la función (en este caso se toma como ejemplo el "follow" de Twitter).

if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
    {
        ACAccountStore *accountStore = [[ACAccountStore alloc] init];
        ACAccountType *twitterAccountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
        [accountStore requestAccessToAccountsWithType:twitterAccountType options:nil completion:^(BOOL granted, NSError *error)
        {
            if(granted)
            {
                NSArray *accountsArray = [accountStore accountsWithAccountType:twitterAccountType];
                
                // En caso de tener más de una cuenta, aquí se debería integrar el código
                if ([accountsArray count] > 0)
                {
                    ACAccount *twitterAccount = [accountsArray objectAtIndex:0];
                    
                    NSMutableDictionary *twitterApiDictionary = [[NSMutableDictionary alloc] init];
                    [twitterApiDictionary setValue:@"OhMyAppss" forKey:@"screen_name"];
                    [twitterApiDictionary setValue:@"true" forKey:@"follow"];
                    
                    SLRequest *twitterRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:[NSURL URLWithString:@"http://api.twitter.com/1/friendships/create.json"] parameters:twitterApiDictionary];
                         
                    [twitterRequest setAccount:twitterAccount];
                         
                    [twitterRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
                    {
                        NSLog(@"%@",[NSString stringWithFormat:@"HTTP response status: %i", [urlResponse statusCode]]);

                 //Aquí se deben estudiar los posibles casos de error

                    }];
                }
            }
        }];
    }

Para conocer todas las funciones que nos ofrecen Twitter y Facebook deberemos consultar su documentación: 



martes, 25 de junio de 2013

Controladores nativos de Facebook y Twitter en iOS 6.x. Social framework primera parte.

  Una de las novedades que trajo consigo iOS 6.0 fue el Social framework. Con él, podemos incluir el controlador nativo de redes sociales, de una manera muy sencilla. Aquellos que nos tuvimos que pelear con los SDKs de Facebook y Twitter, estaremos eternamente agradecidos. Los pasos para dicha integración son los siguientes:

1.  Añadir la librería Social.framework a nuestro proyecto. No olvidéis marcarlo como "optional" si la aplicación está habilitada para dispositivos anteriores a iOS 6.0.



2. Importamos las cabeceras del framework a nuestro archivo de cabecera.

#import <Social/Social.h>
3. Añadimos el siguiente código al método que ejecute la acción de social media deseada:


if([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
    {
        SLComposeViewController *twitterVC = [SLComposeViewController  composeViewControllerForServiceType:SLServiceTypeTwitter];
    
        [twitterVC setInitialText:NSLocalizedString(@"App creada por Oh My Apps!", nil)];
        [twitterVC setTitle:@"Oh My Apps!"];
        [twitterVC addImage:[UIImage imageNamed:@"oma_icon"]];
        [twitterVC addURL:[NSURL URLWithString:@"http://www.ohmyapps.es"]];
    
        [self presentViewController:twitterVC animated:YES completion:nil];
    }


Podremos utilizar el controlador para Twitter con el tipo SLServiceTypeTwitter, para facebook con el tipo SLServiceTypeFacebook y para la red social china Seibo con el tipo SLServiceTypeSinaWeibo.

Al ejecutar nos deberá aparecer el controlador nativo.