Swift – layout subviews, no storyboards

So at work I came across a problem with using a subclass view in order to display a customized button.

If I understand correctly, using the storyboards means that the customized view first initiates the view through init(coder) which is not how I wanted the view initiated.

So I added a view through code instead and everything worked much better.  Without going into the debate of to Storyboard or not to Storyboard, in this case it seemed much simpler.

When I create the frame for my view, though, I want it to be proportional to whatever the user’s device’s screensize might be.  So when to I create it, and set the frame size to be proportional to a view (map view in my case)?  We have to wait till the views have all been instantiated by the device.  And that’s where LayoutSubviews comes in.  Depending on where one sets up their subview, the method is different.  If the developer is adding the subview in a UIView then the method is func layoutSubviews.  If the subview is added in a ViewController then the method is func viewDidLayoutSubviews.

These methods wait for the views and subviews to be calculated and layed out by the device before doing whatever action is required.  In my case, since I wanted my subview to be placed in a proportionate space to the the map view (in it’s lower left side as it’s origin point) I needed the OS to calculate the super view size for that device and plug that into the frame size of my subview.  viewDidLayoutSubviews did the trick.

let originPoint: CGPoint = CGPoint(x: mapView.width70,y: mapView.height85)

let customView: UIView = UIView(frame: CGRect(origin: originPoint, size: CGSize(width: 50, height: 50)))

Warning – as always, make sure to call the super – super.viewDidLayoutSubviews.

Where to save and access files in iOS Swift 3.0

Today I wasted a bit of time trying to write to a file.  I didn’t realize the file directory that I was writing to was different than the directory I was reading from.  Don’t do that!

So when you’re accessessing, reading or writing to files here are some of the different places to do so:

Caches Directory

SearchPathDirectory.cachesDirectory is where you want to store temporary data that the app doesn’t need and could be deleted by the OS if it needs the space.  The tmp directory, on the other hand will only keep files while the app is in the foreground.

App Directory

SearchPathDirectory.applicationDirectory – App Directory is where to keep files the app uses to run but should be hidden from the user.

Document Directory

SearchPathDirectory.documentDirectory – This is the one I’ve used where information will persist between app loads, will be backed up by default, and that are exposed to the user – the user may write, read, delete, etc from.

https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW28

New to Swift 3.0

For work I have to update my project from Swift 2.3 to Swift 3.0.  Here are some changes I noticed:

@escape

Any (vs. AnyObject)

“_” as the first parameter

Fileprivate

GreatestFiniteMagnitude

 

 

 1. @escape

Before Swift 3.0, all closures where @escape by default.  This means that it would be possible to pass information or effect properties outside of the closure.  For example, we could update a class property within the closure.  Prior to 3.0 we would have to put the @noescape keywords before the closure parameter.  With Swift 3.0 @noescape is the default and if we want to change a property outside of the closure we have to specify using the keyword “@escape” before the closure parameter in a function.

http://krakendev.io/blog/hipster-swift

2. Any

AnyObject is used as a sort of generic way to refer to any possible class type object.  “Any” is used to be even more generalized.

3.  “_ ” as first parameter

 

This is just a way for Apple to tell us that they first will not be written out,:

 

func myNiftyMethod(_ input: String) -> Void {

//Do nifty stuff

}

var niftyText: String = “niffftttty”

myNiftyMethod(niftyText)

otherwise it would look like this:

func myNiftyMethod(input: String) -> Void {

//Do nifty stuff

}

var niftyText: String = “niffftttty”

myNiftyMethod(input: niftyText)

 

Swift Global Variables

Generally, global variables are bad.  Some problems include tight coupling, that is your classes are now dependent on each other in ways that they shouldn’t be, namespace confusion – you might want to use the same name in a variable in 3,234th function, but you forgot that you have a global variable using that same name, and testing issues. http://c2.com/cgi/wiki?GlobalVariablesAreBad

However, I needed to temporarily use one in order to test push notifications, since the debugger is wonky with Amazon’s current push framework.  So it’s pretty easy.  Just place the variable outside of the class:

import UIKit

var globalVariable: String = ""

class MyRandomClass: NSObject {

 …

The variable needs to be initialized.  People also recommend using structs for greater utility:

import UIKit

struct MyGlobalVariable {

    var wordVariable: String = ""

    var numberVariable: Int = 0

}

You can use the same struct device for global constants (just changing the var to a let).

 

http://stackoverflow.com/questions/26195262/how-to-create-a-global-variable

 

Apple Docs on Global Variables

Swift Core Data: ManagedObjectModel, PersistentStoreCoordinator and MainObjectContext

So I’m starting to dive into Core Data at work.  Core Data is more than just a database.  Among other things, it manages the database for you which is very handy, especially when a database update is needed.

Today, we made a Core Data manager that initializes Core Data for use in my app.  It initializes the Managed Object Model, the Persistent Store Coordinator and the Main Object Context.

-Managed Object Model

This is the structure of the database.  Generally, you’ll create this in your .xcdatamodel file.  It’s initialized by referring to that file:

        self.managedObjectModel =  NSManagedObjectModel(contentsOfURL: modelUrl)!

-Persistent Store Coordinator

This object coordinates the data with the managed object model to create the database:

       self.persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

-Main Object Context

This object is the way the app communicates with the database.  So a swift way of creating the data manager class might look like this (including a save function- pretty important for a database):

 

import Foundation

import CoreData

final class CoreDataManager {

    

    private let managedObjectModel: NSManagedObjectModel

    private let persistentStoreCoordinator: NSPersistentStoreCoordinator

    let mainObjectContext: NSManagedObjectContext

    

    init() {

        //    //Get the Object Model

        let modelUrl: NSURL = NSBundle.mainBundle().URLForResource("YourXCDatamodelFileName", withExtension: "momd")!

        

        self.managedObjectModel =  NSManagedObjectModel(contentsOfURL: modelUrl)!

        let paths = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)

        let storeURL: NSURL = paths[0].URLByAppendingPathComponent("NotificationModel.sqlite")

        

        let options = [NSMigratePersistentStoresAutomaticallyOption: true,

                        NSInferMappingModelAutomaticallyOption: true]

        

        self.persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

        do {

            try self.persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options)

        } catch {

            fatalError("Error migrating store: \(error)")

        }

        

        self.mainObjectContext =  NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)

        mainObjectContext.persistentStoreCoordinator = persistentStoreCoordinator

    }

    

    func save() {

        do {

            try mainObjectContext.save()

        } catch {

            fatalError("Error while saving: \(error)")

        }

    }

}

ps. I take no credit for this code – it’s mostly pilfered and/or translated from Objective C

Changing font size and dynamically changing cell row height for Accessibilty

The iPhone is great for people with vision problems. As it turns out, most people start to lose the ability to focus in our forties – called presbyopia.  So, yeah maybe most people using mobile phones are younger than 40, maybe, but for those of us getting on in years it’s nice to adjust the text size in our iPhones.  This can be done by going to the general settings of the iPhone, going to accessibility and tapping on the “Large Text” option which leads to a slider.  However, unless developers code apps appropriately, this will cause some problems in an app.

First, it’s important to use “textStyles” for fonts, the “Headline” or “Subheadline,” either in the interface builder or in code.  This will allow the font to be changed when users change text size in their iPhones accessibility settings.  But then this can cause problems in sizing various elements. For example, say we have a table view cell that has a certain height that’s just fine for the default text we use.  But when the text is enlarged by a certain amount it will look awful and in some cases spill off the cell (particularly for custom cells).

Here’s what I did (with help of course) to get around this problem:

In the Cocoa frame work for TableViewControllers there’s a method called “heightForRowAtIndexPath” that you can use to change the height of a specific cell, or in my case, set a constant height for all cells.  Let’s say that we know we want the cell to be as high as the text plus 10 for margin’s sake.  That means, no matter how large the text gets there will always be a margin.  We can set up a dummy label using the text size the user has set, find out what size that is and set the row height as that size plus 10.  Here’s sample code:

    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

        let label = UILabel()
        label.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
        label.text = "Placeholder Text"
        let size = label.sizeThatFits(CGSizeMake(view.width, CGFloat.max))
        return size.height + 10

}

It’s the “sizeThatFits” method on the label that does the magic.  We always use “CGFloat.max” as the height and the width is actually irrelevant.  This method returns the size of the label that fits for the current text.  An viola!  Now whenever text size is changed the cell heights will also change.

Swift Enums

So today I used enums for the first time and I’ll try to describe the process.  Let’s say you’re dealing with an app that deals with five different brands of cars – Fords, Volvos, Chevrolets, Auidis and Hondas.  You know that those car brands will be referenced over and over again.  You’ll also have to have an array of them somewhere and maybe store user preferences about them (maybe listing all the brands the user prefers).  Enums can make your life a lot easier.  So let’s make one:

enum CarBrand {
 case Ford, Volvo, Chevrolet, Auidi, Honda
 }

It’s that easy.  But there’s a lot more we can do.  So now we have a type – “CarBrand.”  Xcode will autocomplete for us when we begin typing CarBrand and give us the five options.  We’ve also limited the amount of errors because they will always be completed.  Also, if we add something or get rid of one of the brands we just do it in one place, wherever we typed the enum.

 

Still to come, functions within enums and CustomStringConvertible protocol (for printing or converting to a string).

 

Accessibilty, changing voice-over app name

Insuring accessibility is just the right thing to do for an app. It’s not too hard and let’s people who are blind or vision impaired use your app. For those who have vision impairment it’s really important for developers to by mindful to the contrasts of the visual elements in an app. Those ratios should never be less than 4.5:1

https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/ColorImagesText.html

For the app I’m working on I had another problem: the app name.  It sounds silly but since “NYC” is in the title, Apples voice-over framework will pronounce the app, when an vision impaired user clicks on it, as “Nick.”  I’ve got nothing against Nick but the app is not for him/her.  So there’s a really easy fix as of iOS 8:

In the Info.plist simply add the key:

CFBundleSpokenName

then for the value, in quotes, add whatever the you want the voice over to say, in text-to-speech usable writting.  For example, in my case, for “NYC” I’d write “my app N Y C.”  Easy-peasy.

The didSet attribute of a property Swift iOS

I’ve never used the Key Value Observer pattern (KVO) but I came across a situation that would otherwise call for it except for the Swift ability to call “didSet” for a property.

I’m making a view that partially pops up over a map when a map pin annotation is tapped with information about that annotation.  This pop up view has a number of labels and is initialized when the map first loads.  So, when the map first loads, there are no annotations selected and I can’t pass information to the pop up view (POV).  How do I get that information on to it?  I can simply set the labels when the annotation is tapped in my primary view controller (VC). That is, in the delegate function for pin annotation taps (calloutAccessoryControlTapped in my case). So the VC handles all that information.  But for my current project all that information, the datasource, or model, should not be handled by the VC.  Really the VC should only handle UI interactions and movement.  We’re actually using Model-View-View Model design, so we want all the model information and logic to be off the VC.  So one way to do this is to have the labels on the POV “listen” for new information and update when they here something has changed.

In Objective C, Key Value Observer pattern was a way to do just that.  In Swift it’s much simply. We simply use the “observers” didSet.

 

 Class SomeViewContoller: UIViewController {

 @IBOutlet weak var firstLabel: UILabel!
 @IBOutlet weak var secondLabel: UILabel!
 @IBOutlet weak var thirdLabel: UILabel!

 var viewModel: ViewModel {
    didSet {
    firstLabel.text = viewModel.someInfoProperty
    secondLabel.text = "Hey I'm using the view model to set this info: \(viewModel.getInformationForSentence)"
    thirdLabel.text = viewModel.convertPublicServiceAnnouncementsToHaiku
    }
 }

}

In this example, the viewModel property is setup so that when there’s a change, that is, when it’s set, the labels are updated accordingly.  There is a problem with this setup however – if the viewModel were to be set before the SomeViewController was intialized/loaded then we’d have a crash because the labels wouldn’t have been initialized yet.

didSet will not work for an init function – and shouldn’t.  If you want properties to be initialized to a certain value, just do that.

Detecting Any Touch in a View in iOS

My problem was that I had a sort of shelf view that would pop up to display information when a pin was tapped on a map view.  The shelf only covered part of the map view.  I wanted the view to go away when any part of the still visible map view was tapped.  Fairly common behavior.  On the interwebs, however I got a convoluted answer about using a wildcard gesture recognizer from Stack Overflow [See http://stackoverflow.com/questions/1049889/how-to-intercept-touches-events-on-a-mkmapview-or-uiwebview-objects ].

But, it turns out all UIViews inherit from UIResponder, which has a number of touch related functions.  The one that’s important to me is touchesBegan, which fires any time there’s a touch on the view.   So when I add the shelf to the super view (in my case the map view) I can also add a transparent overlay view.  I set it to hidden until the shelf pops up, then when someone clicks on that overlay a delegate function from the overlay tells the VC that the overlay touchesBegan has happened.  In that function I dismiss the shelf view and hide the overlay.

import MapKit

protocol ViewTouchDelegate: class {
 func ViewTapped()
}

OverlayView: UIView {

//normally would call this parameter "delegate" but if using specialized view "delegate" may already be used.
weak var viewTouchDelegate: ViewTouchDelegate?
...

 override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {


    self.mapViewTouchDelegate?.mapViewTapped()
    super.touchesBegan(touches, withEvent: event)
 }

 

And yet… since the map view is inherits from UIResponder… I can just subclass the map view, add the protocol/delegate methods and not even use an overlay, remembering that in the map view touchesBegan function to include super.touchesBegan so that all the functionality of gestures still work.  The advantage of this approach is that when the user touches anywhere on the map, not only is my dismiss shelf function called but also the other functionality still works – tapping on a pin still does whatever it’s going to do.  If I use an overlay that first touch simply fires that delegate method, then I have to use my normal gestures, or possibly pass the gestures through some communication pattern.  So in my case, it seems easier to just subclass map view and override it’s touchesBegan method.  An overlay view would be useful if there were a number of different views underneath the shelf view, instead of just a map.