Problems modal alert windows on iOS


#1

Hi,

I'm having some difficulties with native modal alert windows on iOS 7 - e.g. showing a native OK/Cancel box and waiting for a reply. According to the documentation if you specify no callback to NativeMessageBox::showOKCancelBox then it should be modal, but what actually happens is it terminates the program.  Looking further in the native stuff this functionality seems to rely on the getResult() method in iOSMessageBox class. However, from what I can see, the method returns so long as the mesage box is visible or attached to a superview.

However it seems that the view is created without any superview so this falls through immediately. There is no check that a message box button has actually been pressed, which means that the routine can return and destroy both the message box and the delegate, before a button has been pressed. So when a button is actually pressed who knows what happens to the callback.

My solution to this is to add a "resultWasSet" flag to the class,  which is an additional check in the loop. This then waits for the button  to be pressed and responds correctly if the app moves to the background. Have I missed something essential here? My iOS skills are minimal [why do you think I'm using Juce :-)  ].  Code is here:

class iOSMessageBox

{

public:

    iOSMessageBox (const String& title, const String& message,

                   NSString* button1, NSString* button2, NSString* button3,

                   ModalComponentManager::Callback* cb, const bool async)

        : result (0), delegate (nil), alert (nil),

          callback (cb), isYesNo (button3 != nil), isAsync (async)

    {

        delegate = [[JuceAlertBoxDelegate alloc] init];

        delegate->owner = this;

        

        /*** Updated modal Box 6-12-2013 ***/

        resultSet = false;

        /*** end update ***/

        

        alert = [[UIAlertView alloc] initWithTitle: juceStringToNS (title)

                                           message: juceStringToNS (message)

                                          delegate: delegate

                                 cancelButtonTitle: button1

                                 otherButtonTitles: button2, button3, nil];

        [alert retain];

        [alert show];

    }


    ~iOSMessageBox()

    {

        [alert release];

        [delegate release];

    }


    int getResult()

    {

        jassert (callback == nullptr);


        JUCE_AUTORELEASEPOOL

        {

            /*** Updated modal Box 6-12-2013 ***/

            //while (! alert.hidden && alert.superview != nil)

            while ( ! alert.hidden && ! resultSet )

            /*** end update ***/

                [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];

        }

        return result;

    }


    void buttonClicked (const int buttonIndex) noexcept

    {

        result = buttonIndex;

        

        /*** Updated modal Box 6-12-2013 ***/

        resultSet = true;

        /*** end update ***/

        

        if (callback != nullptr)

            callback->modalStateFinished (result);


        if (isAsync)

            delete this;

    }


private:

    /*** Updated modal Box 6-12-2013 ***/

    bool resultSet;

    /*** end Update ***/

    

    int result;

    JuceAlertBoxDelegate* delegate;

    UIAlertView* alert;

    ScopedPointer<ModalComponentManager::Callback> callback;

    const bool isYesNo, isAsync;


    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSMessageBox)

};

 

 


#2

Thanks! Sorry I didn't notice your post until just now.. Good catch, and good idea - I've updated it now!


#3

is this broken again? It crashes when I don't specify a callback.


#4

Are you sure it's the same thing? Got a stack trace of the crash?