<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
   <channel>
      <title>design-nation.blog/es</title>
      <link>http://www.design-nation.net/es/</link>
      <description>un blog sobre actionscript, java, cocoa, y lo que se tercie</description>
      <language>es</language>
      <copyright>Copyright 2008</copyright>
      <lastBuildDate>Wed, 23 Apr 2008 10:33:03 +0000</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/?v=3.2</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>Testing: Hopefully not nobody</title>
         <description><![CDATA[<p>Que yo recuerde, las versiones anteriores de XCode no incluían en la plantilla de los proyectos ningún archivo de créditos. Según parece, ahora ya sí...</p>

<p><img src="http://www.design-nation.net/es//hopefully.png" alt="hopefully.png" border="0" width="364" height="414" /></p>

<p>"Hopefully not nobody". Tiene su chispa.</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003836.php</link>
         <guid>http://www.design-nation.net/es/archivos/003836.php</guid>
         <category>Cocoa</category>
         <pubDate>Wed, 23 Apr 2008 10:33:03 +0000</pubDate>
      </item>
            <item>
         <title>FND 1.2</title>
         <description><![CDATA[<p>Se acaba de liberar una nueva versión de <a href="http://bambooapps.com/fnd/">FND (film is not dead)</a>, la aplicación para Mac OS X que viene en ayuda de aquellos que amamos la fotografía analógica y que disfrutamos revelando película con nuestras propias manos.</p>

<p>FND es una tabla de tiempos y reveladores, que permite tener organizada y clasificada tu experiencia con las diferentes combinaciones de película y revelador. Además, FND es un reloj de revelado, que puede funcionar en modo fullscreen, y que es controlable a través del Apple Remote.</p>

<p>La aplicación es sólo para Leopard, tiene un período de prueba de 30 días, y su precio es de 12.95 dólares.</p>

<p>Esta nueva versión incluye, entre varias mejoras de usabilidad, la localización en castellano.</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003835.php</link>
         <guid>http://www.design-nation.net/es/archivos/003835.php</guid>
         <category>Cocoa</category>
         <pubDate>Tue, 01 Apr 2008 12:23:07 +0000</pubDate>
      </item>
            <item>
         <title>Uso de Core Animation para modificar el contenido de una vista</title>
         <description><![CDATA[<p>Uno de los frameworks que han hecho su debut con Leopard ha sido Core Animation.</p>

<p>Según Apple, Core Animation es un framework para renderizado gráfico, proyección y animación. Lo que, traducido, quiere decir, que es un framework que facilita enormemente la animación de elementos de interfaz.</p>

<p>Por ejemplo, es muy sencillo cambiar en tiempo de ejecución el contenido de una vista, sustituyéndolo por otra vista completamente distinta, presentando el cambio con una animación discreta, pero efectiva para reforzar el feedback que recibe el usuario del proceso.</p>

<p>En este ejemplo vamos a partir de este primer estado:</p>

<p><img src="http://www.design-nation.net/es//swap_final_1.png" alt="swap_final_1.png" border="0" width="560" height="243" /></p>

<p>Para llegar a este otro:</p>

<p><img src="http://www.design-nation.net/es//swap_final_2.png" alt="swap_final_2.png" border="0" width="560" height="243" /></p>

<p>Como puede verse, en este caso el cambio no es muy grande, se pasa de un slider a un desplegable, no obstante, la transición se hará utilizando la animación por defecto del framework (dissolve).</p>

<p>Construir el interfaz de la aplicación es sencillo. En la ventana principal de la misma, voy a colocar un botón, que será el que lance la animación, y una instancia de NSView, cuyo contenido iré modificando en tiempo de ejecución. Éste sería el aspecto de mi ventana:</p>

<p><img src="http://www.design-nation.net/es//swap_ventana.png" alt="swap_ventana.png" border="0" width="560" height="243" /></p>

<p>Además, construyo dos instancias más de NSView:</p>

<p><img src="http://www.design-nation.net/es//swap_view1.png" alt="swap_view1.png" border="0" width="381" height="193" /></p>

<p><img src="http://www.design-nation.net/es//swap_view2.png" alt="swap_view2.png" border="0" width="243" height="198" /></p>

<p>Como se puede ver, el ancho de las dos vistas es diferente. </p>

<p>Construyo también el esqueleto del controlador de la aplicación. Tendré tres outlets, uno a cada uno de las tres vistas (la de la ventana principal, y las dos vistas independientes). Además, tendré un action para el botón de "swap" y un booleano que me indicará si la vista que se está mostrando en cada instante es la primera o la segunda:</p>

<p>#import <Cocoa/Cocoa.h></p>

<p><br />
@interface ViewSwapController : NSObject <br />
{<br />
	IBOutlet NSView *containerView;<br />
	IBOutlet NSView *firstView;<br />
	IBOutlet NSView *secondView;<br />
	<br />
	BOOL showFirst;<br />
}</p>

<p>-(IBAction) swapView: (id) sender;<br />
@end</p>

<p>Tras crear la instancia de NSObject en Interface Builder y asignarle como clase mi controlador, la estructura del nib sería como la siguiente:</p>

<p><img src="http://www.design-nation.net/es//swap_nib.png" alt="swap_nib.png" border="0" width="399" height="430" /></p>

<p>Quiero que mi controlador haga lo siguiente: al arrancar la aplicación, insertará dentro de la vista de la ventana principal la primera de las dos vistas independientes que he creado, y cuando se haga clic en el botón de "swap" se cambiará esa vista por la segunda.</p>

<p>Por tanto, el método awakeFromNib será:</p>

<p>-(void) awakeFromNib<br />
{	<br />
	showFirst = NO;<br />
	<br />
	[ containerView setWantsLayer: YES ];<br />
	<br />
	[ [ containerView animator ] addSubview: firstView ];<br />
}</p>

<p>Simplemente, preparo la vista de la ventana principal para que sea animable, y le añado como subvista la primera de mis vistas personalizadas.</p>

<p>Al hacer clic en el botón:</p>

<p>-(IBAction) swapView: (id) sender<br />
{<br />
	NSRect containerFrame = [ containerView frame ];<br />
	NSRect firstViewFrame;<br />
	NSNumber *newCoord;<br />
	NSView *tempFirst;<br />
	NSView *tempSecond;<br />
	<br />
	if( showFirst )<br />
	{<br />
		tempFirst = secondView;<br />
		tempSecond = firstView;<br />
		<br />
		showFirst = NO;<br />
	}<br />
	else<br />
	{<br />
		tempFirst = firstView;<br />
		tempSecond = secondView;<br />
		<br />
		showFirst = YES;<br />
	}<br />
	<br />
	[ [ containerView animator ] replaceSubview: tempFirst with: tempSecond ];		<br />
	<br />
	firstViewFrame = [ tempSecond frame ];<br />
	<br />
	newCoord = [ NSNumber numberWithFloat: containerFrame.size.width /2 - firstViewFrame.size.width/2 ];</p>

<p>	[ tempSecond setFrameOrigin: NSMakePoint( [ newCoord intValue ] , 0 ) ];		<br />
	</p>

<p>}</p>

<p><br />
En primer lugar, calculo el ancho de la vista contenedor, y de la vista que quiero colocar dentro de la misma, para luego poder centrarla, y posteriormente reemplazo la vista que esté dentro del contenedor por la que corresponda.</p>

<p>Lo importante, lo que hace que la transición entre las dos vistas sea animada, es que no ataco directamente la vista contenedor para modificar su contenido, sino que utilizo el decorator que crea CoreAnimation (que se llama animator) para que, de esa forma, cualquier modificación de las propiedades de esa vista se muestre de forma animada.</p>

<p>El proyecto puede descargarse del repositorio público de subversion:</p>

<p>svn co http://svn.liadorasoft.com/viewswap ViewSwap</p>

<p>Enlaces relacionados:<br />
<a href="http://www.design-nation.net/es/archivos/003819.php">Un clon de Photo Booth en 32 minutos</a><br />
<a href="http://www.design-nation.net/es/archivos/003829.php">Extensión por delegación</a><br />
<a href="http://www.design-nation.net/es/archivos/003830.php">Bindings, NSArrayController y Value Transformers</a></p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003834.php</link>
         <guid>http://www.design-nation.net/es/archivos/003834.php</guid>
         <category>Cocoa</category>
         <pubDate>Wed, 19 Mar 2008 04:36:35 +0000</pubDate>
      </item>
            <item>
         <title>iPhone SDK</title>
         <description><![CDATA[<p>Ya está, ya hay un SDK para el iPhone/iPod Touch. Por fin se van a poder desarrollar y distribuir aplicaciones de forma "oficial", soportada por Apple y sin necesidad de hacer nada estraño con el aparato.</p>

<p>Desde mi punto de vista, como parte interesada, como usuario y como desarrollador, hay varias cosas que me han llamado la atención.</p>

<p><strong>¿Documentación?</strong><br />
En primer lugar, la mayor sorpresa ha sido lo bien documentada que está la plataforma, sobre todo teniendo en cuenta que Apple tiende a producir una documentación más bien opaca, que es poco más que una enumeración de los interfaces de sus APIs.</p>

<p>Esta vez no sólo se han publicado documentos "en papel" sino una serie de vídeos en ADC on iTunes que sobrepasan con creces, en calidad de contenidos, lo que había disponible hasta ahora (exceptuando las sesiones de la WWDC). Lo que me lleva a pensar dos cosas más.</p>

<p><strong>No es el iPhone ni el iPod, es lo que está por venir</strong><br />
La sensación, al menos la que yo tengo, es que lo que se documenta no es sólo el SDK para iPhone e iPod Touch, sino que lo se documenta es <em>la plataforma de Apple para dispositivos con pantalla táctil</em>.</p>

<p>Es decir, que la sensación que a mí me ha quedado es la de que lo que se abre hoy no es la puerta al desarrollo para un cacharro, sino para muchos, que compartirán ciertos frameworks. Conociendo la arquitectura de los frameworks de Apple no es nada descabellado pensar que este SDK mañana sirva para desarrollar aplicaciones para el sistema de control de un coche, o para un tablet, o para lo que sea que se pueda manejar con los dedos.</p>

<p>Y eso está muy bien.</p>

<p><strong>Cuota de mercado</strong><br />
La otra conclusión es que esta plataforma (la llamémosla móvil) no tiene las limitaciones en cuanto a cuota de mercado que tiene un Mac. Apple lo sabe, y le da la importancia pertinente al SDK. D e hecho, se ha dividido el Developer Connection en dos partes jerárquicamente igual de importantes: el iPhone Developer Program, y el Mac Developer Program. </p>

<p>Sinceramente, llama la atención ver el esfuerzo que ha puesto Apple en facilitar a los desarrolladores la adaptación a esta plataforma.</p>

<p>Eso es, obviamente, bueno. Cuantos más sean (o seamos) desarrollando aplicaciones, mejores aplicaciones habrá. Lo que lleva al siguiente punto.</p>

<p><strong>Modelo de distribución</strong><br />
Sinceramente, no me parece mal que las aplicaciones se distribuyan vía iTunes. No me tengo que preocupar de montar la web del producto, ni un sistema de cobro on-line, y más importante aún, mi aplicación está disponible para mis futuros usuarios desde el mismo dispositivo en el que se va a utilizar. Lo que va en la línea de actuación de Apple, por cierto, que no es otra que poner lo más fácil posible a la gente que pueda comprar. Y si yo quiero vender, y Apple lo pone fácil para comprar, la cosa me gusta.</p>

<p>Peeeeeeeeero, y siempre hay un pero, lo que no me gusta es que Apple tenga que aprobar la entrada de la aplicación en la tienda. Entiendo que ellos intentan proteger, por un lado, al proveedor de servicios móviles, y por otro, su propia reputación, pero eso tiene dos cosas muy malas, relacionadas: Apple puede decidir cerrar el paso a una aplicación porque le guste la idea, y puede decidir desarrollar una apliación similar por sí misma.</p>

<p>Lo de los 99$ por poder firmar aplicaciones y distribuirlas en la tienda de iTunes no me parece tan grave. Total, es lo que me iba a costar el hosting. Lo mismo pasa con el 30% de comisión que se queda Steve. Es alta, es cierto, pero tampoco es tan grave.</p>

<p>Lo que sí que toca las canicas es el empeño que tiene Apple en poner una línea entre usuarios/desarrolladores de USA y del resto del mundo. Que, digo yo, que algún día se nos va a acabar la paciencia.</p>

<p><strong>El flujo de trabajo</strong><br />
Lo primero que me ha llamado la atención del SDK es que el emulador funciona muy bien. Lo segundo es que, por favor, hace falta un Interface Builder específico, que para hacer layouts a mano ya existe Java.</p>

<p>Poco más se puede decir, la verdad. Las herramientas son las de siempre, el lenguaje es el de siempre (aunque, ojo, sin recolector de basura, así que hay que volver al hold me - use me - release me) y todo es como siempre.</p>

<p>Sólo que, esta vez, con muchísima más expectación que otras veces.</p>

<p>Ahora, ya, sólo queda empaparse por completo de la documentación, y comprobar si, por favor, finalmente ha entrado en el SDK público el acceso a los SyncServices...</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003833.php</link>
         <guid>http://www.design-nation.net/es/archivos/003833.php</guid>
         <category>Cocoa</category>
         <pubDate>Fri, 07 Mar 2008 04:26:06 +0000</pubDate>
      </item>
            <item>
         <title>FND 1.1.1</title>
         <description><![CDATA[<p><a href="http://bambooapps.com/2008/02/10/fnd-111/">Release de mantenimiento</a> de FND (film is not dead), la aplicación para Mac OS X que viene en ayuda de aquellos que amamos la fotografía analógica y que disfrutamos revelando película con nuestras propias manos.</p>

<p>FND es una tabla de tiempos y reveladores, que permite tener organizada y clasificada tu experiencia con las diferentes combinaciones de película y revelador. Además, FND es un reloj de revelado, que puede funcionar en modo fullscreen, y que es controlable a través del Apple Remote.</p>

<p>La aplicación es sólo para Leopard, tiene un período de prueba de 30 días, y su precio es de 12.95 dólares.</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003832.php</link>
         <guid>http://www.design-nation.net/es/archivos/003832.php</guid>
         <category>Cocoa</category>
         <pubDate>Sun, 10 Feb 2008 12:42:03 +0000</pubDate>
      </item>
            <item>
         <title>Entresijos. Haciendo la web. El nuevo proyecto de Design-Nation</title>
         <description><![CDATA[<p>Durante los próximos días, tomará cuerpo "<a href="http://www.design-nation.net/entresijos/">Entresijos. Haciendo la web</a>", el nuevo y por ahora último proyecto que sale de la cocina de ideas del emporio Design-Nation.</p>

<p>¿Qué es "Entresijos" ?</p>

<p>Se trata de un podcast, en formato entrevista y mesa redonda, de una duración no superior a media hora por episodio, por el que queremos que pase la mayor cantidad de profesionales que, de una u otra manera, desarrollen su actividad en relación con internet.</p>

<p>No es un podcast de y para programadores, sino que queremos que sirva como plataforma en la que todos los perfiles implicados en la construcción de internet (programadores, técnicos de sistemas, diseñadores, expertos en HCI, formadores, jefes de proyecto, bloggers, editores de webs de noticias, etc) y también aquellos que desde el otro lado de la barrera utilizan lo que los primeros construyen, puedan explicar en qué consiste su trabajo, cuáles son los retos con los que se enfrentan día a día, y cuál es la dirección que creen que debe tomar el futuro cercano, tanto de su parcela profesional como del negocio en general.</p>

<p>En el grupo de personas con el que estamos contactando hay programadores cliente y programadores servidor, técnicos de sistemas, diseñadores, animadores, expertos en e-learning, expertos en usabilidad,  directores de agencias de publicidad, o project managers.</p>

<p>Aprovechamos desde ya, para agradecer la colaboración de quienes en el último mes y medio nos han confirmado su deseo de participar. Y confiamos en que haya más gente colaborando a futuro.</p>

<p>Esperamos que os guste. </p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003831.php</link>
         <guid>http://www.design-nation.net/es/archivos/003831.php</guid>
         <category>General</category>
         <pubDate>Thu, 31 Jan 2008 18:33:55 +0000</pubDate>
      </item>
            <item>
         <title>Bindings, NSArrayController y Value Transformers</title>
         <description><![CDATA[<p>Parece el título de una mala película de acción, de esas que estiran hasta más allá de lo razonable y lo creíble la trama de un comic, pero es aún peor, porque es el título de la última aventura de El Gran Dragón.</p>

<p>Y es que Bruce le ha ordenado que construya una prueba de concepto, una aplicación funcional, pero no muy compleja, que presente en un listado unos cuantos iconos. En concreto, los que corresponden con NSComputer, NSFolderBurnable y NSFolderSmart, que como El Gran Dragón bien sabe, Apple ha expuesto para su uso a partir de Leopard.</p>

<p>El Gran Dragón sabe que su Gran Maestro gusta de las aplicaciones sencillas y elegantes, de tener un núcleo funcional desde etapas muy tempranas sobre el que ir realizando los refinamientos necesarios y, por eso, su primera idea llega rápida como el ataque de una serpiente: "voy a construir una aplicación con un controlador, que tenga agregado un array mutable, array que controlaré con una instancia de NSArrayController, de forma que, por bindings, asigne el contenido del listado que constituye el interfaz de la aplicación a partir del contenido de ese array".</p>

<p>No está mal. Una solución sencilla, elegante, que utiliza en su beneficio muchos de los frameworks y metodologías de desarrollo para Mac OS X y cuya arquitectura sigue las líneas maestras de lo que se consideran "buenas prácticas".</p>

<p>Por tanto, El Gran Dragón comienza por crear el proyecto, como siempre, y por crear el controlador de la aplicación, una clase llamada AppController, que agregará un array mutable. La cabecera de esa clase sería por tanto:</p>

<p>#import <Cocoa/Cocoa.h></p>

<p><br />
@interface AppController : NSObject <br />
{<br />
	NSMutableArray *iconsCollection;<br />
}</p>

<p>@property(copy, readwrite) NSMutableArray *iconsCollection;</p>

<p>@end</p>

<p>El interfaz de la aplicación tampoco tiene mucha complicación. Basta con crear en Interface Builder una instancia de NSObject y asignarle su tipo al controlador de la aplicación, y construir un interfaz con un NSTableView con una única columna, sobre la que El Gran Dragón arrastra una instancia de NSImageCell, para de esa forma poder presentar imágenes en la tabla.</p>

<p><img src="http://www.design-nation.net/es//interfaz_transformer.png" alt="interfaz_transformer.png" border="0" width="210" height="460" /></p>

<p>Bien, se dice a sí mismo el Gran Dragón. "Como tengo un controlador de la aplicación responsable de crear un array mutable, si consigo colocar en ese array mutable las imágenes que quiero presentar en el interfaz, sólo tendría que asignar el binding correspondiente en Interface Builder para que mi aplicación presentara las imágenes". </p>

<p>Como ya era tarde, El Gran Dragón se marchó a la cama sonriente, pensando que había encontrado la solución a su problema. Sin embargo, a altas horas de la madrugada, su maestro se le apareció en sueños: "Gran Dragón, ¿acaso has olvidado todo lo que te he enseñado? De verdad crees que es necesario crear esas imágenes? ¿Y si quisieras serializar el modelo a disco? No entiendes que no es necesario crear tantas instancias de NSImage, sobe todo si las puedes crear en tiempo de ejecución? ¿No te parece?"</p>

<p>El Gran Dragón despertó sudoroso, pero con una mirada de gran determinación. "¡Es cierto! Puedo crear las instancias de NSImage al vuelo, lanzándole un mensaje como éste!":</p>

<p>[ NSImage imageNamed: @"NSComputer" ];</p>

<p>Por tanto, sólo necesitaría guardar en el modelo las cadenas de texto con los nombres de las imágenes a crear. Pero, ¿cómo hago para convertir esa cadena de texto en una instancia de NSImage, que es a lo que necesito bindar la columna de mi tabla?</p>

<p>Pues utilizando un Value Transformer. Un Value Transformer es una clase que ejerce como mediador en el mecanismo de bindings. Si esa clase existe, antes de pasarle al interfaz lo que debe dibujar, se le pasa al mediador el valor extraído del modelo, y es el mediador el que, tras hacer lo que deba con ese valor, quien le termina por dar al interfaz lo que éste necesita.</p>

<p>En este caso, el interfaz (la columna de la tabla) necesita instancias de NSImage, mientras que lo que El Gran Dragón va a guardar en el modelo con cadenas de texto. Por tanto, el Value Transformer será el que cree las instancias de NSImage necesarias a partir de ese valor de texto.</p>

<p>Lo primero es crear la entidad básica del modelo. El Gran Dragón ha decidido, en un alarde de originalidad, llamarla AppEntity:</p>

<p>#import <Cocoa/Cocoa.h></p>

<p><br />
@interface AppEntity : NSObject <br />
{<br />
	NSString *iconName;<br />
}<br />
@property(copy, readwrite) NSString *iconName;<br />
@end</p>

<p><br />
La implementación:</p>

<p>#import "AppEntity.h"</p>

<p><br />
@implementation AppEntity<br />
@synthesize iconName;<br />
@end</p>

<p><br />
Ahora, El Gran Dragón escribe la implementación de su controlador para que, al crearse, éste cree las tres instancias de esa entidad que El Gran Maestro le ha pedido. Por tanto, AppController.m será:</p>

<p>#import "AppController.h"<br />
#import "AppEntity.h"<br />
#import "DNIconValueTransformer.h"</p>

<p><br />
@implementation AppController<br />
@synthesize iconsCollection;<br />
-(id) init<br />
{<br />
	self = [ super init ];<br />
	<br />
	NSValueTransformer *iconTransformer = [ [ DNIconValueTransformer alloc ] init ];<br />
	[ NSValueTransformer setValueTransformer: iconTransformer forName:@"IconImageTransformer"];		<br />
	<br />
	iconsCollection = [ [ NSMutableArray alloc ] init ];<br />
	<br />
	AppEntity *entityOne = [ [ AppEntity alloc ] init ];<br />
	entityOne.iconName = @"NSComputer";<br />
	[ iconsCollection addObject: entityOne ];</p>

<p>	AppEntity *entityTwo = [ [ AppEntity alloc ] init ];<br />
	entityTwo.iconName = @"NSFolderBurnable";<br />
	[ iconsCollection addObject: entityTwo ];</p>

<p>	AppEntity *entityThree = [ [ AppEntity alloc ] init ];<br />
	entityThree.iconName = @"NSFolderSmart";	<br />
	[ iconsCollection addObject: entityThree ];	<br />
	<br />
	return self;<br />
}<br />
@end</p>

<p>Como puede verse, al inicializarse el controlador también se registra el Value Transformer, asignándole como nombre IconImageTransformer.</p>

<p>El siguiente paso de El Gran Dragón, por tanto, es escribir esa clase, que tendrá la siguiente cabecera:</p>

<p>#import <Cocoa/Cocoa.h></p>

<p><br />
@interface DNIconValueTransformer : NSValueTransformer<br />
{</p>

<p>}</p>

<p>@end</p>

<p>Como se puede ver, el único misterio que tiene es que extiende de NSValueTransformer.</p>

<p>La implementación tiene un poco más de miga:</p>

<p>#import "DNIconValueTransformer.h"</p>

<p><br />
@implementation DNIconValueTransformer</p>

<p>+ (Class)transformedValueClass<br />
{<br />
	return [ NSImage class ]; <br />
}</p>

<p>+ (BOOL)allowsReverseTransformation<br />
{<br />
	return YES;<br />
}</p>

<p>- ( id ) transformedValue:( id )value<br />
{<br />
		NSLog( @"transformador %@", value );<br />
		<br />
		if( value != nil )<br />
		{<br />
			return [ NSImage imageNamed: value ];<br />
		}<br />
		else<br />
		{<br />
			NSLog( @"pasa por el transformador" );<br />
			return nil;<br />
		}<br />
}</p>

<p>@end</p>

<p>Aunque tampoco es muy complicada. El método transformedValueClass devuelve el nombre del tipo de retorno del método que va a realizar la transformación: transformedValue. En este caso, lo que se va a crear son imágenes, por tanto devuelve NSImage.</p>

<p>El método de transformación en sí tampoco es muy complejo. Simplemente se crea la instancia de NSImage a partir del nombre de la imagen que se le pasa al transformador desde el modelo.</p>

<p>Ahora, de vuelta en Interface Builder, hay que crear una instancia de NSArrayController, y encargarla manejar la colección de iconos:</p>

<p><img src="http://www.design-nation.net/es//controlador_Array.png" alt="controlador_Array.png" border="0" width="450" /></p>

<p>El último paso es asignar el binding de la columna de la tabla. </p>

<p><img src="http://www.design-nation.net/es//table_column_bindings.png" alt="table_column_bindings.png" border="0" width="299" height="380" /></p>

<p>Como se puede ver, se binda al valor de la propiedad iconName de la entidad del modelo, pero utilizando la transformación que El Gran Dragón registró previamente como IconImageTransformer.</p>

<p>De esa forma, el resultado final es el que se pretendía conseguir:</p>

<p><img src="http://www.design-nation.net/es//icons_final.png" alt="icons_final.png" border="0" width="210" height="460" /></p>

<p>Y se ha conseguido con un modelo ligero, sin gran consumo de memoria ni de recursos del sistema, que puede ser serializado fácilmente.</p>

<p>Es un paso más para El Gran Dragón en el largo camino de la sabiduría.</p>

<p>El proyecto completo puede descargarse de <a href="http://www.design-nation.net/es/archivos/transformers.zip">este enlace</a></p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003830.php</link>
         <guid>http://www.design-nation.net/es/archivos/003830.php</guid>
         <category>Cocoa</category>
         <pubDate>Wed, 23 Jan 2008 10:07:44 +0000</pubDate>
      </item>
            <item>
         <title>Extensión por delegación</title>
         <description><![CDATA[<p><br />
<strong>Gran Maestro</strong>: <em>La prueba de hoy en el largo y tortuoso camino del conocimiento, Gran Dragón, es programar una vista clickable, que puedas reutilizar en todas las futuras pruebas con las que te vas a encontrar, y que sea capaz de notificar al controlador de la aplicación en la que se esté utilizando cada uno de los clicks que reciba.<br />
</em></p>

<p><strong>Gran Dragón</strong>: <em>Gracias, Gran Maestro, por proponerme un reto de tal importancia.<br />
</em></p>

<p>El Gran Dragón sabe que no puede traicionar la confianza ni las expectatias de su maestro. Sabe que ha comenzado un largo camino, un camino lleno de escarpadas montañas, cruzado por anchos y profundos ríos, un camino del que parten otros muchos caminos que le pueden distraer de su objetivo final: llegar al estado mental en el que sólo hay una forma de pensar; la que termina con la máxima cohesión y el mínimo acoplamiento.</p>

<p>Por eso, y porque El Gran Dragón cree que la mejor forma de programar es iterativa, produciendo desde la primera iteración algo funcional, aunque no esté necesariamente muy refinado, comienza su aventura declarando una subclase de NSView, que, simplemente, sea capaz de capturar los clicks de ratón que se produzcan sobre ella. Además, El Gran Dragón quiere proporcionar feedback visual de las interacciones con su vista, por lo que va a hacer que la vista cambie de color, del gris en reposo, a un gris más claro cuando sea clickada.</p>

<p>Por tanto, la cabecera de su vista será:</p>

<p>#import <Cocoa/Cocoa.h></p>

<p><br />
@interface DNClickableView : NSView <br />
{<br />
	BOOL mouseIsPressingMe;<br />
}</p>

<p>@end</p>

<p><br />
Al inicializar la instancia de su clase, el Gran Dragón hará que la variable booleana valga false. Cada vez que redibuje el contenido de su vista, comprobará el valor de esa variable: si es true, el fondo será gris claro, y si es false, gris:</p>

<p>- (id)initWithFrame:(NSRect)frame <br />
{<br />
    self = [super initWithFrame:frame];<br />
	<br />
    if (self) <br />
	{<br />
		mouseIsPressingMe = NO;<br />
    }<br />
	<br />
    return self;<br />
}</p>

<p>- (void)drawRect:(NSRect)rect <br />
{<br />
    if ( mouseIsPressingMe )<br />
	{<br />
		[ [ NSColor lightGrayColor ] set ];<br />
	}<br />
	else<br />
	{ <br />
		[ [ NSColor grayColor ] set ];<br />
	}<br />
	<br />
    NSRectFill( rect );<br />
}</p>

<p><br />
Para capturar los eventos de ratón sobre esa vista, basta con declararlos, sobreescribiendo por tanto los correspondientes a NSView, en la implementación de la subclase:</p>

<p>- ( void ) mouseDown: ( NSEvent * ) theEvent <br />
{<br />
	mouseIsPressingMe = YES;<br />
	[ self setNeedsDisplay: YES ];<br />
}</p>

<p>- (void)mouseUp:(NSEvent *)theEvent <br />
{<br />
    mouseIsPressingMe = NO;<br />
    [ self setNeedsDisplay: YES ];<br />
}</p>

<p>Por tanto, la declaración completa de la implementación de la clase sería:</p>

<p>#import "DNClickableView.h"</p>

<p><br />
@implementation DNClickableView</p>

<p>- (id)initWithFrame:(NSRect)frame <br />
{<br />
    self = [super initWithFrame:frame];<br />
	<br />
    if (self) <br />
	{<br />
		mouseIsPressingMe = NO;<br />
    }<br />
	<br />
    return self;<br />
}</p>

<p>- (void)drawRect:(NSRect)rect <br />
{<br />
    if ( mouseIsPressingMe )<br />
	{<br />
		[ [ NSColor lightGrayColor ] set ];<br />
	}<br />
	else<br />
	{ <br />
		[ [ NSColor grayColor ] set ];<br />
	}<br />
	<br />
    NSRectFill( rect );<br />
}</p>

<p>#pragma mark -<br />
#pragma mark mouse handling</p>

<p>- ( void ) mouseDown: ( NSEvent * ) theEvent <br />
{<br />
	mouseIsPressingMe = YES;<br />
	[ self setNeedsDisplay: YES ];<br />
}</p>

<p>- (void)mouseUp:(NSEvent *)theEvent <br />
{<br />
    mouseIsPressingMe = NO;<br />
    [ self setNeedsDisplay: YES ];<br />
}<br />
@end</p>

<p><br />
El Gran Dragón sabe que debe testar mucho y desde muy pronto, por lo que, para probar la funcionalidad de su clase, declara un controlador con un outlet hacia su vista, y compila y prueba el proyecto (El Gran Dragón da por supuesto que le lector sabe cómo hacer eso en XCode 3; no obstante, se puede encontrar <a href="http://www.design-nation.net/es/archivos/003819.php">más información sobre cómo hacerlo aquí</a>)</p>

<p>Bien, el Gran Dragón está satisfecho de su primera iteración. Tiene una aplicación que compila aunque no cumple con uno de los requisitos pedidos: que la vista notifique al controlador los clicks recibidos.</p>

<p>Ahora ha llegado el momento de meditar, con la cabeza fría, sobre los diferentes caminos que se presentan ante el Gran Dragón.</p>

<p>El primer camino, el más obvio, y probablemente el más sencillo y rápido de implementar sería declarar una variable de clase en la vista, en la que guardar una referencia al controlador, de forma que, al capturar el clic, se envíe un mensaje al controlador de la aplciación.</p>

<p>No está mal, piensa El Gran Dragón. Sin embargo, ¿hasta qué punto esa solución es portable? ¿Hasta qué punto esa misma vista se podría reutilizar en más proyectos?</p>

<p>El Gran Dragón sonríe, con la satisfacción de saberse en el buen camino. Sabe que esa primera solución, la más rápida, es, probablemente, la menos adecuada. En primer lugar, porque implica que tanto la vista como el controlador tengan una referencia cruzada el uno al otro. </p>

<p>En segundo lugar, porque si se hacen las cosas como se debería, la referencia al controlador que se guarda en la vista debería estar declarada con el tipo del controlador. Lo que no la hace precisamente portable. Claro, que eso se podría solventar haciendo que esa referencia sea de tipo un protocolo que implemente el controlador. Lo que tampoco es bastante portable, porque obliga a que el controlador de cualquier aplicación que quiera utilizar esta vista deba implementar ese protocolo.</p>

<p>El Gran Dragón, por tanto, deshecha la primera solución.</p>

<p>La segunda solución que le viene a la cabeza es utilizar una notificación, de forma que la vista lance esa notificación (lo que en la mayoría de lenguajes se llama un evento), que será escuchada por el controlador.</p>

<p>El problema en esta segunda solución es que la implementación de las notificaciones en Objective-C es un poco particular. Las notificaciones se emiten y se reciben a través de un singleton (NSNotificationCenter) de forma que es relativamente sencillo pisarse un pie con el otro, y hacer que una notificación sea escuchada por quien no debe.</p>

<p>Además, las notificaciones hacen el código un poco más difícil de leer, y tampoco es que sean demasiado amigables a la hora de pasar parámetros. </p>

<p>El Gran Dragóm, por tanto, inmerso como está en la búsqueda de la elegancia en su código, deshecha la segunda solución.</p>

<p>La tercera solución, sin embargo, es utilizar uno de los patrones más comunes en Cocoa: la delegación.</p>

<p>La extensión por delegación no es más que una forma de composición un poco más sofisticada. Una clase, la que se quiere extender, puede delegar detalles concretos de implementación (lo que es susceptible de cambiar entre una utilización y otra de la clase) en otras clases, de forma que, en tiempo de ejecución, se pueden delegar responsabilidades en clases concretas declaradas a ese efecto.</p>

<p>Ese patrón es muy sencillo de implementar en Objective-C, como bien sabe El Gran Dragón, gracias a la naturaleza tan dinámica del lenguaje, y a que siempre se puede saber si una clase va a poder responder o no a un mensaje.</p>

<p>El Gran Dragón comienza, por tanto, la escalada de la delegación declarando una variable de clase en la vista, junto con sus accesors:</p>

<p>#import <Cocoa/Cocoa.h></p>

<p><br />
@interface DNClickableView : NSView <br />
{<br />
	BOOL mouseIsPressingMe;<br />
	id delegate;<br />
}</p>

<p>- (id) delegate;<br />
- (void) setDelegate: (id) newDelegate;</p>

<p>-(void) viewWasClicked;<br />
@end</p>

<p>Además, declara un método que será ejecutado cuando se haga click en la vista (viewWasClicked)</p>

<p>Por tanto, los métodos dedicados a la captura de los clics quedarían así:</p>

<p>- ( void ) mouseDown: ( NSEvent * ) theEvent <br />
{<br />
	mouseIsPressingMe = YES;<br />
	[ self setNeedsDisplay: YES ];<br />
}</p>

<p>- (void)mouseUp:(NSEvent *)theEvent <br />
{<br />
    mouseIsPressingMe = NO;<br />
    [ self setNeedsDisplay: YES ];<br />
	<br />
	[ self viewWasClicked ];<br />
}</p>

<p>Los acessors para el delegate:<br />
- (id)delegate<br />
{<br />
	return delegate;<br />
}</p>

<p>- (void)setDelegate:(id)newDelegate <br />
{<br />
	delegate = newDelegate;<br />
}</p>

<p>El método que gestiona los clicks:</p>

<p>-(void) viewWasClicked<br />
{<br />
	if( [ delegate respondsToSelector: @selector( viewWasClicked ) ] )<br />
	{<br />
		[ delegate viewWasClicked ];<br />
	}<br />
}</p>

<p>Ahí es donde el Gran Dragón se siente más orgulloso. Para evitar estar enviando mensajes a nil, comprueba que lo que se haya asignado como delegate responde al mensaje viewWasClicked. En caso de responder, reenvía el mensaje a esa clase, y en caso de no responder a él, lo deja morir.</p>

<p>En el controlador, sólo faltaría asignar el delegate:</p>

<p>-(void) awakeFromNib<br />
{<br />
	[ clickView setDelegate: self ];<br />
}</p>

<p><br />
Por tanto, la implementación completa de la vista sería:</p>

<p>#import "DNClickableView.h"</p>

<p><br />
@implementation DNClickableView</p>

<p>- (id)initWithFrame:(NSRect)frame <br />
{<br />
    self = [super initWithFrame:frame];<br />
	<br />
    if (self) <br />
	{<br />
        // Initialization code here.<br />
		<br />
		mouseIsPressingMe = NO;<br />
    }<br />
	<br />
    return self;<br />
}</p>

<p>- (void)drawRect:(NSRect)rect <br />
{<br />
    if ( mouseIsPressingMe )<br />
	{<br />
		[ [ NSColor lightGrayColor ] set ];<br />
	}<br />
	else<br />
	{ <br />
		[ [ NSColor grayColor ] set ];<br />
	}<br />
	<br />
    NSRectFill( rect );<br />
}</p>

<p>#pragma mark -<br />
#pragma mark mouse handling</p>

<p>- ( void ) mouseDown: ( NSEvent * ) theEvent <br />
{<br />
	mouseIsPressingMe = YES;<br />
	[ self setNeedsDisplay: YES ];<br />
}</p>

<p>- (void)mouseUp:(NSEvent *)theEvent <br />
{<br />
    mouseIsPressingMe = NO;<br />
    [ self setNeedsDisplay: YES ];<br />
	<br />
	[ self viewWasClicked ];<br />
}</p>

<p><br />
#pragma mark -<br />
#pragma mark delegate handling</p>

<p>- (id)delegate<br />
{<br />
	return delegate;<br />
}</p>

<p>- (void)setDelegate:(id)newDelegate <br />
{<br />
	delegate = newDelegate;<br />
}</p>

<p>-(void) viewWasClicked<br />
{<br />
	if( [ delegate respondsToSelector: @selector( viewWasClicked ) ] )<br />
	{<br />
		[ delegate viewWasClicked ];<br />
	}<br />
}</p>

<p><br />
-(void) dealloc<br />
{<br />
	[ delegate release ];<br />
	[ super dealloc ];<br />
}<br />
@end</p>

<p><br />
y la del controlador:</p>

<p>@implementation DNAppController</p>

<p>-(void) awakeFromNib<br />
{<br />
	[ clickView setDelegate: self ];<br />
}</p>

<p>#pragma mark -<br />
#pragma mark DNClickableView delegate method</p>

<p>-(void) viewWasClicked<br />
{<br />
	NSLog( @"DNAppController.viewWasClicked" );<br />
}<br />
@end</p>

<p><br />
Mucho mejor. El Gran Dragón se siente satisfecho, porque aunque aparentemente haya implementado una solución similar a la primera que deshechó, ha ganado muchísimo en flexibilidad. En este ejemplo, el delegate se ha asignado al controlador, pero podría ser cualquier clase, instanciada directamente en el controlador, o producto de algún patrón de creación.</p>

<p>Sin embargo, el Gran Dragón sabe que su Gran Maestro, hombre noble y de infinita sabiduría pero muy exigente, no va a ver con buenos ojos que haya declarado la referencia al delegate como id, sin más.</p>

<p>Objective-C soporta protocolos, por lo que el método que se va a delegar, podría estar declarado en un protocolo, en un protocolo informal en realidad. ¿Porqué informal? Porque de esa forma no se obliga a nadie que lo cumpla a que implemente todos los métodos declarados en el mismo.</p>

<p>Por cierto, El Gran Dragón sabe que su solución está un pelín obsoleta con la introducción de Objective-C 2.0, ya que los ingenieros de Apple recomiendan que en la nueva iteración del lenguaje no se utilicen protocolos informales, sino que prefieren que se utilicen protocolos formales con métodos opcionales.</p>

<p>No obstante, El Gran Dragón quiere terminar con el proyecto, porque fuera del templo brilla el sol y le apetece salir a hacer fotos, así que comienza por declarar el protocolo informal, en la cabecera de la vista:</p>

<p>@interface DNClickableView : NSView <br />
{<br />
	BOOL mouseIsPressingMe;<br />
	id  delegate;<br />
}</p>

<p>- (id) delegate;<br />
- (void) setDelegate: (id) newDelegate;</p>

<p>@end</p>

<p>@interface NSObject ( DNClickableViewDelegate )<br />
-(void) viewWasClicked;<br />
@end</p>

<p><br />
De esa forma, el compilador creerá que todos los objetos conforman ese protocolo, aunque en realidad, al ser informal, no tienen porqué implementar el método declarado en él.</p>

<p>El Gran Dragón está contento. Su subclase de NSView puede reutilizarse en todos los proyectos, porque no está vinculada a ningún controlador concreto, ni siquiera a un interfaz de éste, y lo que es mejor, la funcionalidad que se quiere asignar al click del ratón, que puede ser diferente en cada caso, puede estar encapsulada en una clase que se le proporciona a la vista, sin que ésta sepa nada de ella, ni de sus detalles de implementación, ni de su forma de creación.</p>

<p>El Gran Maestro, mientras tanto, mira orgulloso a su pupilo, pensando ya en el próximo reto...</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003829.php</link>
         <guid>http://www.design-nation.net/es/archivos/003829.php</guid>
         <category>Cocoa</category>
         <pubDate>Tue, 08 Jan 2008 05:47:46 +0000</pubDate>
      </item>
            <item>
         <title>Instalación de git en Mac OS X 10.5 Leopard</title>
         <description><![CDATA[<p>Como estoy hasta el gorro de las peleas a brazo partido con Subversion, he decidido comenzar a utilizar <a href="http://git.or.cz/">git</a> para los proyectos nuevos.</p>

<p>La instalación de git en Leopard (Mac OS X 10.5.1) es muy sencilla. </p>

<p>El primer paso es descargar el tar con la última versión (a día de hoy la 1.5.3.7), y descomprimirlo (ya desde el Terminal):</p>

<p>tar xvzf git-1.5.3.7.tar.gz</p>

<p>A continuación, desde la carpeta generada al descomprimir el tar:</p>

<p>make configure<br />
./configure --prefix=/usr/local<br />
make all<br />
sudo make install</p>

<p>Y listo. Ya se pueden borrar tanto el tar como la carpeta donde éste se descomprimió.</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003828.php</link>
         <guid>http://www.design-nation.net/es/archivos/003828.php</guid>
         <category>Cocoa</category>
         <pubDate>Sun, 06 Jan 2008 13:21:52 +0000</pubDate>
      </item>
            <item>
         <title>¡Al abordaje!</title>
         <description><![CDATA[<p>Asunto espinoso éste del pirateo, pero que me apetece abordar ahora que veo los toros, al menos durante unas horas al día, desde el otro lado de la barrera.</p>

<p>Partimos de la siguiente premisa: tengo <a href="http://bambooapps.com/fnd/">una aplicación completa</a>, y <a href="http://bambooapps.com/arriba/">otra en espera de poder ser lanzada</a>, que pretendo vender, a un precio que considero razonable, y que creo que es proporcionado para lo que la aplicación puede ofrecer y para la utilidad que se le puede extraer.</p>

<p><strong>Hay 10 tipos de personas: los que pagan por el software y los que no lo hacen.</strong></p>

<p>Recuerdo una conversación en la última Caverna de los Hombres Malos. Yo estaba hablando con el compañero de la mesa de al lado, porque había recibido una oferta de Adobe por la que me podía hacer con un Photoshop CS3 por dos perras (no recuerdo sin 50 euros, o algo así), y le estaba preguntando si le interesaba a él, cuando el compañero de dos mesas más allá, sin esperar respuesta, saltó con un sonoro "¿para qué pagar por algo que puedes tener gratis?"</p>

<p>Hay que asumir que hay quien nunca va a comprar tu programa, ni cualquier otro. Punto pelota. No importa el precio ni la lista de funcionalidades, ni si es fácil o no de usar. No importa nada. Hay quien nunca va a gastar dinero en software, y no importa el porqué de su decisión.</p>

<p>Contra eso no se puede hacer nada, ni siquiera entrar a juzgarlo, así que lo mejor es olvidarlo y seguir adelante.</p>

<p><strong>La condición humana</strong></p>

<p>Si embargo, que alguien no esté en el grupo anterior no implica que vaya a pagar por tu programa.</p>

<p>Obviamente, uno no está en la situación de poder hacer lo que hacen Adobe, Microsoft o Apple, y pedirle al usuario el dinero por adelantado, antes de que pruebe el software. Por eso, lo lógico, lo razonable, incluso lo mejor a largo plazo es llegar a un acuerdo claro y sencillo: yo te doy una copia de mi programa, tú la pruebas 30 días, y se te gusta lo que has visto me das 13 dólares. No me importa lo que hagas con el programa pasado ese tiempo, no me importa que lo uses en uno, dos, tres o treinta ordenadores. Obviamente, preferiría que si lo vas a usar en 30 ordenadores me pagaras 30 veces, pero tampoco me voy a meter en eso. </p>

<p>Es tuyo, haz con ello lo que quieras. El plan es sencillo y fácil de recordar, ¿no?</p>

<p>Sin embargo, el plan tiene un fallo. Y bastante grande, por cierto: el ser humano no es así. </p>

<p>Seguro que hay quien está pendiente de si el período de prueba termina o no, de si debe hacer el pago o no, pero lo más normal es que si el software no te da un empujoncito, y te recuerda de alguna forma cuál era el trato original, aquí no ocurra nada. </p>

<p>Pero darte un empujoncito no es lo mismo que pegarte cuatro tiros, no. Tal  y como yo lo entiendo, un empujoncito es decirte: han pasado los 30 días; si no me das un número de serie válido, me cierro. Si el programa recibe un número de serie que valide contra el algoritmo que lo generó, aquí no ha pasado nada, y si no lo recibe, aquí tampoco ha pasado nada; cada uno por su lado, y punto.</p>

<p>Pero hay que ser comedido. Obviamente, no es justificable tocar el sistema de archivos del usuario ni su configuración de arranque, ni hacer ninguna perrería. Simplemente, hay que asumir que la relación no ha funcionado, y salir de esa relación con la mejor disposición posible para iniciar la siguiente. Sin acritud.</p>

<p><strong>Poner puertas al campo</strong></p>

<p>Dice el refrán castellano que no se pueden poner puertas al campo. En el mundo en el que vivimos no se puede pretender crear una protección anticopia inviolable.</p>

<p>Por eso, y pese a que haya quien considera esto como una demostración de egolatría, cuando llega el momento de ponerse a pensar en cómo dar el empujón a tus usuarios para que hagan el desembolso deseado, hay que ser realista. ¿Hasta qué punto merece la pena complicarse intentando lo imposible?</p>

<p>En mi caso, el límite está claro. Hago lo necesario para que esas personas a las que habitualmente nos referimos como "usuarios medios" crean que su única salida para seguir usando la aplicación, una vez pasado el período de prueba, sea pagar. A partir de ahí, cualquier esfuerzo es en vano. Siempre va a haber alguien que va a encontrar la forma de romper cualquier muralla que yo crea haber construido.</p>

<p>Por eso, creo que no merece la pena intentar construir esa muralla, y sí dedicar parte de ese tiempo a implementar cosas útiles para los usuarios de la aplicación. Que para eso han pagado, por cierto.</p>

<p><strong>¿Pero eso no es dejar la puerta entreabierta?</strong></p>

<p>Todo depende de cómo se quiera mirar, imagino. Habrá quien diga que no intentar blindar tu aplicación contra cualquier contingencia es más bien poco coherente, porque en el fondo estás dejando la puerta abierta para que quien quiera, encuentre la forma de utilizarla. </p>

<p>Claro que, ¿quién dejaría esa puerta abierta? ¿Y para qué?</p>

<p><strong>No tiene por qué ser malo que tu aplicación sea pirateada</strong></p>

<p>Así de simple. En muchas circunstancias, es bueno que tu aplicación sea pirateada. Es una forma de tener usuarios, de aumentar tu distribución, o si se quiere llamar así, de ganar cuota de mercado.</p>

<p>No sería la primera vez que alguno de los grandes (¿alguien se acuerda de windows 2000?), por un supuesto error, deja al alcance de todo el mundo una copia de alguno de sus productos, mientras hace como que no se ha dado cuenta y como que está mirando a otro lado.</p>

<p>Lo malo es que eso no es mucho más ético que borrarle la carpeta del sistema a un usuario al que le ha caducado la trial. No se puede pedir dinero con una mano mientras con la otra haces lo posible para que la distribución aumente a base de ofrecerlo gratis en canales de distribución no oficiales.</p>

<p><strong>¿En qué quedamos entonces?</strong></p>

<p>Pues en que, para mí al menos, el usuario ideal es el que compra mi programa después de haberlo probado, y de haber comprobado que cumple sus expectativas, que le puede resultar útil, bien sea ahora mismo o bien en un futuro no muy lejano. El usuario que ha meditado su decisión, que no compra por impulso, con el que puedo contar para comenzar a construir una segunda versión de la aplicación y que probablemente me va a aportar muy buenas ideas sobre cómo se puede mejorar el programa.</p>

<p>Y a ese tipo de usuario hay que cuidarle, sin marearle con procesos de activación ni de validación de licencias complicados, sin limitarle el uso del programa, sin considerarle un ladrón a priori, confiando en él y dándole, en definitiva, libertad absoluta para utilizar el programa a su antojo. Que para eso ha pagado.</p>

<p>¿Lo demás? Lo demás me da igual. No me importa si mi aplicación termina en mininova, no me importa si hay cuarenta números de serie por ahí corriendo. Podría hacerlo sin mucho problema, pero no voy a bloquearlos en siguientes versiones. ¿Por qué? Porque no creo que me estén robando, ni que me estén haciendo perder dinero. ¿Qué dinero pierdo yo porque alguien use mi programa sin pagarme por ello? Ciertamente, dejo de ingresar algo, pero no ingresar no es lo mismo que ser robado.</p>

<p>No creo que se pueda luchar contra el pirateo, porque ni siquiera sé muy bien si hay algo contra lo que luchar, ni mucho menos que se deba luchar activamente contra ello.</p>

<p>Simplemente, quiero que, aquél que encuentre útil mi programa se plantee que es justo hacerme llegar algo de dinero tanto para compensar el esfuerzo realizado, como para animar a seguir realizándolo. Nada más y nada menos.</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003827.php</link>
         <guid>http://www.design-nation.net/es/archivos/003827.php</guid>
         <category>Cocoa</category>
         <pubDate>Mon, 10 Dec 2007 05:26:47 +0000</pubDate>
      </item>
            <item>
         <title>FND 1.1</title>
         <description><![CDATA[<p>
Una nota rápida para informar del lanzamiento de la <a href="http://bambooapps.com/fnd/">versión 1.1 de FND</a>. 
</p><p>
La lista de nuevas funcionalidades es corta: soporte para el Apple Remote. Sin embargo, la importancia del release, para mí al menos, no está en la longitud de la lista de cambios respecto a la versión anterior, sino en que es el primer paso adelante en el roadmap previsto.
</p><p>
La nueva versión puede descargarse desde el mecanismo de actualización de la propia aplicación, o <a href="http://bambooapps.com/fnd/">directamente en bambooapps</a>.
</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003826.php</link>
         <guid>http://www.design-nation.net/es/archivos/003826.php</guid>
         <category>Cocoa</category>
         <pubDate>Thu, 06 Dec 2007 09:52:52 +0000</pubDate>
      </item>
            <item>
         <title>El retorno de MacSanta</title>
         <description><![CDATA[<p>
<img src="http://www.design-nation.net/es/macsanta125.png" height="125" width="125" border="1" align="left" hspace="4" vspace="4" alt="Macsanta125" />
<br />MacSanta, la promoción navideña en la que varios desarrolladores independientes ofrecen sus aplicaciones con descuentos muy importantes, y que dura todo el mes de diciembre, comienza en unas horas.
</p><p>
Este año, hay una novedad. Si en la edición anterior, se ofrecía un descuento del 20% en esas aplicaciones durante todo un mes, este año ese descuento se produce sólo un día, el día que la aplicación en cuestión esté en portada. El resto del mes, el descuento será del 10%
</p><p>
<a href="http://bambooapps.com/">bambooapps</a> participa en MacSanta con <a href="http://bambooapps.com/fnd/">FND</a>, y estaremos en portada durante hoy (porque en China ya es hoy desde hace un rato largo) día 1 de diciembre. Así pues, corred al sitio web de <a href="http://www.macsantadeals.com/">MacSanta</a>, para ver cómo beneficiaros del descuento,  y sobre todo, ver las fabulosas aplicaciones que podéis conseguir.
</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003825.php</link>
         <guid>http://www.design-nation.net/es/archivos/003825.php</guid>
         <category>Cocoa</category>
         <pubDate>Sat, 01 Dec 2007 01:41:47 +0000</pubDate>
      </item>
            <item>
         <title>Lo sé, lo sé</title>
         <description><![CDATA[<p>
No es nada, pero no está mal <a href="http://www.apple.com/downloads/macosx/productivity_tools/fnd.html">verse a uno mismo en la web de Apple</a>.
</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003824.php</link>
         <guid>http://www.design-nation.net/es/archivos/003824.php</guid>
         <category>Cocoa</category>
         <pubDate>Thu, 29 Nov 2007 05:44:37 +0000</pubDate>
      </item>
            <item>
         <title>FND postmortem</title>
         <description><![CDATA[<p>
Por fin, <a href="http://bambooapps.com/fnd/">el niño ha visto la luz</a>. Y aunque lo cierto es que en vez de un niño <a href="http://bambooapps.com/arriba/">yo quería niña</a>, y que el parto se ha adelantado, creo que es buen momento para sentarse, coger una cerveza y fumarse un puro a su salud y a la mía. Que ya habrá tiempo de preocuparse por si <a href="http://store.kagi.com/cgi-bin/store.cgi?storeID=6FFLD_LIVE&amp;&amp;">aprueba las mates</a> o no.
</p><p>
Por eso, porque tal vez sea interesante para alguien más, porque ya es casi una tradición entre todo el que lanza alguna aplicación para Mac OS X, y sobre todo porque me apetece soltarlo y verlo escrito, es por lo que quiero hacer esta especie de autopsia de lo que ha sido el proceso de desarrollo de una aplicación de escritorio.
</p><p>
<strong>Es importante tener un plan</strong>
<br />Es fundamental saber lo que se quiere hacer. Obviamente, eso no garantiza que el resultado final sea un éxito comercial, pero sí que te pone en el buen camino para conseguir que al menos exista ese resultado final.
</p><p>
Durante los dos meses en los que he estado trabajando en Arriba! y en FND, no ha habido un sólo día en el que no haya tenido una nanoidea para comenzar una nueva aplicación, nanoideas que morían a los cinco minutos de comenzar a reflexionar en serio sobre ellas.
</p><p>
Por eso me parece fundamental el tener claro lo que se pretende hacer, y seguir adelante por el camino marcado.
</p><p>
<strong>Es muy importante tener más de un plan</strong>
<br />Si algo he aprendido en estos meses es que no basta con que tú quieras hacer algo. Desgraciadamente, en un proceso tan largo y tan complejo hay mil una variables que te pueden desviar de tu camino, o que simplemente, te pueden hacer plantearte la necesidad de buscar una nueva vía.
</p><p>
Comencé a trabajar en FND para llenar los momentos de bloqueo en el desarrollo de Arriba!, para ayudarme a poner en el thread del fondo de la cabeza el punto muerto, el callejón sin salida en el que me había metido, a base de ocupar el supuesto grueso de la supuesta materia gris a resolver un problema completamente distinto. Al final, por circunstancias que espero que aún no sea tarde para cambiar, la aplicación que primero ha visto la luz ha sido la que desarrollaba como entretenimiento. 
</p><p>
Lo de trabajar en dos aplicaciones a la vez es algo que, a partir de ahora, pienso hacer siempre.
</p><p>
<strong>Es importante trabajar en algo que te resulte útil a ti mismo</strong>
<br />No es por tirarme el pisto ni dármelas de listo, pero hacer una aplicación de escritorio, por mucho que me gusten los frameworks Cocoa, y por mucho que éstos faciliten el trabajo, sigue siendo un auténtico dolor, y de los más inhumanos.
</p><p>
Por eso, aunque no por la razón habitual, aquella teoría de que así se producen aplicaciones más amigables, es muy conveniente que el desarrollador sea usuario de su propia aplicación.
</p><p>
Sinceramente, si no fuera porque necesitaba tanto Arriba! como FND, nunca las hubiera terminado. Son tantos los callejones sin salida, son tantas las vallas que hay que saltar, que la tentación de mandarlo todo al limbo de las aplicaciones inconclusas es enorme, y lo único que hace seguir adelante es pensar que tú vas a poder utilizar esa aplicación en la que estás trabajando y que has empezado a hacer porque no había ninguna otra cosa que te sirviera para tus malvados fines.
</p><p>
Por tanto, la mejor motivación para seguir es el producir algo para tu propio uso.
</p><p>
<strong>Es muy importante, y muy difícil, saber decir que no</strong>
<br />Sobre todo, saber decirle que no a uno mismo. Es muy fácil intentar que tu aplicación haga de todo, que tenga mil y una funcionalidades, que sirva para todo, que sincronice datos por .Mac, que pueda exportar a galerías web, que importe de XML, CSV, SVG, y /o cualquier otra sigla.
</p><p>
Pero lo difícil es decidir dónde cortar, decidir qué entra en la primera versión y qué no. Entre otras cosas, porque puedes estar seguro que siempre va a haber algún usuario para el que la funcionalidad que tú has dejado fuera es fundamental. Eso hay que asumirlo.
</p><p>
Por eso es muy importante el marcarse un objetivo realista, que permita entregar una aplicación que haga cosas, pero que permita entregarla, y a ser posible en un plazo no demasiado largo. No hay que olvidar que el precio, al menos en mi caso, tampoco es muy alto, por lo que el desarrollo no puede eternizarse.
</p><p>
Tiempo habrá para incluir más y mejores funcionalidades en versiones y subversiones venideras. Además, guste o no, de eso va el negocio del software, de estirar hasta más allá de lo imaginable una aplicación, para sacar el mayor rendimiento posible a su codebase.
</p><p>
Eso sí, algunas de las decapitaciones de la primera versión de Arriba! y de FND no han sido nada fáciles, desde un punto de vista estrictamente personal. Ayer, en planetamac, <a href="http://www.planetamac.es/fnd-ayuda-a-llevar-un-control-de-los-datos-de-revelado-de-peliculas/">oreixa apuntaba</a>, con más razón que un santo, que una aplicación como FND, desarrollada por un castellano-parlante, no está localizada en castellano, lo que no es algo especialmente bueno, que digamos.
</p><p>
Desgraciadamente, los números cantan, y localizar en castellano una aplicación tan "de nicho" como FND no es rentable, se mire por donde se mire. Y ésa es una de las cosas en las que hay que pensar cuando necesitas lanzar algo.
</p><p>
<strong>No todo es programar</strong>
<br />En realidad, en todo este proceso, lo que es programar en sí, supone sólo el 60% del trabajo, y eso con suerte. 
</p><p>
Hay que escribir la ayuda, montar la web, la tienda, testar la aplicación, montar algún sistema de bugtracking o de gestión de soporte, hacer o buscar la forma de que alguien te haga el icono de la aplicación, los iconos del toolbar, escribir los textos de la web...
</p><p>
Y lo peor de todo, que sí que entra dentro del "programar": el montar el sistema anti-pirateo de las aplicaciones. Sobre eso volveré otro día con más tiempo y ganas, pero básicamente, todo se puede resumir como "jamás he dedicado tanto tiempo a nada tan inútil" como el sistema de encriptación de los números de serie, y la validación de los períodos de prueba. Tiempo que podría haber dedicado a meter alguna feature más, por cierto.
</p><p>
<strong>Pero al final, merece la pena</strong>
<br />Sinceramente, no sé cómo funcionará todo esto, si realmente algún día llegará a merecer la pena económicamente o no, pero lo que sí que sé es que he disfrutado programando <a href="http://bambooapps.com/arriba/">Arriba! </a>y <a href="http://bambooapps.com/fnd/">FND</a> como no disfrutaba desde hacía años (profesionalmente hablando). Nunca había sentido nada comparable a la sensación, cercana al éxtasis, que produce el decidir lo que entra en tu programa y lo que no, lo que se hace y lo que no se hace, y sobre todo, el cómo se hace.
</p><p>
Pero sobre todo, merece la pena por todo lo que hay que aprender para poder salir de un proceso como éste con algo tangible en las manos.
</p><p>
Por eso, la sensación que se tiene cuando se cambia el número de versión a 1.0 y se compila es tan difícil de explicar. Por un lado es felicidad, mezclada con una sensación de absoluta libertad, pero por otro es también un gran alivio, con un cierto toque de miedo, no sé si al fracaso, o a exponerse al juicio y la valoración de otros.
</p><p>
Sin embargo, cuando entra el primer email con la notificación de una venta, entonces, ya sí, sólo quedan la felicidad, y el alivio.
</p><p>
Hasta el día 10 de enero, puedes <a href="http://store.kagi.com/cgi-bin/store.cgi?storeID=6FFLD_LIVE&amp;&amp;">comprar FND</a> con un 20% de descuento, utilizando el siguiente cupón:
</p><p>
DESIGN-NATION.
</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003823.php</link>
         <guid>http://www.design-nation.net/es/archivos/003823.php</guid>
         <category>Cocoa</category>
         <pubDate>Wed, 28 Nov 2007 15:34:19 +0000</pubDate>
      </item>
            <item>
         <title>Se va a intentar: bambooapps</title>
         <description><![CDATA[<p>
De forma un poco inesperada, pero no por ello menos deseada, tengo el placer de anunciar la nueva aventura de design-nation.
</p><p>
<a href="http://www.bambooapps.com">bambooapps</a> nace con una doble vocación. En primer lugar (por estricto orden de importancia), con la intención de que éste que les habla pueda hacer lo que de verdad le gusta, que no es otra cosa que programar, y en segundo lugar, con la intención de proporcionar aplicaciones para Mac OS X que puedan servir para simplificar o facilitar aspectos determinados de cualquiera de los posibles flujos de trabajo de un makero.
</p><p>
Hoy me complace presentar la primera aplicación de bambooapps: <a href="http://bambooapps.com/fnd/">FND (film is not dead)</a>
</p><p>
<img src="http://www.design-nation.net/es/fnd_interface_final_thumb-1.png" height="387" width="450" border="1" hspace="4" vspace="4" alt="Fnd Interface Final Thumb-1" />
</p><p>
FND viene a cubrir una doble necesidad personal, la de tener a mano, sin depender de una conexión a internet, una tabla con los tiempos de revelado de mis películas preferidas, en todas sus variaciones (de revelador y/o de exposición), y la de tener un reloj que se pudiera ver desde el otro lado de la casa, y que marcara los tiempos de revelado y agitación.
</p><p>
Como no quiero aburrir loando las alabanzas de mi aplicación, aunque sin duda podría hacerlo, me voy a tomar la libertad, nada más, de ofrecer a los sufridos lectores un cupón que permite comprar FND, hasta el próximo día 10 de enero, con un 20% de descuento sobre los 12,95$ de su precio original.
</p><p>
El cupón, que deberá introducirse en el momento de la compra,  es: <strong>DESIGN-NATION</strong>
</p><p>
<a href="http://bambooapps.com/fnd/">FND (film is not dead)</a>
</p>]]></description>
         <link>http://www.design-nation.net/es/archivos/003821.php</link>
         <guid>http://www.design-nation.net/es/archivos/003821.php</guid>
         <category>Cocoa</category>
         <pubDate>Tue, 27 Nov 2007 11:18:51 +0000</pubDate>
      </item>
      
   </channel>
</rss>
