revUp - Updates and news for the LiveCode community
Issue 133 | April 20th 2012 Contact the Editor | How to Contribute

MyMap: Stick a Pin in Externals
Add new features to your app using ready-made externals with mergExt and MobGUI

by Monte Goulding

LiveCode has a rich feature set that supports a huge range of the features specific to iOS apps. But what if you want to add a feature to your app not directly supported by LiveCode? You can either get to grips with the iOS Externals interface for LiveCode in order to add your feature, or let someone else do the heavy lifting for you.

To follow along with this hands on tutorial, you will need a copy of both mergExt and MobGUI. These popular addons are on offer by special arrangement with their authors, you can get them together for half price until May 4th 2012, using the coupon code MERGUI. Put both products in your basket and apply the coupon for 50% off.

Meet mergExt
mergExt is a rapidly growing suite of externals for LiveCode. The suite is iOS focussed at the moment but will evolve to be as cross platform as LiveCode. Included in the suite is:

  • mergAnswerColor - presents a modal color picker.
  • mergAV - adds functions and commands related to the AVFoundation framework. This currently includes picking, recording, saving and editing video.
  • mergBanner - adds an iAd banner to your app at the bottom of the screen.
  • mergDoc - present a modal document preview, open in app menu, document options menu and requesting the document icons in PNG form. Printing is also available via the preview.
  • mergMessage - send an SMS message.
  • mergMK - adds a map control which supports showing user location with heading, adding annotation pins and polylines.
  • mergPop - present an action sheet (popover on iPad) and contextual menu for user interaction.
  • mergTweet - send a tweet message.
  • mergXattr - set the do not backup and protection attributes of a file.
  • mergZXing - read barcodes via the back camera of a mobile device.

App concept
When I was asked to write an article for revUp I had to come up with an idea. Just talking about mergExt would have been a little dull (well I'm sure some would still be excited) compared to creating something real. With mergMK (MapKit) and mergPop (action sheet) externals just added to the mergExt suite I wanted to demonstrate these powerful yet simple externals in an app. The concept I came up with is a map annotation app that allows users to easily mark up a map with text/images/video called MyMap. I decided to also use MobGUI for a couple of reasons: I just got a free copy when I upgraded LiveCode and wanted to play; it will be easy to make the app look pretty without much work. We will also use mergDoc for interacting with files and mergAV for recording/picking video.

Let's get started
Open up LiveCode and MobGUI, create a new stack named MyMap with 2 cards "Map" and "Detail" and use MobGUI to edit its properties. Include the status bar as translucent (this will overlay the map) then add the MobGUI preOpenCard code to both cards. We will keep things simple by only allowing portrait orientation for iPhone. MobGUI handles the orientations and screen sizes really easily so it won't be hard to add that later if we want.

App navigation
On the map card we just add a background (to display before the map loads) and a tab bar at the bottom with a Drop Pin button. Edit the touchEnd command of the Drop Pin button to read:

on touchEnd pId
      mobGUIUntouch the long id of me
      # LC5 : new visual effect syntax
      lock screen for visual effect
      set the uAnnotationID of cd "Details" to ""
      go card "Details"
      wait for 0 millisecs with messages
      unlock screen with visual effect "push left very fast"
end touchEnd

The uAnnotationID is how we will tell the Details card to lookup the annotation. If it's empty in the preOpenCard handler we will just clear everything.

Follow the same procedure with the Details card but this time name the button Back and push right to the Map card. Next we want to create a user interface for editing the annotation details. Add labels and native text controls for the Title, Subtitle and a Notes multi-line field. I'm going to add more options here later but for now I think it's time to start playing with mergMK.

Adding mergMK to our stack
Open the Standalone Application Settings dialog. Before we start we should go to the iOS tab and set the build for iPod and iPhone, 4.0 or later and Universal. Give it an app id and provisioning profile and set GPS, Location Services, Still Camera and Video Camera to required. Set the status bar to visible, black transparent and theiPhone Initial Orientation to Portrait. Now go to the Copy Files tab and click Add Folder... Browse to and choose your iOS/binaries folder in the mergMK folder. While we are here let's repeat the process for mergPop, mergAV and mergDoc.

Creating and showing a map
Now that our externals are added let's create a map and display it on the Map card. Edit the card script as follows:

local sMapCreated = false

on preOpenCard
      # mobGUIStart ---> Inserted by mobGUI plugin
      if"MobGUILib-1334708811870.287842" is not among the lines of \
         the stacksInUse then start using stack \
"MobGUILib-1334708811870.287842"
   mobGUIPreOpenCard me    # mobGUIEnd    ifthe environment is "mobile" and not sMapCreated then       send initMap to me in 200 milliseconds    end if end preOpenCard on openCard    ifthe environment is "mobile" and sMapCreated then       -- send after the openCard is completed otherwise the       -- map will flash on screen before the visual effect       send showMap to me in 1 millisecond    end if end openCard on showMap    mergMKSet "visible",true end showMap on initMap    mergMKCreate    put true into sMapCreated    mergMKSet "rect",(0,0,the width of this stack,the top of \ group "TabBar")    mergMKSet "user tracking mode","follow with heading"    mergMKSet "visible",true end initMap on closeCard    ifthe environment is "mobile" then       mergMKSet "visible",false    end if end closeCard

Note that your MobGUILib won't be the same stack name as mine. Here's what we see in the simulator.

Annotations
It's time to add some annotations to our map but before we can do that we need to work out how to save the annotation data. I want to keep things simple for this tutorial so I'm going to go with a stack file saved in the Documents directory called userdata.dat (make it easy for Apple to pass). Each annotation will be a custom property set with keys title, subtitle, notes, coordinate, photos, videos. We will ignore the photos and videos keys for now and come back to them in Part 2. We will use the seconds command to generate unique names for the custom property sets.

On first run if there's no userdata.dat file then we need to create it. Then we need to loop over the custompropertysets and add an annotation for each one. mergMK will give us an id for each annotation so we will use a global variable to maintain the relationship between custom property set name and annotation id. Our new map card script is now as follows:

global gIDs
local sMapCreated = false

on preOpenCard
      # mobGUIStart ---> Inserted by mobGUI plugin
      if"MobGUILib-1334708811870.287842" is not among the lines of \
         the stacksInUse thenstart using stack \
"MobGUILib-1334708811870.287842"
      mobGUIPreOpenCard me
      # mobGUIEnd
      ifthe environment is "mobile" and not sMapCreated then
            send initMap to me in 200 milliseconds
      end if
end preOpenCard

on openCard
      ifthe environment is "mobile" and sMapCreated then
            -- send after the openCard is completed otherwise the 
            -- map will flash on screen before the visual effect
            send showMap to me in 1 millisecond
      end if
end openCard

on showMap
      -- add or update any annotations on the map and save data etc
      switchthe uAnnotationID of cd "Details"
      case"just opened"
            break
      case""
            -- create and save a new annotation
            put the seconds into tPropSet
            break
      default
            -- update existing annotation
            put gIDs["setnamefromid"][the uAnnotationID of cd \
         "Details"] into tPropSet
            -- remove the annotation from the map
            mergMKDeleteAnnotation the uAnnotationID of cd "Details"
            -- delete the old relationship (the other one is
   -- overwritten later)
            delete variable gIDs["setnamefromid"][the \
         uAnnotationID of cd "Details"]
   end switch
   put the customProperties["annotation"] of cd "Details" into \
      tAnnotationA
   set the customProperties[tPropSet] of stack "UserData" to \
      tAnnotationA
   save stack "UserData"
   mergMKSet "visible",true
   -- add to the map after it's visible so we see the animation
   ifthe uAnnotationID of cd "Details" <> "just opened" then
         -- add the annotation and update the globals
         put \
         mergMKAddAnnotation(tAnnotationA["coordinate"],\
tAnnotationA["title"],tAnnotationA["subtitle"],true,true,"red",true)\
 into gIDs["idfromsetname"][tPropSet]
         put tPropSet into \
         gIDs["setnamefromid"][gIDs["idfromsetname"][tPropSet]]
   end if
end showMap

on initMap
      mergMKCreate
      put true into sMapCreated
      mergMKSet "rect",(0,0,the width of this stack,the top of \
         group "TabBar")
      mergMKSet "user tracking mode","follow with heading"
      mergMKSet "visible",true
      put specialFolderPath("Documents")&"/userdata.dat" into tPath
      ifthere is not a stack tPath then
            create invisible stack "UserData"
            set the filename of stack "UserData" to tPath
            save stack "UserData"
      end if
      repeatfor each line tPropSet in the customPropertySets of \
         stack "UserData"
         put the customProperties[tPropSet] of stack "UserData" \
         into tAnnotationA
         -- make it quick to map either way by making two arrays
   -- of the relationship
         put \
         mergMKAddAnnotation(tAnnotationA["coordinate"], \
tAnnotationA["title"],tAnnotationA["subtitle"],true,true,"red",true) \
into gIDs["idfromsetname"][tPropSet]
         put tPropSet into \
         gIDs["setnamefromid"][gIDs["idfromsetname"][tPropSet]]
   end repeat
   -- flag to ensure annotations not created when opening stack
   set the uAnnotationID of cd "Details" to "just opened" 
end initMap

on closeCard
      ifthe environment is "mobile" then
            mergMKSet "visible",false
      end if
end closeCard

command mergMKAnnotationCalloutTapped pID
      lock screen for visual effect
      set the uAnnotationID of cd "Details" to pID
      go card "Details"
      wait for 0 millisecs with messages
      unlock screen with visual effect "push left very fast"
end mergMKAnnotationCalloutTapped

command mergMKAnnotationDropped pID,pCoordinate
      put "Dropped: "&pID &" ("&pCoordinate&")" &cr before fld "log"
      put the uLocs of this stack into tLocs
      if tLocs <> "" thenput cr after tLocs
      put pCoordinate after tLocs
      set the uLocs of this stack to tLocs
end mergMKAnnotationDropped
 
All that remains is saving the annotation details in the closeCard script of the "Details" card into a customPropertySet and setting up the fields on preOpenCard. Here's our new script for the "Details" card.

global gIDs

on preOpenCard
      # mobGUIStart ---> Inserted by mobGUI plugin
      if"MobGUILib-1334708811870.287842" is not among the lines of \
         the stacksInUse thenstart using stack\
 "MobGUILib-1334708811870.287842"
      mobGUIPreOpenCard me
      # mobGUIEnd
      ifthe uAnnotationID of me <> "" then
            put gIDs["setnamefromid"][the uAnnotationID of me] into \
            tPropSet
            put the customProperties[tPropSet] of stack "UserData" \
            into tAnnotationA
            set the uText of group "title" to tAnnotationA["title"]
            set the uText of group "subtitle" to \
            tAnnotationA["subtitle"]
            set the uText of group "notes" to tAnnotationA["notes"]
            -- save extra stuff like "coordinate"
      else
            set the uText of group "title" to ""
            set the uText of group "subtitle" to ""
            set the uText of group "notes" to ""
            put mergMKGet("center coordinate") into \
            tAnnotationA["coordinate"]
      end if
      set the customProperties["annotation"] of this cd to \
         tAnnotationA
      pass preOpenCard
end preOpenCard

on closeCard
      set the annotation["title"] of this cd to the uText of group \
         "title"
      set the annotation["subtitle"] of this cd to the uText of \
         group "subtitle"
      set the annotation["notes"] of this cd to the uText of group \
         "notes"
end closeCard

Here you can see our map with a couple of pins.

Showing/editing the annotation details
Our next step is to handle the mergMKAnnotationCalloutTapped callback message sent when the user touches up on the annotation disclosure triangle. When the user does that we want to push over to our Details card. Adding this script to the Map card will handle that.

command mergMKAnnotationCalloutTapped pID
      lock screen for visual effect
      set the uAnnotationID of cd "Details" to pID
      go card "Details"
      wait for 0 millisecs with messages
      unlock screen with visual effect "push left very fast"
end mergMKAnnotationCalloutTapped

Here you can see the Details card after clicking on the disclosure triangle shown above.

Allowing the user to relocate the annotation pin

When we created our annotations we set the draggable parameter to true so all we need to do is handle the mergMKAnnotationDropped callback message. Here's the handler for the Map card we need to do that:

command mergMKAnnotationDropped pID,pCoordinate
      put gIDs["setnamefromid"][pID] into tPropSet
      put the customProperties[tPropSet] of stack "UserData" into \
         tAnnotationA
      put pCoordinate into tAnnotationA["coordinate"]
      set the customProperties[tPropSet] of stack "UserData" to \
         tAnnotationA
      save stack "UserData"
end mergMKAnnotationDropped

What next?
In part 2 of this series we will start working with mergPop, mergAV and mergDoc to add photos and videos to the Detail card.

You can buy mergExt in the LiveCode store, here, and MobGUI is available here. For 2 weeks only, you can buy both together for half off the cost, using coupon MERGUI.

About the Author

Monte Goulding lives on a small farm in Tasmania, Australia where he built a straw bale house. When not working at M E R Goulding - Software development he enjoys spending time with the family, pretending to be a farmer, mountain bike riding, diving for and eating abalone.

 

Main Menu

What's New

Simulcast for $199 this week only