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 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.

Top Alignment for Labels and Labels v. TextView

A UILabel is giving me problems because it needs to display a lot of text but is aligning vertically in the center of the field frame. I need it to align to the top. Knowing there would be a lot of text I made the label large – it’s constraints are equal to the view. So I thought I had a couple of different options – 1) make it a textView which is meant for multiple lines of editable text, or 2) hacks. “Hacks” means to add the max number of lines to 0 and set the label to “size to fit.” However, there were a couple of things I had wrong. First, I shouldn’t have made the label so large. The label size will automatically increase, so what I should’ve done is just set the label to the top of the view and as text continues the label automatically increases it’s size down. So that includes getting rid of it’s constraints for height so that it would initially be at the minimal height. That way it will always start at the top.

So then the question is, do I want a label or a textView? I would think a label because I don’t want the view to be editable. However, in my situation the super view of the label is a subview of a map. It’s a shelf on a map, so it doesn’t cover the entire page, meaning there will be text off the viewable frame of the phone. The text will have to be scrollable. Apple tells us:

Although these classes actually can support the display of arbitrary amounts of text, labels and text fields are intended to be used for relatively small amounts of text, typically a single line. Text views, on the other hand, are meant to display large amounts of text.

https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/UsingTextClasses/UsingTextClasses.html

Apple also tells us labels are for static text, and text views and text fields are for editable text.  What about for large amounts of static text?  It seems to be the case that text views are the best in this circumstance.  Just change the “editable” setting to false, either programmatically or in the interface builder.

Communication Patterns between View Controllers

In a project I’m working on with three other people we have a number of different view controllers that need to pass information to each other.  Being a bit of a rushed project we used many different ways to communicate between the controllers.  Most of them not optimal.

1. The first and maybe easiest way to transfer information or instructions to another controller is through a segue in the prepare for segue method. This only really works to pass the information forward to the destination controllersegue

2.Another and generally respectable way of passing information forward and back is through delegation.delegation

3.If you need to send information to multiple places, NSNotifications can be useful.notification

Then there are hacks and/or less desirable methods of communicating.

4. Datastore. So you have a datastore and it’s got all the info you need. Why not use it? A data manager is used to store information for use with APIs, for example. It’s not specific to passing information. You can use it, but it’s not it’s intended purpose. It’s a bit of a hack:datastore

5. KVO. I noticed Key Value Observer was mentioned on the web. This is meant as a way to have object properties notice other object properties. It’s not really used much anymore and was never really intended for use for View Controllers. Stick with NSNotifications.kvo

6. NSUserDefaults. Really? Well we did it in our app. But I’m not proud of it, and it was a question of time. A quick and dirty way to store information that’s accessible to everyone else. What a mess. nsuserdefaults

So for both NSUserDefaults and the datastore, why not use information that’s stored somewhere else? I think of it like this – in programming you can use all sorts of means to accomplish your goal. But in object oriented programming, for example, the whole point is to use classes, inheritance, and object structure. So why would you use a different sort of strategy in you coding? Likewise, if you’re trying to communicate information between controllers or objects even, why not use the structures that were developed to do so? Otherwise, you code will be a big mess.

In our project we have the same information being passed to the datastore, to core data, to an API, to NSUserdefaults. It’s a mess and easy to get everything mixed up and broken. If we do it the right way, we only need to have the information communicated one time in one place. So if you’re in a rush, sure do what you need to do to get the job done, but by all means, if you can do it right the first time you’ll save yourself a lot of trouble later.