Teclado / InputBox para iOS

Previous topic - Next topic

DaCarSoft

¡Hola a todos!

Para celebrar que por fin he puesto un avatar en mi perfil...   :D  y animado al ver las últimas publicaciones (como la de iAds) y el buen "rollo" de este foro en español... he decidido hacer público este código, y así estrenar mi avatar por todo lo alto   :booze:   aunque, igual me ha quedado un poco grande   :S

Creo que cuando este tipo de "codesnippets" se hacen públicos pueden ser una gran ayuda para todos, porque así podremos avanzar más rápidamente con nuestros proyectos individuales y eliminar poco a poco las barreras que GLBasic pueda tener bajo iOS :P


Cuando empecé a escribir el código no tenía demasiados conocimientos de XCode, pero decidí que podría ser muy útil aprender lo necesario del lenguaje original de Apple para poder cubrir cualquier necesidad (fuese cual fuese) y llegar donde GLBasic no pudiera llegar, complementándolo...

Así que puede que alguno de vosotros ya se haya topado con este hilo: http://www.glbasic.com/forum/index.php?topic=5747.0
Lo arranqué para ver si alguien me echaba "un cable"...     pero al final nadie pudo ayudarme demasiado con los problemas más importantes (bajo mi punto de vista)  :'(  y me pareció que el código que alguien presentó no era muy "definitivo" o correcto del todo...
Añadiendo el hecho de que (y no es que me importen esas cosas) todo el mérito parecía al final de otros...     :blink:

En fin, no me enrollo más, vamos al grano:

En un archivo con extensión ".mm" que debemos arrastrar hasta nuestro proyecto en XCode (si es que no tenemos uno ya) agregamos:
Code (glbasic) Select


#if defined (TARGET_OS_IPHONE)



// UIALERT

//#import <UIKit/UIAlert.h>    // No es necesario duplicar la importación hecha con anterioridad

@interface GLBasicMessageBoxer: NSObject <UIAlertViewDelegate>
{
    // Declaración de los punteros para los elementos comunes
    UITextField* txtObject;
    NSMutableArray* resObjects;
    NSString* resValues;
    NSString* pTitle,* pMessage,* pLabels,* pButtons;
}
@end

@implementation GLBasicMessageBoxer

// Llamada necesaria para llamar al código que muestra el Alert en el proceso principal
- (void)showAlertCaller:(NSString*)cTitle andMessage:(NSString*)cMessage andLabels:(NSString*)cLabels andButtons:(NSString*)cButtons
{
    // Introducción de los valores pasados como parámetro punteros accesibles por "showAlert"
    pTitle = [[NSString alloc] initWithString:cTitle];
    pMessage = [[NSString alloc] initWithString:cMessage];
    pLabels = [[NSString alloc] initWithString:cLabels];
    pButtons = [[NSString alloc] initWithString:cButtons];
    // Llamada a "showAlert" enviándola al proceso principal
    [self performSelectorOnMainThread:@selector(showAlert) withObject:nil waitUntilDone:NO];
}
   
- (void)showAlert
{
// Creación de la vista para el Alert
    UIAlertView* alert = [[UIAlertView alloc] init];
alert.title = pTitle;
alert.message = pMessage;
alert.delegate = self;
    // Anulación de la asignación de la función de cancelar al botón con índice 0
    alert.cancelButtonIndex = -1;
// Creación de la matriz de botones introduciendo valores según los parámetros pasados separados por "|"
NSArray* aButtons = [pButtons componentsSeparatedByString:@"|"];
    // Creación de cada botón a partir de cada valor de la matriz
for (NSString* iObject in aButtons){
[alert addButtonWithTitle:iObject];
}
// Creación de la matriz de controles de texto introduciendo valores según los parámetros pasados separados por "|"
if (pLabels != @"") {
double iPos = 70.0;
NSArray* aLabels = [pLabels componentsSeparatedByString:@"|"];
        resObjects = [[NSMutableArray alloc] init];
        // Creación de cada control de texto con las propiedades adecuadas asegurando que no devuelve un valor "(null)" mediante .text = @""
for (NSString* iObject in aLabels){
txtObject = [[UITextField alloc] init];
txtObject.frame = CGRectMake(12.0, iPos, 260.0, 25.0);
            txtObject.text = @"";
txtObject.placeholder = iObject;
txtObject.backgroundColor = [UIColor whiteColor];
txtObject.clearButtonMode = UITextFieldViewModeWhileEditing;
txtObject.keyboardType = UIKeyboardTypeAlphabet;
txtObject.keyboardAppearance = UIKeyboardAppearanceAlert;
txtObject.autocapitalizationType = UITextAutocapitalizationTypeNone;
txtObject.autocorrectionType = UITextAutocorrectionTypeNo;
txtObject.borderStyle = UITextBorderStyleLine;
            // Colocación del foco si es el primer control creado para introducción de texto
if ([aLabels indexOfObject:iObject] == 0) {
[txtObject becomeFirstResponder];
                // El teclado aparece automáticamente al colocar el foco en un control de texto
}
            // Aumentar el valor de la variable que controla la coordenada de posición del "top" de cada control de texto
iPos = iPos + 30.0;
// Asociación de cada control a la matriz mutable
            [resObjects addObject:txtObject];
            // XCode soporta añadir controles de texto directamente a un Alert pero Apple no permite su uso directo al no estar documentado
            // Para poder imitar el comportamiento pueden agregarse "subvistas" al Alert e introducir los controles necesarios en ellas
[alert addSubview:txtObject];
            [txtObject release];
            txtObject = nil;

}

}

[alert show];
[alert release];
}

// Recogida del botón pulsado en la memoria mediante la propiedad "delegate" (= self) asignada en "showAlert"
- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
resValues = [[NSString alloc] initWithFormat:@"%d", buttonIndex];
}

// Código que obtiene y trata los valores para construir el resultado mediante valores separados por "|"
- (void)getValues
{
    // Agregado a la cadena del botón pulsado de los valores de cada control de texto en el Alert
    for (UITextField* iObject in resObjects){
        // Insertar el texto recogido del control en un NSMutableString
        NSMutableString* tmpObjectValue = [[NSMutableString alloc] initWithString:iObject.text];
        // Reemplazar el caracter usado en el código como separador de valores si el usuario lo ha introducido en los controles
        [tmpObjectValue replaceOccurrencesOfString:@"|" withString:@"!" options:0 range:NSMakeRange(0, [tmpObjectValue length])];
        // Agregar el valor obtenido al contenido anterior de la memoria
        resValues = [resValues stringByAppendingFormat:@"|%@", tmpObjectValue];
        // Liberar la memoria
        [tmpObjectValue release];
        tmpObjectValue = nil;
    }
    // Creación de otro puntero "NSData" temporal para albergar los valores obtenidos transformados para evitar errores por "encodings"
    NSData* tmpValuesEncoded = [[NSData alloc] init];
    // Conversión de la cadena obtenida a un "encoding" Windows Latin obviando los caracteres no reconocidos mediante "allowLossyConversion"
    tmpValuesEncoded = [resValues dataUsingEncoding:NSWindowsCP1252StringEncoding allowLossyConversion:YES];
    // Reintroducción de la cadena final convertida al "encoding" adecuado para que pueda devolverse desde "getValuesCaller"
    resValues = [[NSString alloc] initWithData:tmpValuesEncoded encoding:NSWindowsCP1252StringEncoding];
    // Liberar la memoria
    tmpValuesEncoded = nil;
}

// Obtención de los valores del botón pulsado y texto introducido en los controles recuperando los valores desde el proceso principal
- (const char*)getValuesCaller
{
    // Comprobación de si se ha pulsado un botón del Alert
    if (resValues != nil){
        // Llamada a "getValues" en el proceso principal
        [self performSelectorOnMainThread:@selector(getValues) withObject:nil waitUntilDone:YES];
        // Devolvemos el valor de la cadena preparada desde "getValues"
        return [resValues cStringUsingEncoding:NSWindowsCP1252StringEncoding];
        // Liberar la memoria
        [resObjects release];
        resObjects = nil;
        [resValues release];
        resValues = nil;
        [pTitle release];
        pTitle = nil;
        [pMessage release];
        pMessage = nil;
        [pLabels release];
        pLabels = nil;
        [pButtons release];
        pButtons = nil;
        [self release];
        [super dealloc];
    }
    else
    {
        // Si no se ha pulsado ningún botón se devuelve una cadena vacía
        return "";
    }
}

@end

// Creación del puntero para las funciones contenidas en "GLBasicMessageBoxer" a usar cada vez que se requiera un Alert
GLBasicMessageBoxer* newAlert;



// Creación de función "externalizada" a usar desde GLBasic (los parámetros "labels" y "buttons" pueden contener varios valores separados con "$")
extern "C" void iOSMessageBox(const char* cTitle, const char* cMessage, const char* cLabels, const char* cButtons)
{
newAlert = [[GLBasicMessageBoxer alloc] init];
    // Conversión de tipos "C" a "NSString" al recuperar los parámetros enviados desde GLBasic y lanzado del Alert
[newAlert showAlertCaller:[NSString stringWithCString:cTitle encoding:NSASCIIStringEncoding] andMessage:[NSString stringWithCString:cMessage encoding:NSASCIIStringEncoding] andLabels:[NSString stringWithCString:cLabels encoding:NSASCIIStringEncoding] andButtons:[NSString stringWithCString:cButtons encoding:NSASCIIStringEncoding]];
}

// Creación de la función "externalizada" a usar desde GLBasic (los parámetros devueltos son valores separados con "$")
extern "C" const char* GetiOSMessageBoxValues()
{
    // Se recuperan en primer lugar el índice de botón pulsado y a continuación los textos introducidos en los controles generados
return [newAlert getValuesCaller];
}

// FIN UIALERT



#endif



Después, desde GLBasic, podríamos hacer algo como esto:

Code (glbasic) Select


IMPORT "C" void iOSMessageBox(const char*,const char*, const char*, const char*)
IMPORT "C" const char* GetiOSMessageBoxValues()

GLOBAL MENSAJE$

// El nuevo símbolo de separación para los parámetros a pasar a la función es la "tubería", antes era $, en mi código anterior...
iOSMessageBox("Título","Mensaje" + CHR$(0xD) + CHR$(0xD) + CHR$(0xD) + CHR$(0xD), "Esto es una prueba  ü Ñ |:P", "Aceptar|Cancelar")

LOCAL InstanteComienzo% = GETTIMERALL()

MENSAJE$ = ""
WHILE MENSAJE$ = ""
SLEEP 500
WHILE ABS(GETTIMERALL() - InstanteComienzo%) < 500
HIBERNATE
WEND

MENSAJE$ = GetiOSMessageBoxValues()
WEND

PRINT MENSAJE$, 100, 100

SHOWSCREEN
HIBERNATE
MOUSEWAIT



iOSMessageBox("Título","Mensaje" + CHR$(0xD) + CHR$(0xD), "Esto es otra prueba", "Aceptar|Cancelar")

InstanteComienzo% = GETTIMERALL()

MENSAJE$ = ""
WHILE MENSAJE$ = ""
SLEEP 500
WHILE ABS(GETTIMERALL() - InstanteComienzo%) < 500
HIBERNATE
WEND

MENSAJE$ = GetiOSMessageBoxValues()
WEND

PRINT MENSAJE$, 100, 100

SHOWSCREEN
HIBERNATE
MOUSEWAIT




Como vereis, las funciones envían y recogen los valores como una cadena de valores separados por "|" (tubería), que desde GLBasic es muy fácil tratar...  :)     además GetiOSMessageBoxValues devuelve como primer parámetro el índice del botón pulsado en el "alert".

También es totalmente parametrizable en cuanto a botones y cantidad de casillas de texto, probad a introducir más o menos valores separados por "|" en iOSMessageBox    ;)

Así, podemos conseguir un simple "alert" desde GLBasic haciendo algo como esto:

Code (glbasic) Select


iOSMessageBox("Título","Mensaje de prueba", "", "Aceptar")



Y bueno...   si alguien tiene alguna duda que lo pregunte por aquí y estaré encantado de hacer cualquier aclaración...

Tengo planeadas muchas "ampliaciones" al código a partir de aquí, como por ejemplo hacer aparecer el teclado, y recoger los caracteres, sin necesidad de un alert...  aunque si alguien se atreve con ello ántes que yo...   :D


Si alguien lo va probando me encantaría que posteara que tal le ha ido...

Y ya... si alguien que utilice éste código quiere simplemente mencionarme de pasada en los "agradecimientos" de los créditos de su juego...   Hago una fiesta, vamos...    XDDDDDDDDDDDD


Weno, ahí queda eso, ánimo con vuestro código!!!!


-David-
"Si quieres resultados distintos... no hagas siempre lo mismo" - Albert Einstein.

msx

Bueno David, seré el primero en felicitarte por tus avances. Seguro que nos será de ayuda en algún momento. Te agradezco que lo publiques.

El problema que yo veo en este tema, es que si nuestra intención es hacer una aplicación/juego multiplataforma, entonces tendremos que ir adaptándolo a cada SDK. Sin embargo si nuestra intención es publicarlo solo para iPhone desde luego que es una chulada.

P.D.: En cuanto a tu avatar... umm la cosa está tensa, no sé si es una buena idea  :S

DaCarSoft

Gracias por la felicitación MSX...    ;)

Desde luego, esto, como con el uso de iAds, In-App purchases y demás, no tiene sentido en plataformas distintas a iOS...    pero siempre podemos poner algún "IF" que otro para hacer algo distinto en otras plataformas...    quizá lo mejor sería que GLBasic tuviese sus propias instrucciones para hacer aparecer "alerts" o el teclado y usase las funciones propias de cada sistema operativo al elegir la plataforma en el momento de la compilación...    pero creo que eso ya sería pedir demasiado...    :P


En cuanto a mi avatar...  :)   siempre es bueno ver una cara sonriente, a pesar del momento...    :)   además me gusta su X...  ;)
"Si quieres resultados distintos... no hagas siempre lo mismo" - Albert Einstein.

mentalthink

Hola David, bueno sobre tu avatar, lo primero me ENCANTA!!!, y aún más que estes haciendo código con Xcode, ya que justo hoy yo he empezado ha intentar pasar funciones desde Glbasic a Xcode, y bueno para no tener ni idea me ha enganchado un poco. Aunque he leído el post por encima, creo que me va aportar un gran "empujón" en esto de utilizar comandos externos. Lo que dice MSX tiene razón, de que solo funcione para el iOS, pero no todo el mundo en Glbasic programa para todas las plataformas, incluso el otro día leí un usuasrio que solo lo utiliza para Windows.

Sin más darte las gracias, por compartir un code-Snippet tan útil, no solo por su funcionamiento, sino por los conocimientos que puedes aportar con él.

Un saludo, y gracias, te doy la bienvenida de mi parte, porque nunca había charlado contigo.

Iván J.

DaCarSoft


Hola Iván, me alegro de que te guste el avatar ;)

Hummmm...   no creo que sea la primera vez que hablamos...   creo que cruzamos algún post poco después de abrir el foro en Español...   Lo que pasa es que como en aquel anuncio de TV, paso desapercibido a veces, hasta para el aire acondicionado con sensor de movimiento...   XDDDDDDD

En breve postearé más código, si puedo, esta misma noche, que seguro os va a encantar...

Mi plataforma preferente ahora mismo es iOS, por eso no le doy tanta importancia a otras plataformas...   por lo menos de momento.

Enhorabuena, por "picarte" y animarte a hacer cosas en XCode...    A mi me pasó lo mismo, empecé esto del teclado sin tener NI IDEA de XCode, pero al final creo que merece la pena la recompensa de verlo funcionando, mezclado con tu código de GLBasic, y lo que es más importante: Sin cuelgues (o eso espero, confirmádmelo por favor... ;))

Si tienes dudas o necesitas un cable, puedes preguntarme lo que quieras...  que si puedo ayudarte lo haré encantado, aunque no me considero ningún gurú del lenguaje de la manzana, pero seguro que entre todos encontramos solución y caminos a cualquier cosa.

Por lo demás: de nada!    y...    gracias por la RE-Bienvenida!!!   XDDDDDD

-David-
"Si quieres resultados distintos... no hagas siempre lo mismo" - Albert Einstein.

Hark0

Más te vale que no termine incluyendo tu código y tu nombre en mis agradecimientos... poruqe me apunto a la fiesta!!!

Una vez más enhorabuena por tu trabajo :D
http://litiopixel.blogspot.com
litiopixel.blogspot.com - Desarrollo videojuegos Indie · Pixel-Art · Retroinformática · Electrónica Development Indie Videogames · Pixel-Art · Retrocomputing · Electronic

blackway

Gracias a DaCarSoft, Ampos, MSX, Iván y un largo etcétera por compartir estos estupendos códigos fuentes que dan un valor agregado al GLBasic bajo Iphone/Ipod/Ipad....
Ojalá alguno de vosotros pueda con Game Center  ;)   
De todos modos ya me animaré a mirar un poco el Objetive C e intentar armar algo en conjunto con el GLBasic... !!!!
Saludos...

DaCarSoft


Para Hark0:  Jajajajaja...   No es necesario tampoco buscar muchas excusas!!!  La fiesta la organizo hasta sin código fuente de por medio!!!

Y por cierto...   que nadie se sienta obligado a ponerme en ningún sitio en sus creaciones...   no sea que entonces yo deje de ser tan...   "anónimo-us"...  :D :D :D

Para Blackway: "Denadas"!, Lo del GameCenter es cuestión de tiempo :P  Ánimo con Objective-C!!!!
"Si quieres resultados distintos... no hagas siempre lo mismo" - Albert Einstein.

aroldo

#8
DaCarSoft

Disculpen mi español.
También estoy aprendiendo un poco más de XCode, gracias por su código.

En mi proyecto tengo el archivo iOSKeyBoadTest.gbas y el archivo iOSKeyBoadTest.mm

Acabo de intentar su código, pero estoy recibiendo los siguientes errores en GLB.

Code (glbasic) Select
inking:
gpc_temp0.o:gpc_temp0.cpp:(.text+0x1f6): undefined reference to `_iOSMessageBox'
gpc_temp0.o:gpc_temp0.cpp:(.text+0x59a): undefined reference to `_GetiOSMessageBoxValues'
gpc_temp0.o:gpc_temp0.cpp:(.text+0x6ed): undefined reference to `_iOSMessageBox'
gpc_temp0.o:gpc_temp0.cpp:(.text+0x8ef): undefined reference to `_GetiOSMessageBoxValues'
*** FATAL ERROR - Please post this output in the forum


¿Necesito tambien un archivo iOSKeyBoadTest.h ?

Saludos
[a http://apd-games.com/][img http://apd-games.com/images/APDGames135.png][/a]
MacBook Pro OS X El Capitan
XCode Version 7
iPhone 6 running  iOS 9
iPad Mini running  iOS 7.1
Galaxy S5
Dell Latitude Windows 8 Enterprise
Palm Pre, Palm Pre2

DaCarSoft

Hola Aroldo

Ahora mismo no estoy cerca de mi portátil pero intentaré responderte :)

No es necesario un archivo ".h"

Yo he tenido problemas con los wrappers en GLBasic 11 (Beta)... ¿Que versión de GLBasic estas usando?

Los problemas con la versión 11 pueden resolverse, pero prueba antes con GLBasic 10, si tienes ocasión.

GLBasic 11 modifica el proyecto Xcode contínuamente sobreescribiendo todo cada vez que compilas. Habría que generar un proyecto nuevo bajo Xcode sin usar el que proporciona GLBasic automáticamente para que los wrappers puedan funcionar bien en GLBasic 11.

Por otro lado, ¿¿¿has arrastrado el archivo .mm dentro del proyecto Xcode??? Por ejemplo, dentro de la carpeta "classes"...



Buena, suerte!!! Cuéntanos tus resultados.
"Si quieres resultados distintos... no hagas siempre lo mismo" - Albert Einstein.

aroldo

#10
DaCarSoft
Gracias por su respuesta. Tengo tanto GLB V10.xxx y GLB 11.xxx
El problema de compilación " Build-Multiplatform"  está ocurriendo tanto en V10 y V11.
Si el archivo. mm está dentro de la carpeta correcta, pero como he dicho el error ocurre en GLBasic y no en XCode.

Creo que falta algo en la configuración del proyecto GBL.

Si yo ejecuto mi código en Xcode el linker se queja de símbolos no definidos.

Code (glbasic) Select
Undefined symbols for architecture armv7:
  "_iOSMessageBox", referenced from:
      __GLBASIC__::__MainGameSub_() in libPROGRAM.a(gpc_temp0.o)
  "_GetiOSMessageBoxValues", referenced from:
      __GLBASIC__::__MainGameSub_() in libPROGRAM.a(gpc_temp0.o)
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)


[a http://apd-games.com/][img http://apd-games.com/images/APDGames135.png][/a]
MacBook Pro OS X El Capitan
XCode Version 7
iPhone 6 running  iOS 9
iPad Mini running  iOS 7.1
Galaxy S5
Dell Latitude Windows 8 Enterprise
Palm Pre, Palm Pre2

aroldo

DaCarSoft,

Ok movido el archivo. Mm a la carpeta de clases en XCode y ahora tengo diferentes errores.

ver las capturas de pantalla!
[a http://apd-games.com/][img http://apd-games.com/images/APDGames135.png][/a]
MacBook Pro OS X El Capitan
XCode Version 7
iPhone 6 running  iOS 9
iPad Mini running  iOS 7.1
Galaxy S5
Dell Latitude Windows 8 Enterprise
Palm Pre, Palm Pre2

DaCarSoft

Parece que ahora estas sufriendo exactamente el mismo tipo de errores que yo estaba sufriendo con la Beta 11, sospecho que está relacionado con las últimas versiones actualizadas de Xcode, lo comenté todo aquí:

http://www.glbasic.com/forum/index.php?topic=8544.msg79309#msg79309


Yo pude resolver mi problema así:

-Descargué y reinstalé la última versión de la Beta 11 de GLBasic.

-Generé mi propio proyecto de Xcode, borrando y descartando el que generaba GLBasic 11 automáticamente (comprobé que GLBasic 11 tras compilar, borra cualquier cambio hecho en el proyecto de Xcode, por lo que si añades un wrapper a un proyecto de GLBasic 11, éste lo eliminará al sobreescribir el proyecto modificado). Para poder solucionar esto, tuve que borrar los archivos del proyecto Xcode que usa GLBasic cada vez que compila, buscando la carpeta de origen que usa GLBasic, normalmente situada en "C:\Program Files\GLBasic\Compiler\Platform\iPhone\Xcode" pero sin borrar ni tocar las carpetas "Libs", "Media" y "Classes".

-Después, sustituí en el proyecto GLBasic el anterior proyecto de Xcode por mi nuevo proyecto hecho desde cero y manualmente por mí, incluyendo manualmente las librerías precompiladas ".a" que usa GLBasic 11 y los frameworks necesarios.

Es una solución muy compleja para un problema de GLBasic que supongo que Gernot solucionará algún día cuando tenga la Beta 11 más avanzada...   Yo comenté el problema, pero nadie prestó atención.

Normalmente, publicaría un archivo "ZIP" aquí mismo en el foro, con los archivos de Xcode corregidos que yo mismo confeccioné, o incluso un proyecto completo...   pero no puedo hacerlo debido a problemas relacionados con alguna oferta de trabajo recibida y publicada en este foro, relacionada con varias modificaciones a un juego o dos (al menos uno de ellos llamado "Biiirds") en el que habría que incluír wrappers...     Como al final el asunto no ha terminado bien, sin saber exactamente lo que ha sucedido, no puedo/quiero publicar nada más en relación con mi trabajo en diferentes wrappers para iOS, para no ayudar directa o indirectamente a los responsables, sintiéndolo mucho...


"Si quieres resultados distintos... no hagas siempre lo mismo" - Albert Einstein.

aroldo

DaCarSoft,

Muchas gracias.
Entiendo lo que tu dices.
Voy a encontrar la solución, tendrá que pasar cierto tiempo de investigación. De esta manera voy a aprender más acerca de XCode.

Saludos
[a http://apd-games.com/][img http://apd-games.com/images/APDGames135.png][/a]
MacBook Pro OS X El Capitan
XCode Version 7
iPhone 6 running  iOS 9
iPad Mini running  iOS 7.1
Galaxy S5
Dell Latitude Windows 8 Enterprise
Palm Pre, Palm Pre2