Swift – AVAudioFoundation

Playing sounds with Swift and AVAudioFoundation

A few notes about using AVAudioFoundation with Swift.

Be sure to add your audio files to your project. Make sure they have been “added to target”. Without this the audio files will not be bundled with your app.

Import AVAudioFoundation.

Use AVAudioPlayer() to create a new audio player instance.

Call audioPlayer.play() to play a sound and audioPlayer.stop() to stop a sound.

Sounds play from beginning to end. Calling stop() pauses the sound at the current time. Calling play() again starts the sound from the current time. To start from the beginning of the set the currentTime property to 0!

Here is a sample code block.

import AVAudioFoundation

// Get a path to your audio resource
let path = NSBundle.mainBundle().pathForResource(self.audioFileName(), ofType: nil)!

// Make a URL from the path
let url = NSURL(fileURLWithPath: path)
// Try to make an audio player with the URL
do {
    let sound = try AVAudioPlayer(contentsOfURL: url)
    sound.play() // If all is good play your sound
} catch {
    // Handle an error here
    print("unable to load sound...")
}

The Language of Segues

In iOS Segues are used to get from one content screen to another. A tranistion occurs between screens when a sgue is invoked. There are a few simple transitions that are default, and you can also make your own custom transitions.

The default transitions, simple as they are, provide a good foundation for presenting content. Used correctly they act as a language that communicates the relationships between content screens.

Show/Push Segue

When screen slides in from right to left you are moving deeper into the content. Like turning pages in a book. This is a Show Segue in iOS. When the Screen slides left to right you are moving up in content, like turning back to the previous page.

Notice that in both of these cases the current content is pushed off the left or right. This is a subtle part of the communication. Telling you that the you are moving on to new content and the old content has moved aside. It also gives the impression of content screens as a row or a timeline, and adds to the feel of content having a structure or history.

This is what you get when working with the Navigation controller. Which makes sense. Notice in most cases there will be a back button in the upper left. A UI element people universally understand.

Modal Segue

When a screen slides up from the bottom this is a Modal segue. In this case you are staying in the current content screen and showing a screen on top. A modal is a dialog box. You’ll answer any questions, set any options before it goes away and the current content revealed again. The Alert dialog box is a modal. Though in this case it is small enough to allow you to see the current content behind it.

Use a modal when you want to ask a question or get information. Like, asking for a user name and password, presenting some settings, or showing an option to buy some gems in the app store.

Notice when a screen slides up from the bottom the current screen is covered, it is not pushed out of the way. This is telling you that the current content screen is still there behind this new modal content waiting for you.

You’ll notice this is what you get when you open the camera or photo picker in an app. It slides up from the bottom, covering the current screen. You take a photo with the camera or pick a photo from your library, and the photo picker slides down revealing the previous screen. This is exactly what you expect. You don’t want the photo picker to push the current off the left, you would worry that you’d be losing that email, or text message as you moved on to a new content screen.

Non transition

Think about the tab bar controller. Notice there is no transition when switching tabs. In this case I’m guessing Apple felt they wanted to convey that each tab was it’s own separate area, almost like each tab section was an app within the app. With no motion you get the impression you have teleported to a new location. With motion you might feel you are moving to content that is connected to the current content. Instead you are leaving the current content behind, and jumping to a new area with new content. You should be able to visit the previous tab and return to that part of the app in the state you left it.

Imagine if there was a slide/push to the left or right. You might feel like you were moving deeper into the current content rather than moving on to a new content area, which would be confusing when that content belonged to an unrelated area of the app.

It’s all about Communication

You’re not using transitions as a creative tool, instead use them as a form communication. While transitions can be very creative, without thinking about communication first you can create a confusing user experience.

Swift – UIView

Here are some notes on working with UIView. I try to cover the differences between frame and bounds, and how to position elements on the screen using frame, bounds, and center. There is a video example and corresponding files on Github: https://github.com/soggybag/View-Experiements

Views

Views are the visible elements that appear in your apps. Views can look like anything. They can have color, images and text. Views can detect user touch interactions. A view is always a rectangle. The contents can have an alpha mask to allow the view to appear as any shape.

UIView

UIView is the base class for all visible elements that might appear on the screen. Every UI element inherits from UIView. You can think all the UI elements as UIViews with special added features. For example UILabel is a specialized version of UIView that is meant to act as a simple text display. All of the UI elements are built on top of a UIView.

Make a UIView for yourself.

let box = UIView()

In order to see the the view it needs a size, position, and to be added as a subview of a view that is in the display stack. You may also need to set the color. A white view against a white background will be hard to find!

Frames, Bounds, and Rectangles

Views are rectangular. A rectangle where a view can draw itself, and a user can interact. A rectangle or rect defines the both the size and position of a rectangular area of the screen. A rect is defined by four properties: x, y, width, and height.

CGRect(x: 0, y: 0, width: 100, height: 100)

The CGRect class is used to define a rect. UIView defines the frame property as a rect. Setting the frame of a view sets the position and size of the view.

The frame property of a view is a CGRect that defines the size and position of the view in it’s super view‘s coordinate system. Think about this as the size and position in the world in which it’s exists.

The bounds property of a view is also a CGRect. The bounds defines the size and position of a view it’s own, or local, coordinate space.

box.frame = CGRect(x: 0, y:0, width: 100, height: 100)

This should produce a box 100 by 100 points in the upper left corner. NOTE: you may not be able to see the white box against the white background! Set the background color:

box.backgroundColor = UIColor.redColor()

Center, X, and Y

 

A view can be positioned through it’s center property. The center property is a CGPoint. CGPoint contains an x and a y property.

While the x and y of the frame position a view around it’s upper left corner, the center.x and center.y position a view around it’s center!  You can check this for yourself:

let box = UIView()
box.frame = CGRect(x: 0, y:0, width: 100, height: 100)
print(box.center) // (50.0, 50.0)

You can see the center shows x and y of 50 while we set the frame x and y to 0 and 0.

Subviews

All views act as a containers for other views. You can add any view as a subview to another view. You can think of this like creating layers or groups. Or like nesting html tags.

Add a view to another view with the addSubview() method:

let box = UIView()
box.frame = CGRect(x: 0, y:0, width: 100, height: 100)
box.backgroundColor = UIColor.redColor()
view.addSubview(box)

The block of code above, added to your viewDidLoad(), would produce a red box in the upper left corner of 100 by 100 points.

Nesting Views

Views stack, front to back, in the order they are added with addSubview() since addSubview() adds the new view in front of existing views. You can use these other methods to arrange views.

 

  • addSubView()
  • insertSubview(:atIndex)
  • insertSubView(:belowSubview)
  • insertSubview(:aboveSubview)
  • exchangeSubviewAtIndex(:withSubviewAtIndex)
  • bringSubviewToFront()
  • sendSubviewToBack()

Width and Height

Setting the width and the height can be confusing. These properties show up in several places. For example:

box.frame.width // get only! 
box.frame.size.width // get and set

While both of this properties return the same value, the first (frame.width) is get only! You can’t set it. The second (frame.size.width) is get and set. The same is true for height.

box.frame.height // get only! 
box.frame.size.height //get and set

This also applies to bounds.

box.bounds.width // get only! 
box.bounds.height // get only!
box.bounds.size.width // get and set
box.bounds.size.height // get and set

CGSize

CGSize is a type with two properties: width and height.

box.frame.size = CGSize(width: 100, height: 100)

 

Frame vs Bounds and Coordinate space

Each view supports its own coordinate space. Imagine that each view defines x:0 as the position of it’s left edge, and y:0 as it’s top edge. Frame defines the position and size of a view within it’s superview, while bounds defines location and height in a views own coordinate space.

Center

A view also has a center. This is a CGPoint that locates the view on the x and y axis around it’s origin. The origin should be in the center of a view by default.

Origin

The origin is a property of bounds, and sets the x, and y position around which the rectangle is drawn.

 

Parse Swift with Cocoapods

  1. Xcode new project
  2. Terminal CD to project directory
  3. Open your Podfile with: open -a Xcode Podfile
  4. Uncomment Frameworks, and add Parse, and ParseUI to do. Should look something like this:
  5. # Uncomment this line to define a global platform for your project
    # platform :ios, '8.0'
    # Uncomment this line if you're using Swift
    use_frameworks!
    target 'SpringShowApp' do
    pod 'Parse'
    pod 'ParseUI'
    end
  6. Quit Xcode
  7. In terminal: pod install
  8. When Terminal is finished, open your project from the ProjectName.xcworkspace. From this point on, you will need to open the project from this file!

Swift – Open an Alert box

Opening an alert in iOS is very easy. Use the UIAlertController. This creates a special ViewController that can be displayed in one of two styles Alert, or ActionSheet.

 

let alert = UIAlertController(title: "Send Cypher", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
let message = UIAlertAction(title: "Message", style: UIAlertActionStyle.Default) { (action: UIAlertAction) -> Void in
    // Do something when message is tapped
}

let email = UIAlertAction(title: "Email", style: UIAlertActionStyle.Default) { (action: UIAlertAction) -> Void in
    // Do something when email is tapped
}

let cancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alert.addAction(message)
alert.addAction(email)
alert.addAction(cancel)

presentViewController(alert, animated: true, completion: nil)

Swift – Send Email

Sending email with Swift is easy with MFMailComposeViewController. Follow these steps: 

Step 1: Import the MessageUI framework

import MessageUI

Step 2: Add the Protocol to your ViewController

MFMailComposeViewControllerDelegate

Step 3: Add the delegate method

func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) {
    controller.dismissViewControllerAnimated(true, completion: nil)
}

 

Step 4: Make an instance of the MailComposeViewController, and present it.

func sendEmail() {
    let mailVC = MFMailComposeViewController()
    mailVC.mailComposeDelegate = self
    mailVC.setToRecipients([])
    mailVC.setSubject("Message Subject")
    mailVC.setMessageBody("Message text...", isHTML: true)
    
    presentViewController(mailVC, animated: true, completion: nil)
}

 

https://gist.github.com/ed9b0ad90ca0937aac62.git

 

 

Swift – Send SMS Messages

Sending SMS messages in Swift is very easy. You can add the capability to any app with a few lines of code.

Note: This will not work in the simulator, as the simulator doesn’t support SMS messages!

Step 1: Import MessageIU

Add the following to the top of your ViewController.

import MessageUI

Step 2: Add the Protocol

Add the MFMessageComposeViewControllerDelegate your ViewController definition. 

Step 3: Conform to the protocol

Conform to the MFMessageComposeControllerDelegate by adding defining the:

messageComposeViewController:didFinishWithResult method. It might look something like:

func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
    switch result.rawValue {
    case MessageComposeResultCancelled.rawValue :
        print("message canceled")
        
    case MessageComposeResultFailed.rawValue :
        print("message failed")
        
    case MessageComposeResultSent.rawValue :
        print("message sent")
        
    default:
        break
    }
    controller.dismissViewControllerAnimated(true, completion: nil)
}
}

You can omit the switch statement. The switch statement provides some feedback to you on the status of the message. Use this to notify your user when a message fails, or is canceled. Or omit if this is unimportant.

Step 3: Open the Message View controller

func sendMessage() {
    let messageVC = MFMessageComposeViewController()
    messageVC.body = "Message string"
    messageVC.recipients = [] // Optionally add some tel numbers
    messageVC.messageComposeDelegate = self
    // Open the SMS View controller
    presentViewController(messageVC, animated: true, completion: nil)
}

 

https://gist.github.com/1c863ab275e1c0e09936.git

 

Native vs HTML5 and Phonegap

This a question that comes up a lot for me. I think about it everyday. I’ve written about it here in the past. The question came up in class yesterday.

Here is a good discussion of the subject:

http://roadfiresoftware.com/2014/04/when-to-use-phonegap-versus-developing-a-native-ios-app/

Some quotes from this article:

Honestly, one big reason developers want to build an app with PhoneGap is so they don’t have to learn Swift or Objective-C. A lot of times, they’re afraid of learning a new language.

Here’s another

great for getting an app out the door quickly…but definitely lacking compared to native.”

(We’ll look at what it’s lacking in a bit…)

But according to Kevin Munc, “dev speed advantages are a myth.”

After learning the new environment, development time is not much improved using using HTML5 vs Native. Essentially you are doing all of the same things, only in another language. It’s learning the other environment that takes time.

Here is another article.

http://www.lifehacker.com.au/2013/03/ask-lh-should-i-use-phonegap-to-build-mobile-apps/

And here are some quotes:

The short answer: yes, using PhoneGap is fine, and will make it easy to build an app without needing to learn additional languages.

While you can deploy apps on multiple platforms, be aware that they won’t perform like native apps in every environment.

One more:

http://www.fastcompany.com/3030873/our-html5-web-app-flopped-so-we-went-native-and-havent-looked-back

http://www.fastcompany.com/3030873/our-html5-web-app-flopped-so-we-went-native-and-havent-looked-back

http://www.fastcompany.com/3030873/our-html5-web-app-flopped-so-we-went-native-and-havent-looked-back

Addendum: Develop twice

One of, if not the biggest, disadvantages of developing native is the fact that you will have to develop two apps, one for iOS, and another for Android. This sounds like a big hurdle if you’re impatient and time and money are the most important factors. But I think are is a hidden advantage to this approach.

Developing first on one platform, allows you to define, refine, and focus your UI and design on one platform before building for the other. Also, there are inherent differences between iOS and Android, building for both, creates an experience that doesn’t truly feel native on either. When quality is important I’m certain native is the choice.

Cocoa – UITableView

These are a  few notes on creating the UITableView. Understand at the time of writing this, I’m a total beginner, so take everything here with a grain salt, and an once of skepticism. The purpose of writing this is to settle subject in my own brain.

As part of the MVC pattern UITableView is a View, so a table view knows how to draw itself. A table view typically needs a view Controller to manage it. A table view always needs a data source. The data source supplies, the number of section, rows, and the content for each row. Typically a table view needs a Delegate. The Delegate handles events generated by the table view.

An instance of the UITableViewController class can handle the role of Controller, Data Source, and Delegate. The UITableViewController, as a view controller, manages a view and this view is always a UITableView.

Any class that manages a UITableView must be a UITableViewDelegate. The data source for the UITableView must be a UITableViewDataSource. These are often the same class, I’m guessing  they don’t have to be.

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

@end

This is the UITableView Protocol

To display the UITableView these requirements must be met.

UITableViewDataSource

This protocol requires two methods be implemented:

-(NSInteger)tableView:numberOfRowsInSection
-(UITableViewCell *)tableView:cellForRowAtIndexPath

These two methods tell the UITableView how many cells to work with, and give you the opportunity to populate and configure the UITableViewCells.

Giving the table view the number of rows in a section allows it to get data via the index of that row. Without having to contain the actual data. Essentially tableView:numberOfRowsInSection tells the table view how many cells you will need to work with. When comes time to display a cell, the table view calls on tableView:cellForRowAtIndexPath passing the index of the cell, this function returns the cell; and within this function you populate and configure the cell.

-(NSInteger)tableView:numberOfRowsInSection

This method is relatively simple. It returns the number of cells in the table view. If you were using an array you might implement it in this way:

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [array count];
}

– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

The cell For Row method is more complex. Here is where table cells are created, populated, and configured. The table creates cells only as needed. It only creates enough actual cells to fill the screen. As cells scroll up the page they are recycled and repopulated with new data, with the tableview keeping track of which index is currently visible. This process makes the system much more memory efficient, since the computer holds only a limited number of cells in memory. Imagine your iTunes library with 500, or 1000, or more songs. The table view is really only working with enough cells to fill the screen, maybe 10. If the computer to generate 1000 or more table cells, it would slow down, and become unresponsive. Using this system, the table view is recycling a limited number cells like an escalator, changing the content as the cell moves off the top of the view and reappears at the bottom of the view.

You might implement this method as:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *simpleTableIndenitfier = @"SimpleTableCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIndenitfier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIndenitfier];
    }
    
    cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
    return cell;
}

Here the dequeueReusableCellWithIdenifier asks for an available cell from the table, which is stored in cell. If cell is nil, no cell was available, so a new cell is generated. In this case the cell is generated using the UITableViewCell alloc. Then initialized with initWithStyle. Here we tell initWithStyle to use one of the default cell styles UITableViewCellStyleDefault.

– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

This method tells the table view how many sections you will using. Be sure to set this to at least 1. The default implementation of this methods is set 0.

Storyboard and outlets

To connect a tableview created in storyboard to it’s view controller’s outlets, Control drag from the tableView to the view controller and choose dataSource. Repeat the process for delegate. You want the Outlets dataSource and delegate to read ViewController.

Corona – config.lua and making universal apps

Here’s a link to a couple articles on the Corona web site about making apps to fit a wide range of screen sizes. Be sure to comments there’s a lot of good questions and answers there. The second articles takes the whole concept to a very elegant solution.

  1. http://www.coronalabs.com/blog/2012/12/04/the-ultimate-config-lua-file/
  2. http://coronalabs.com/blog/2013/09/10/modernizing-the-config-lua/