iOS Keyboard / Alert / Inputbox (multilanguage)

Previous topic - Next topic

DaCarSoft

Hello!

Some time ago, I started to create my own keyboard for iPhone/iPad...    (you can see at: http://www.glbasic.com/forum/index.php?topic=5747.0)

I wanted to obtain a solid way to make appear the native iOS keyboard, trying to found the best way to mix the appearances of GLBasic results and native iOS controls. I supposed that the best way was to use the Xcode's "UIAlert", with a native textbox into. I saw some solutions in this forums related to this way, but none was working well because it may crash if you do a second call to the same functions to make appears the keyboard two times or more...   or it may crash if you (for example) try to write some non english characters and return them...

Now, I think that the "wrapper" is finished and the code is as simple as it could be...  (I know that it could look scary, I know...)  :D

The code supports non english characters to be returned to GLBasic, several text boxes and buttons that may vary from the GLBasic's code, and in combination with another codesnippet related to the screen orientation that I will publish immediately, you can also change the orientation of the controls to make it appears in the right way if you need it.

Here is the code:


In a file with ".mm" extensión that we should drag and drop into our Xcode project generated by GLBasic, we add:
Code (glbasic) Select


#if defined (TARGET_OS_IPHONE)



// UIALERT

//#import <UIKit/UIAlert.h>    // We don't need to import something imported before in the project...

@interface GLBasicMessageBoxer: NSObject <UIAlertViewDelegate>
{
    // Pointer declarations for the common elements
    UITextField* txtObject;
    NSMutableArray* resObjects;
    NSString* resValues;
    NSString* pTitle,* pMessage,* pLabels,* pButtons;
}
@end

@implementation GLBasicMessageBoxer

// Call needed to throw the code that shows an alert in the main thread
- (void)showAlertCaller:(NSString*)cTitle andMessage:(NSString*)cMessage andLabels:(NSString*)cLabels andButtons:(NSString*)cButtons
{
    // Addition of the needed values to accessible pointers from "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
{
// Alert's view creation
    UIAlertView* alert = [[UIAlertView alloc] init];
alert.title = pTitle;
alert.message = pMessage;
alert.delegate = self;
    // We need to force that the button with index 0 does not cancel the alert
    alert.cancelButtonIndex = -1;
// Creation of the button array using the separator "|"
NSArray* aButtons = [pButtons componentsSeparatedByString:@"|"];
    // Button creation
for (NSString* iObject in aButtons){
[alert addButtonWithTitle:iObject];
}
// Text controls array creation from the values separated by "|" from GLBasic
if (pLabels != @"") {
double iPos = 70.0;
NSArray* aLabels = [pLabels componentsSeparatedByString:@"|"];
        resObjects = [[NSMutableArray alloc] init];
        // Text control creation ensuring a not "(null)" value with .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;
            // We need to put the focus in this text control if it is the first
if ([aLabels indexOfObject:iObject] == 0) {
[txtObject becomeFirstResponder];
                // The keyboard appears magically when the focus is received by a control that can handle text
}
            // We need to calculate the value for the "top" coordinate of each text control
iPos = iPos + 30.0;
// Each control should be "linked" to the mutable array
            [resObjects addObject:txtObject];
            // XCode supports direct addition of text controls to an Alert, but Apple has not documented it and you can't use it for App store...
            // The workaround is to add "subviews" to an Alert with the needed controls
[alert addSubview:txtObject];
            [txtObject release];
            txtObject = nil;

}

}

[alert show];
[alert release];
}

// We need to obtain the button tapped using the "delegate" (= self) property in the "showAlert"
- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
resValues = [[NSString alloc] initWithFormat:@"%d", buttonIndex];
}

// We need to obtain and modify the values parsed from GLBasic to make a string with values separated with "|"
- (void)getValues
{
    // We need to add the button pressed index to the string that will contain all the values needed
    for (UITextField* iObject in resObjects){
        // Addition of the text control to a NSMutableString
        NSMutableString* tmpObjectValue = [[NSMutableString alloc] initWithString:iObject.text];
        // We need to replace the separator character if the user try to use it...
        [tmpObjectValue replaceOccurrencesOfString:@"|" withString:@"!" options:0 range:NSMakeRange(0, [tmpObjectValue length])];
        // All the values should be added to the same string
        resValues = [resValues stringByAppendingFormat:@"|%@", tmpObjectValue];
        // We need to free memory?
        [tmpObjectValue release];
        tmpObjectValue = nil;
    }
    // Creation of a "NSData" temo pointer that will contain the obtained values but after the process that adjust the "encodings"
    NSData* tmpValuesEncoded = [[NSData alloc] init];
    // String conversion to a Windows Latin "encoding" avoiding the not recognized chars using "allowLossyConversion"
    tmpValuesEncoded = [resValues dataUsingEncoding:NSWindowsCP1252StringEncoding allowLossyConversion:YES];
    // We need to use the correct "encoding" when "getValuesCaller" will return the string
    resValues = [[NSString alloc] initWithData:tmpValuesEncoded encoding:NSWindowsCP1252StringEncoding];
    // Freeing memory
    tmpValuesEncoded = nil;
}

// We need to obtain the pressed button values from the main thread
- (const char*)getValuesCaller
{
    // Was a button pressed before?
    if (resValues != nil){
        // Call to "getValues" from the main thread
        [self performSelectorOnMainThread:@selector(getValues) withObject:nil waitUntilDone:YES];
        // We need to return the values generated from "getValues"
        return [resValues cStringUsingEncoding:NSWindowsCP1252StringEncoding];
        // We need to free memory?
        [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
    {
        // If none of the buttons was pressed we should return a "" string
        return "";
    }
}

@end

// Pointer for "GLBasicMessageBoxer" declaration
GLBasicMessageBoxer* newAlert;



// External function for the use from GLBasic (The "labels" and "buttons" values may contain several values separated with "$")
extern "C" void iOSMessageBox(const char* cTitle, const char* cMessage, const char* cLabels, const char* cButtons)
{
newAlert = [[GLBasicMessageBoxer alloc] init];
    // Conversion from "C" to "NSString"
[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]];
}

// External function for GLBasic (The returned values are separated by "$")
extern "C" const char* GetiOSMessageBoxValues()
{
    // You obtain the índex for the pressed button first, and the text wrote in each control after...
return [newAlert getValuesCaller];
}

// FIN UIALERT



#endif



Now, from GLBasic we can do something like this:

Code (glbasic) Select


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

// Please, let me this time to do not translate the vars of my code...   :P
GLOBAL MENSAJE$

// Remember: the symbol is "|" now, but before in old versions of my code was "$"...
iOSMessageBox("Título","Mensaje" + CHR$(0xD) + CHR$(0xD) + CHR$(0xD) + CHR$(0xD), "Esto es una prueba ó ü Ñ |:P", "Send|Cancel")

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("Title","Message" + CHR$(0xD) + CHR$(0xD), "This is a second test", "OK|Cancel")   
// You should use the trick of CHR$(0xD) or "\n" to make the height of the alert grows...  (example: "Message\n\n")

InstanteComienzo% = GETTIMERALL()

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

MENSAJE$ = GetiOSMessageBoxValues()
WEND

PRINT MENSAJE$, 100, 100

SHOWSCREEN
HIBERNATE
MOUSEWAIT




As you can see, the values are sent as symbol separated strings and you can use it easily from GLBasic...  :)    also GetiOSMessageBoxValues returns as first parameter the button index pressed in the "alert".

You can try to put more or less values separated with "|" in the GLBasic call to iOSMessageBox    ;)

We can obtain a simple "alert" doing something like this:

Code (glbasic) Select


iOSMessageBox("Title","Test string", "", "OK")





Now...   someone can paste some photos of the results???



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

spicypixel

Awesome these code snippets are definitely being added to my projects ;) Great work DaCarSoft
http://www.spicypixel.net | http://www.facebook.com/SpicyPixel.NET

Comps Owned - ZX.81, ZX.48K, ZX.128K+2, Vic20, C64, Atari-ST, A500.600.1200, PC, Apple Mini-Mac.

trucidare

years ago i think 2  i send a code snippet like this to kitty for implementation in the iPhone lib. ;) but nothing happens :D
MacBook Pro 2,2 GHz Core 2 Duo, 4 GB RAM, 160 GB HDD, 8600M GT
Core i3 - 3,07 GHz, 8 GB Ram, 2.5 TB HDD, Geforce GTX 260+ OC

DaCarSoft

#3
May be Kitty hasn't time to look at your code until...   :D   :P

So many people asked for something like this (myself, for example... you know) before, but anybody published anything...
Almost anybody published a complete code that really works without all the possible fails and obtaining all the needed values...

With the best intentions: I think that it's time to let others to use the native keyboard ;)


At the other side, may be that this kind of code would be better if it is used like an "addon" to XCode, because this only works under iOS and may be needed some kind of changes in future iOS releases...

Regards.


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

spacefractal

#4
the command could been INPUT eventuelly me guess (or something like that)? iOS cant get input from keyboard anyway, so here the onscreen keyboard could use instead. The same could go for Android.

the main reason of characters that might crash is they might been unicode, and glbasic does not support that.

so glbasic need to been support that first, or convert it to utf-8 or something like that.
Genius.Greedy Mouse - Karma Miwa - Spot Race - CatchOut - PowerUp Elevation - The beagle Jam - Cave Heroes 2023 - https://spacefractal.itch.io/

DaCarSoft

Hi.

This code is completely an Input way for GLBasic under iOS, showing the native keyboard.

It also lets make simple alerts without any input, or "multi-inputs", its to say: You can make appears two text controls like username and password at a time in the same alert window and obtain the two strings.

I solved the encoding problem as better as I could, making some transformations between codepages and discarding the few incorrect characters that could bring unreadable/erroneous returns to GLBasic.

Test it and tell us how it goes ;)

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

bigsofty

Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

blackway

Is there any way to make it works in landscape???
thanks!!!

DaCarSoft

#8
Sure!

You can rotate the entire iOS orientation, to make it appear in landscape, but there are problems related with the "iCade" device support that the new updates of GLBasic implements. It has broken these functions because of an invisible Window that GLBasic uses internally to recover the iCade joystick and buttons data.

If you try to rotate the keyboard, the iCade window makes the keyboard appears in the center of the screen, and cutted...   :(

I suppose that Gernot could solve this problem in short, because the iCade is bringing some other problems.

If you have an old GLBasic version, you can make it work properly.

Anyway, you can test it.

To rotate the keyboard you have to use my other wrapper to force a change in the iOS orientation:

http://www.glbasic.com/forum/index.php?topic=6707.msg54084
(Spanish forums:)
http://www.glbasic.com/forum/index.php?topic=6467.msg51884

When you have this two wrappers (keyboard and orientation)  included in XCode, you have to make the call to rotate iOS just before the call to open the Alert/Keyboard.

This is an example of the GLBasic code:

Code (glbasic) Select


IMPORT "C" void iOSSetOrientation(const char*)

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

GLOBAL MENSAJE$

iOSSetOrientation("LandscapeLeft")

iOSMessageBox("Título","Mensaje" + CHR$(0xD) + CHR$(0xD) + CHR$(0xD) + CHR$(0xD), "This is a test|:D", "OK|Cancel")

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




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

blackway

Thank DaCarSoft for your reply!!!  :nw:
It works like a charm!
Best,

Hark0

http://litiopixel.blogspot.com
litiopixel.blogspot.com - Desarrollo videojuegos Indie · Pixel-Art · Retroinformática · Electrónica Development Indie Videogames · Pixel-Art · Retrocomputing · Electronic

mentalthink

1 Karma....

This forum don´t have but it´s a very good thing, sure!!!  =D =D

Awesome Code Snippet...

Falstaff

This is so amazing; I just wish there was an android equivalent :( I was hoping to target both ios and android and no built-in way to access the native input methods sucks .

But anyway, thanks for sharing, this is still awesome!

DaCarSoft

It's nothing ;)


I'm sorry, but I'm not working for Android (yet)...


iOS is my preferred platform.


Thanks for posting your opinion  :)
"Si quieres resultados distintos... no hagas siempre lo mismo" - Albert Einstein.