iOS Tutorial Custom Dialogs with iCustomDialog library

Erel

Administrator
Staff member
Licensed User
Edit: Cross platform solution based on XUI: https://www.b4x.com/android/forum/threads/99756/#content

B4i v4.30 includes a new library named iCustomDialog. It is based on SCLAlertView open source project (https://github.com/dogo/SCLAlertView).

It makes it simple to create custom dialogs. It is quite similar to B4A CustomLayoutDialog from the Dialogs library (https://www.b4x.com/android/forum/threads/dialogs-library-2017-custom-dialogs-and-async-methods.80204/#content).



The steps to create a custom dialog are:

1. Create and initialize a new panel.
2. Set its size.
3. Load a layout file with the dialog layout.
4. Initialize a CustomDialog object.
5. Change the dialog style if you like.
6. Show it with ShowAsync.
7. Wait for the Diaog_Result event.

Example:
B4X:
'create the panel.
Dim p As Panel
p.Initialize("")
'set its size
p.SetLayoutAnimated(0, 1, 0, 0, Page1.RootPanel.Width - 50dip, 300dip)
'load the layout
p.LoadLayout("DetailsDialog")
'initialize the dialog
DetailsDialog.Initialize(p)
'show it
Dim sf As Object = DetailsDialog.ShowAsync("Enter Details", "OK", "CANCEL", "", False)
'wait for the result
Wait For (sf) Dialog_Result (Result As Int)
If Result = DetailsDialog.RESULT_POSITIVE Then
   Page1.Title = "Welcome " & txtFirstName.Text
End If
You can see the various styles here: https://github.com/dogo/SCLAlertView
STYLE_WAITING shows an animated waiting indicator.

The attached example is a bit more complicated. It disables the OK button until both text fields are not empty.
 

Attachments

Last edited:

Erel

Administrator
Staff member
Licensed User
Second example with a dialog that closes automatically after 10 seconds:



Relevant code:
B4X:
Sub btnDetails_Click
   Dim p As Panel
   p.Initialize("")
   p.SetLayoutAnimated(0, 1, 0, 0, Page1.RootPanel.Width - 50dip, 100dip)
   p.LoadLayout("TimedDialog")
   Dim TimedDialog As CustomLayoutDialog
   TimedDialog.Initialize(p)
   TimedDialog.Style = TimedDialog.STYLE_WAITING
   lblMessage.Text = "Do you want to do something?"
   Dim sf As Object = TimedDialog.ShowAsync("", "OK", "CANCEL", "", False)
   Dim disable() As Boolean = Array As Boolean(False)
   AutoClose(TimedDialog, lblSeconds, disable)
   Wait For (sf) Dialog_Result (Result As Int)
   disable(0) = True 'disable the AutoClose timer
   If Result = TimedDialog.RESULT_POSITIVE Then
     Log("Yes")
   End If
End Sub

Sub AutoClose (TimedDialog As CustomLayoutDialog, UpdateLabel As Label, Disable() As Boolean)
   UpdateLabel.Text = 9 '9 seconds
   Do While UpdateLabel.Text > 0
     Sleep(1000)
     'the timer was disabled
     If Disable(0) = True Then Return
     UpdateLabel.Text = NumberFormat(UpdateLabel.Text - 1, 0, 0)
   Loop
   TimedDialog.CloseDialog(TimedDialog.RESULT_CANCEL)
End Sub
It is explained in the B4A tutorial: https://www.b4x.com/android/forum/threads/dialogs-library-2017-custom-dialogs-and-async-methods.80204/#post-507930
 

Attachments

Last edited:

nwhitfield

Active Member
Licensed User
This looks great, but I'm trying to use it to pop up a panel for composing a new message, which has the OK and Cancel buttons, and a textview.

Once you tap in the textview to enable the keyboard, there's no way to hide it again, and consequently I can't complete the dialog.
 

Erel

Administrator
Staff member
Licensed User
Clicking on 'enter' button should hide it. Another option is to set the event name of the panel to pnlDialog and add this code:
B4X:
Sub pnlDialog_Click
   Dim p As Panel = Sender
   For Each v As View In p.GetAllViewsRecursive
     If v Is TextField Then
       Dim tf As TextField = v
       tf.ResignFocus
     End If
   Next
End Sub
 

nwhitfield

Active Member
Licensed User
Hmmm. Well, the Return/Enter option does remove the keyboard for a textfield, but not for a TextView, which is more suitable to sending a message. Which is a bit irksome.

And sadly the panel click event doesn't seem be be being propagated - I've put
B4X:
Log("Panel click")
in my sub, and it's never triggered.
For now, I'll just use a TextView; there'll be another way for people to send really long messages elsewhere anyway.
 

Erel

Administrator
Staff member
Licensed User
And sadly the panel click event doesn't seem be be being propagated - I've put
Have you set the event name to pnlDialog:
B4X:
p.Initialize("pnlDialog")
It will only be raised outside of the TextView.
You also need to modify the code I posted and change TextField with TextView.
 

nwhitfield

Active Member
Licensed User
Yes, it was named correctly. Problem solved however - and this may be useful for others:

The TextView was the only item on my panel, and in the designer I anchored it to all sides, so it filled the panel.

Of course, while the dialog looks as if it has space above the panel - where the title is - that's not the panel. So you need to have a bit of space at the top of the panel, before the TextView, where the user can tap, to hide the keyboard. (Or, if at some stage, there was an option to trigger an event on tapping in the dialog, but not on a field or button, that would fix it. So too, of course, would "Don't use TextViews in dialogs")
 

Misterbates

Active Member
Licensed User
@nwhitfield The following code implements three things that may be useful to you:
1) Using a panel to enclose a TextField or TextView then handling a click on the panel to ResignFocus from the active TextField/View, closing the keyboard.
2) Adding a "close keyboard" icon to the top of the keyboard (as a keyboard accessory) that when clicked, calls ResignFocus on the active TextField/View.
3) Changing the position of the CustomDialog on the display, so that the keyboard doesn't pop-up and cover the CustomDialog TextField/View.

B4X:
private Sub MoveCustomDialog(cd As CustomLayoutDialog, Top As Int)
    Dim no As NativeObject = cd
    Dim v As View = no.GetField("alertView").GetField("view")
    Do While v.Top = 0
        Sleep(5)
    Loop
    v.Top = Top
End Sub

private Sub AddCloseKeyboardView(no As NativeObject)
    Private const miKEYBOARD_CLOSE As String = Chr(0xE31A)
    Dim l As Label
    l.Initialize("lblHideKeyboard")
    l.Font = Font.CreateMaterialIcons(30)
    l.Text = miKEYBOARD_CLOSE
    l.TextColor = Colors.White
    l.TextAlignment = l.ALIGNMENT_RIGHT
    l.Height = 50dip
    l.Width = 50dip
    l.Color = Colors.Transparent
    l.Tag = no
    no.SetField("inputAccessoryView", l)
End Sub
private Sub lblHideKeyboard_Click
    Dim l As Label = Sender
    Dim v As Object = l.Tag
    If v Is TextField Then
        Dim tf As TextField = v
        If tf.IsFocused Then tf.ResignFocus
    Else If v Is TextView Then
        Dim tv As TextView = v
        If tv.IsFocused Then tv.ResignFocus
    End If
End Sub
private Sub pnlDialog_Click
    Log("pnlDialog_Click")
    Dim p As Panel = Sender
    For Each v As View In p.GetAllViewsRecursive
        If v Is TextField Then
            Dim tf As TextField = v
            If tf.IsFocused Then tf.ResignFocus
        Else If v Is TextView Then
            Dim tv As TextView = v
            If tf.IsFocused Then tf.ResignFocus
        End If
    Next
End Sub
The CustomDialog is built per the following example (so that the pnlDialog_Click event is fired):
B4X:
'Enables the user to change the value of a TextItem. Updates the item with the vew Value and returns TRUE if the Value was changed
public Sub ChangeTextItem(Item As PropertyEditorItem, Width As Int) As ResumableSub
    Dim Changed As Boolean ' Return TRUE if selection changed, FALSE otherwise
    'Setup custom panel
    Dim PanelWidth As Int = Width - 16dip
    Dim PanelHeight As Int = DipToCurrent(Min(100dip, Min(100%x, 100%y)))
    Dim p As Panel
    p.Initialize("pnlDialog")
    p.SetLayoutAnimated(200, 1, 0, 0, PanelWidth, PanelHeight)
    p.SetBorder(2dip, Colors.LightGray, 10)
    Dim tv As TextView
    tv.Initialize("")
    tv.Font = Font.CreateNew(18)
    tv.Text = Item.TextValue
    AddCloseKeyboardView(tv)
    p.AddView(tv, 8dip, 8dip, PanelWidth - 16dip, PanelHeight - 16dip)
    'Setup/show the dialog
    Dim d As CustomLayoutDialog
    d.Initialize(p)
    d.Style = d.STYLE_EDIT
    Dim sf As Object = d.ShowAsync("Edit text", "Done", "Cancel", "", False)
    MoveCustomDialog(d, 100) 'Move the dialog to the top of the display
    Wait For (sf) Dialog_Result (dr As Int) 'Wait for then return result
    Select dr
    Case d.RESULT_POSITIVE
        Dim NewValue As String = tv.Text
        Changed = (NewValue <> Item.TextValue)
        Item.TextValue = NewValue
    Case Else
    End Select
    Return Changed
End Sub
 
Last edited:

nwhitfield

Active Member
Licensed User
That's absolutely excellent - many thanks; I shall have a play with all that now I'm back from Berlin.
 

Erel

Administrator
Staff member
Licensed User
Based on the timed dialog example:
B4X:
Dim sf As Object = TimedDialog.ShowAsync("", "OK", "CANCEL", "", False)
Dim no As NativeObject = sf
Dim v As View = no.GetField("circleView")
v.Color = Colors.Red
TimedDialog.GetButton(TimedDialog.RESULT_POSITIVE).Color = Colors.Red
TimedDialog.GetButton(TimedDialog.RESULT_CANCEL).Color = Colors.Red
 

fox96

Member
Licensed User
Hi Erel,

I'm trying the example but arrived at this line in debugging the app closes without errors.

B4X:
DetailsDialog.Initialize(p)
Also trying with try closes. Even B4i-Bridge closes.
IPAD 4 iOS 8.3
B4i 4.81
B4X:
    Try
        DetailsDialog.Initialize(p)
    Catch
        Log(LastException)
    End Try
 

Erel

Administrator
Staff member
Licensed User
Do you have a different device with a more recent version of iOS? Can you try it?
 

Erel

Administrator
Staff member
Licensed User
It works on iPhone 4S running iOS 9. It is possible that it fails on older versions of iOS. Set the #MinVersion to 9.
 

ykucuk

Well-Known Member
Licensed User
is it possible to change font of the question and buttons? I tried with CSBuilder but it doesn't work.
 

Erel

Administrator
Staff member
Licensed User
No.

I recommend you to use B4XDialog for new projects. It is cross platform and fully customizable.
 
Top