30 December 2014
Tutorial: Implementing “SendTo” with CoronaCards and Swift
In a previous tutorial, we showed you how to use CoronaCards with Swift. In this tutorial, we’ll implement a native feature in a CoronaCards-based app: the SendTo button, recognizable as the following:
In many iOS apps, when you press the SendTo button, it brings up a “sharing panel” that lets you share your information via Twitter, Facebook, and other services, as well as providing access to features like printing and AirDrop. In native terms, this panel is known as an UIActivityViewController. Conveniently, this can be implemented using CoronaCards.
Before you proceed, ensure that you have already installed the CoronaCards templates from the previous tutorial and that you can build the project presented therein.
Setting Up
First, create a new Xcode project using the CoronaCards “Single View” template. Then, give your project a name and copy your CoronaCards license.ccdata
file to the core project folder where main.lua
is located.
The Corona Side
The CoronaCards template provides you with the “HelloPhysics” sample app as a starting point, and this tutorial will build upon it. Our “SendTo” Corona app will provide a button to invoke the sharing, but first we need something to share. Toward this end, the app will capture the screen using display.captureScreen() and save it to a temporary file using display.save(). Then, the code will send an event to the native side where the UIActivityViewController
will be shown, and this event will contain a text message, the captured image, and a URL. When the controller finishes, it will trigger an event back in the Corona side that will tell your app if it was completed and what service was chosen. Finally, a display.newText() object will be used to show the results.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
--------------------------------------------------------------------------------------- -- A simple physics example in 8 lines! --------------------------------------------------------------------------------------- local physics = require( "physics" ) -- -- Hello Physics Sample app -- physics.start() local sky = display.newImage( "sky.png", 160, 195 ) local ground = display.newImage( "ground.png", 160, 445 ) local crate = display.newImage( "crate.png", 180, -50 ) crate.rotation = 5 physics.addBody( ground, "static", { friction=0.5, bounce=0.3 } ) physics.addBody( crate, { density=3.0, friction=0.5, bounce=0.3 } ) -- -- Set up the share "button" and result text object -- local shareText -- Forward declare the button local resultText = display.newText( "", display.contentCenterX, 20, "HelveticaNeue", 20 ) -- -- Set up the communications to Swift -- local function handleTap( event ) -- -- Capture the screen -- Also, hide the button initially -- shareText.isVisible = false local screen = display.captureScreen( false ) -- -- Save the capture to the temporary directory -- display.save( screen, { filename="screencapture.jpg", baseDir=system.TemporaryDirectory, isFullResolution=true, backgroundColor={0,0,0,0} } ) screen:removeSelf() screen = nil -- -- Show the button -- shareText.isVisible = true local event = { name = "coronaView", message = "I just watched Hello Physics!", image = { filename="screencapture.jpg", baseDir=system.TemporaryDirectory }, url = "" } -- Dispatch the data to the global Runtime object Runtime:dispatchEvent( event ) return true end shareText = display.newText( "Share", display.contentCenterX, 200, "HelveticaNeue", 24 ) shareText:addEventListener( "tap", handleTap ) shareText.x = display.contentCenterX shareText.y = display.contentHeight - 15 -- -- Set up a listener to handle return event from Swift -- local function sendToComplete( event ) print( "sendToComplete" ) print( event.name ) -- String value of "sendTo" print( event.complete ) -- Boolean of true or false print( event.activity ) -- Activity name if ( event.complete ) then resultText.text = "Shared by " .. event.activity else resultText.text = "Cancelled" end return true end Runtime:addEventListener( "sendTo", sendToComplete ) |
Let’s inspect the key aspects of this code. Pressing the share “button” (shareText
) triggers the object’s handler function, handleTap()
, inside which the screen is captured. It then saves the captured screen to the system.TemporaryDirectory
folder. Next, an event table is constructed. The native side listens for an event called coronaView
, and it expects some text, a URL, and an image, so we add those items to the event table. Finally, we dispatch the event to the global Runtime.
When the UIActivityViewController
finishes (see below), it sends an event back to the CoronaCards side. This requires a Runtime event listener that we’ll call sendTo
, and it will communicate whether the information was shared or if it was cancelled. If shared, it will return the method which was used.
The Swift Side
On the Swift side, we need to make several changes to the ViewController.swift
file. For reference, the entire file looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
// // ViewController.swift // ActivityController // // Created by Rob Miracle on 12/28/14. // Copyright (c) 2014 Corona Labs. All rights reserved. // import UIKit class ViewController : CoronaViewController, CoronaViewDelegate { var coronaView: CoronaView! = nil override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. coronaView = self.view as CoronaView // Force downcast coronaView.coronaViewDelegate = self coronaView.run() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func coronaView(view: CoronaView, receiveEvent event: NSDictionary) -> AnyObject? { /* event dictionary contains: message = text to share image = NSDictionary with file name and path url = URL to post */ let message: String = event["message"] as String let imageData = event["image"] as NSData let image:UIImage = UIImage(data: imageData as NSData)! let myURL:String = event["url"] as String let url = NSURL(string: myURL) shareTextImageAndURL(sharingText: message, sharingImage: image, sharingURL: url) return(true) } func shareTextImageAndURL(#sharingText: String?, sharingImage: UIImage?, sharingURL: NSURL?) { var sharingItems = [AnyObject]() var view = self.view as CoronaView if let text = sharingText { sharingItems.append(text) } if let image = sharingImage { sharingItems.append(image) } if let url = sharingURL { sharingItems.append(url) } let activityViewController = UIActivityViewController(activityItems: sharingItems, applicationActivities: nil) activityViewController.completionHandler = {(activityType, completed:Bool) in if !completed { println("cancelled") let event = ["name":"sendTo", "complete":false] view.sendEvent(event) return } else { println("complete \(activityType)") let event = ["name":"sendTo", "complete":true, "activity":activityType] view.sendEvent(event) } } self.presentViewController(activityViewController, animated: true, completion: nil) } } |
The first change is setting up a delegate to handle the events — this is how other controllers can “talk” to your CoronaView on the CoronaCards side. We also add the CoronaViewDelegate
to the end of the class declaration.
The event handler which receives the event from CoronaCards needs to be called coronaView
. So, we must rename view
to coronaView
and declare it as part of the ViewController
class. Also, we’ll hook up the delegate prior to running the CoronaView.
Next, we need to set up the event handling function which will receive the data from Corona. Inside the function, we extract the various fields from the event table. Interestingly, the image entry does not include the file name and path, but rather the binary image data, and this arrives as a NSData
type which we can convert to a UIImage
type. In addition, we convert the URL from a string to an NSURL
.
With the data ready to go, we call the function shareTextImageAndURL
, passing the objects to the function. The shareTextImageAndURL
function simply looks for each of the three objects and adds them to the sharingItems
array.
Finally, we create the UIActivityViewController
, initializing it with our items to share, and instruct it to use all available options. For the completion handler for UIActivityViewController
, if the controller was cancelled, we set up an event table named sendTo
(as defined on the CoronaCards side) and set the complete
entry to false
. In the else
clause, we set up an event table with complete
set to true
and set activity
to the activityType
. In both cases, we send the event to the CoronaCards view. Finally, we instruct the controller to show via the presentViewController()
command.
Conclusion
As demonstrated in this tutorial, CoronaCards gives you access to a wide range of native features which allow you to expand the power of your Corona-based project. As always, please contribute with questions and comments below.
Erich Grüttner D.
Posted at 17:45h, 30 DecemberThank you, just in time!!!
Happy new year CoronaLabs Team!!!
JCH_APPLE
Posted at 08:22h, 02 JanuaryIt could be nice to have the same example with Corona Enterprise in order to see what differs in the 2 ways : native using Corona and Corona using native.
Rob, thank you for all these greats tutor, and happy new year to Corona people.
Rob Miracle
Posted at 14:58h, 02 JanuaryThe ObjC way of doing this isn’t drastically different. My Enterprise Fu isn’t very strong, but if it were me, I’d go back to the Enterprise tutorials that I did and turn it into a plugin. Maybe name it plugin.sendto. That tutorial had data passing between native and Corona as well.
The one thing, is it would need to be the ObjC version of using the UIActivityViewController instead of Swift. With CoronaCards, the Lua library (and lua_State structures) are behind an Objective C class which Swift can easily bridge too. However in the Enterprise example, the current plugin code is C++ talking to a C library and ObjC calls the C++. The bridging between Swift and C/C++ takes more work to make happen and I’m not there yet.
JCH_APPLE
Posted at 04:01h, 03 JanuaryThank you. I will try and if succeed I will post result. Printing would be very useful in business app.
corona273
Posted at 22:30h, 06 JanuaryNote there’s also an outstanding feature request to add the iOS sharing sheet to Corona SDK (it’s a trivial addition, and the Android sharing panel is already supported):
http://forums.coronalabs.com/topic/53371-supporting-ios-modal-share-action-sheet-airdrop-print-add-to-home-screen-add-bookmark-add-to-reading-list-etc/
Rob, any thoughts on when this might happen?
Mustafa
Posted at 00:47h, 07 JanuaryThank you Rob,
Will this possible with Pro?
Rob Miracle
Posted at 16:04h, 07 JanuaryNo. This is not available for Pro. We need to have people vote for it. Please go to http://feedback.coronalabs.com and look for an existing feature request or create a new one.
Rob