Tom was looking for someone to take over the project because he was unable to maintain it anymore as he joined Apple. Even though I never used ViewDeck myself I always thought it was a very nice and helpful project and I did not want to see it die.
Back then I was working at HRS and already pushed forward the open sourcing of some of the components we developed there. I had some spare time and wanted to get more involved in the Open Source community to give something back after years of benefitting from it. So I contacted Tom and asked him if he already had found a maintainer and if not, I’d love to take over – he had not, so I did take over.
Due to the fact that Tom did not had any time to maintain it for the last couple of years, I was confronted with a huge amount of issues that were lacking a response when I first looked into the repository. My first task was to create a couple of labels and start tagging all the things to get a better overview about what were questions from people that had issues, what might be bugs, and feature requests. I then started answering them as good as possible. There were a couple of issues that required me to dig deep into the code and while doing so I already realized that there are many, many lines of code in ViewDeck that are from the ages where child view controllers were not supported by UIKit – an inconvenient state when you are maintaining a view controller whose purpose is to manage child view controllers. ViewDeck contained, and still contains, a ton of legacy code. The other part of ViewDeck is a ton of code that makes this legacy code work on current iOS versions. And while it was a very noble approach from Tom to keep that project working on all the old iOS versions as well, I quickly decided that I don’t want to go down that route, but I will come back to this in a minute.
While answering a lot of questions and bug reports, I fixed a bunch of bugs and was working on getting the first version of ViewDeck out that fixed the most critical issues that were reported in the current versions of iOS. This is what version 2.4.0 was about. Another thing I did in this version was, I deprecated a lot of methods in IIViewDeckController
. In fact I probably deprecated half of its features. My main reasoning behind this was to cut down the complexity of the controller and see what was actually used in the wild. Each deprecation had a warning on it to file an issue if people would still want to use this feature – the results surprised me, but we come to that later. I did two more bug fix releases and at the time I am writing this the latest release is 2.4.2.
As I said above, I did not want to maintain the library in a way that would still keep it compatible to old iOS versions. It requires a huge amount of work as in my eyes the only way to do this without piling up a huge amount of patches in the code is to always refactor the stuff you are working on to the latest technologies and then make it also work on old versions. If you do not do it this way, you inadvertently end up with code that has the architecture and the features of the minimum iOS Version you are supporting and then a bunch of patches that try to get this code working with the latest iOS versions. This results in a UI that doesn’t feel right anymore as it doesn’t take advantage of all the new features available, while other apps do.
I don’t have the time to do it the right way, the benefit is very little, and I could build a ton of features while instead trying to make a version working almost nobody is using anymore. My goal here is clear: I want to maintain the last two major iOS releases. If stuff works on older versions, fine, but if I need to write a single line of code to get it working on anything that is older than that, it’s not going to happen as it would be code that someone would need to clean up again at some point.
Because of the amount of outdated code and the huge number of features, I started reimplementing ViewDeck 3.0 from scratch after I tried to remove all the outdated stuff piece by piece. There is simply no point in removing a single piece and make the newly written code work with the other outdated parts, just to then start removing the next outdated part. By now I have an implementation that restores the main functionality of showing and hiding side view controllers via buttons and gesture recognizers. I also have put some effort in making the presentation of side view controllers customizable. As I am not entirely settled on the API yet, my plan is to have most of that customization methods still private in 3.0 and then open this up more and more through out the minor releases of ViewDeck 3.
There are a couple of other, smaller things on my todo list for ViewDeck 3.0 but I plan to release a minimal set of features shortly after iOS 10 launches in order to get a new release out as soon as possible. I want to enable people to move to a new version as soon as possible as the current release still has a couple of issues when it comes to rotation and resizing. So I decided to release with the feature set that probably is enough for 90% of the apps using ViewDeck. The other 10% might need to wait a little longer but I see no point in holding back code that works for most people just to take care of a bunch of rarely used features.
I deprecated a lot of features and currently I don’t plan to reintroduce most of them because I did not get a lot of responses after deprecating all these methods. This either means nobody is using them or nobody is updating to the most recent version of ViewDeck. The update behavior may be worse than on other projects as at some point there was a broken release version and the solution was to use an old version. Therefore I think many people have pinned their version of ViewDeck to an old one, not even noticing that there are updates available. In either case it doesn’t really matter if the old features are still in the next major version and the less features there are, the easier it is to maintain everything. If, of course, there are feature requests coming in and enough people would love to see the same feature, I am more than happy to add them. I just want to try and keep things small and simple so that the code base is easy to understand.
Let me know what you think about my approach and check out the v3-reintegration branch on GitHub. I will publish another post about versioning in ViewDeck shortly, so stay tuned.
]]>I am planning on changing a couple of things in this library in order to lift this very big project which technically still supports iOS 4 to a current set of technologies. My goal is to provide a future proof option to easily integrate a side bar into every iOS project.
If you are using ViewDeck, you might at least want to read ‘Transition to 3.x’. But if you don’t even want to read that, the message basically is: There will be a 2.4.0, after that there will be a 3.0.0 and unless you open an issue on GitHub, a couple of features will go away in 3.0.0.
The hamburger menu has always been discussed very controversial. I myself am a very strong objector of the hamburger menu. So how comes I now maintain a library that does exactly that? Because I think the problem with the hamburger menu is that it is done wrong most of the time. Often a couple of people are discussing what the most important aspects of an application are and if they can’t decide, they simply use a hamburger menu and show all of the features of an app, letting the user figure out what really is important. This is bad user experience, in my opinion, and if you have so many features in your app that are all equally important, you may want to reconsider your decision to put them all in one app. For these kind of menus there is the UITabBarController
and there is a reason why it can only show a maximum of 5 elements at a time. This is the use case that I would refer to as hamburger menus.
However there are many other reasons where a side menu might make sense. One is Facebook’s chat list on the right side. The right side of the screen isn’t occupied by any native iOS gestures, so it is a great place to show stuff like a buddy list. This is not a list that shows different features but it is a selection where the user can choose from a list of equal items, in this case their buddies.
The left side of an iOS application is a bit more tricky. In every navigation controller based application the left side of the screen is occupied by the back gesture of the navigation controller. Putting a side menu there leads to a bad user experience. Unless you are an application that is not navigation controller based or you only show the menu in the lowest content controller, where no back gesture is available. A good example for this is Slack. It is an application that technically has a navigation controller but the vast majority of time you spend in the app will be with only one view controller of the navigation stack. So it makes sense to put a list of channels in a left side menu. Metaphorically they are below the main view controller that shows what is going on in a channel, however they are not and should not be the entry point of the application as you are always inside a channel. It makes sense to use a side menu in this case. And again, it shows a list of the same kind of items.
Currently I am working on a stable 2.4.0 version that fixes the most common bugs that are currently in ViewDeck. This probably will not be the last 2.x version but after this I will start working on a 3.0.0 version which, according to my current plan, will remove a couple of features where I don’t see a use case anymore.
I will maintain ViewDeck strickly under the Semantic Versioning system. In short this means, within a given major release (e.g. 2.x) you can be sure that you will not see any breaking changes in the API. There might be methods getting deprecated but they are guaranteed to at least function until the next major release.
With the release of 2.4.0 I will flag a lot of methods as deprecated. This does not necessarily mean that the methods and features will go away in 3.0.0. It is a great way for me to get a feeling of what features are currently in use. If you find one of your favorite features to be deprecated in 2.4.0 please create an issue on GitHub and let me know.
One of the single biggest changes in 3.0.0 will probably be that it removes compatibility to old iOS releases. I am currently planning to make ViewDeck iOS 8+. As the 3.0.0 release will be at least a couple of month in the future, I think by then the adoption of iOS 8 and iOS 9 will be big enough to safely remove iOS 7 compatibility. This enables a lot of other cleanup work in the code. ViewDeck can then finally move to a fully child-controller enabled pattern where it doesn’t need to call appearance methods (viewWillAppear:
, viewDidAppear:
, …) manually anymore. Everybody who has written a container view controller in the past knows that this is a big step forward and probably eliminates a lot of code which always means less bugs.
Another thing that I am pretty sure can be removed is the support for top and bottom menues. Not only does this mean less code to maintain but I also think that these menues collide with the iOS notification center and control center, leading to a bad user experience.
There are other features where I want to change the API for them or, in some cases, remove API but not the feature. ViewDeck currently has too many options and it is very hard to get your head around all of them, especially if you are new to the framework and try to integrate this the first time.
The last thing that I find is very important for a new major release is documentation. For the 3.0.0 release my goal is to have a documentation for all public methods of ViewDeck in the header and in cocoadocs.org.
My goal is to make this a required thing for contributing to this project. In order for a pull request to get merged, the pull request needs to have full, extensive documentation of every newly added public method. Also in order to keep a clean API I will not merge pull requests that implement new features that are not requested by at least a couple of users.
I hope you see my thoughts as an improvement to the library and agree with them. If not, please let me know in the comments or open an issue on GitHub; after all this is an open source project and changes should be made in a democratic way.
]]>UIViewController
series. This time I want to explain what I think is the best practice to do connection handling in your custom view controller instances.
There are a couple of ways to implement a connection handling in your application and different reasons for an application to load data from the web. If you are for example developing an application like a todo application, you probably do not want to load data in the view controllers at all. A better way to do this would be to have a synchronization manager that does all the loading and the view controllers are only showing the locally cached data. If you are developing one of these apps, this post is not helping you at all. Connection handling is not part of your view controller, but updating your view according to changes in your model is, so you might want to have a look at my previous post about model handling in UIViewController. This post is only about the how and when to load data in your view controllers and how to respond to error events.
There is not much to say about it. Use AFNetworking
when making connections! You have a very comfortable block based API when dealing with your requests and you do not need to deal with taming NSURLConnection
in a background thread yourself. The framework is constantly improved and the development community is very active in this open source project. It is maintained by Mattt Thompson, who is well known for a lot of very great projects.
So we are going to load data in our view controllers. My first point is something that you might not be able to do, but still: Do not do it! Well, that is not entirely true, you just should not load the data in the view controller that is also displaying it. In my experience it is a lot easier if the previous view controller starts the connection and presents the displaying view controller only if the connection was successful. In my opinion this also leads to a much better user experience with less effort.
One thing people tend to forget is: Your connection might fail. It is even very likely that your connection will fail. Do not make the mistake to think that everybody around the world has a LTE or 4G connection with speeds around 100MBit/s. Mobile networks are still very bad in a lot of places around the world! Make sure to design your app and your connection handling around a slow EDGE connection. This are the circumstances people will use your app the most in. If you have an app that has something to do with travel, chances are high that a large number of your customers don’t have access to the internet at all, when they are using your app, as most people still have roaming disabled.
The point I am trying to make here is that people will see your error dialog way more often than you think. Design it wisely and make sure it interupts the user’s workflow as less as possible. Empty pages with a text that tells the user that he or she needs an internet connection might look nice, but it forces the user to read the text to realize what went wrong and leave the screen again before he can continue to use your app. A popover telling the user that the request failed with a ‘cancel’ and a ‘retry’ button lets the user quickly decide how to preceed and if he taps ‘cancel’ he is right back where he was.
This is one of the reasons why I think the workflow should be as follows: The user taps on whatever makes the next view controller appear. The current view controller triggers the connection and shows a loading overlay. If an error occurs, show it to the user, either in an alert view or in a custom designed popover. You can do this very easily with the techniques described in my post about custom error handling on iOS. Make sure to give the user an option to retry, if it was an error from the NSURLErrorDomain
error domain. If no error occurs, instantiate the target view controller, set the model on it and display the controller.
To make sure you do not need to setup the same response in a variety of view controllers that all have a transition to the same view controller, you can still let the target view controller create the connection that is needed. With blocks you can create a very easy to use pattern that can be implemented by all of your view controllers:
1 2 |
|
Make sure to implement the first method as a class method. This way you can load the data before you instantiate the view controller. If you use a framework like AFNetworking
the implementation of this method is as simple as creating the request and enqueuing a new operation with it. You already have a success and a failure completion handler that you can use to call the completion handler from in there. If you like the approach of having two different completion handler, one for the success case and one for the failure case, you can easily adjust the method above to represent that as well. If you are using AFNetworking
you should also change the return value of the method to AFHTTPRequestOperation
. The return value of the operation is to be able to cancel the operation if you have to, e.g. when the view controller that started the connection is removed from the navigation stack.
The setData:
method takes the data
from the completion handler and configures the view controller.
So to display a view controller with prefetched data, all you need to do is:
1 2 3 4 5 6 7 8 9 10 11 |
|
If you use the custom error handling I mentioned, your error handling is also only a couple of lines:
1 2 3 4 5 |
|
If you cannot load your data in the presenting view controller for whatever reason, you can still use a very similar API design as above and add a couple of guards. The important part is to make sure the reload trigger can be called at every time, regardless of whether your view controller is currently visible or not. Your view controller does not know when its view will be displayed or hidden, so make sure that it does not matter.
If you do that, you can then load the data during initialization or when your view controller is displayed. Your connection layer does not have to care about the surrondings and so do you. The only thing you should ensure is to not enqueue the operation in your viewDidLoad
method. viewDidLoad
has a very random execution behavior. UIKit
does not gurantee you that this method is only called once. This method could get called multiple times, but the time depends on the current memory situation. Since iOS6 I have not seen this happen anymore, but it is still the documented way for this method! Anyway, this method basically tells you that your view needs to be configured. As we are dealing with a connection here and a connection deals with your model, this simply has nothing to do with your view being loaded, so don’t put it there!
Keep in mind: It is totally valid for a view controller to present another view controller several times instead of instantiating a new one each time! This is one of the reasons why your loading method should not care about the view state of your controlller. The fact that you are not doing this at the moment does not mean you – or anybody else, for that matter – will not do it in the future.
That being said, make sure to call your load method only once during the initial setup of your view controller. If you trigger a reload in viewWillAppear:
, do not already start a request during initialization. That does not mean that you should only trigger your connection during the setup phase. It is totally valid to have a reload
action the user can trigger or poll again after a certain timeout.
Make sure to keep a reference to the connection or the operation that you started to
viewDidLoad
if you are still loading and present a loading indicator in that caseIn your completion handler you then check if your view has already been loaded and dismiss the loading indicator, if this was the case.
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 |
|
If you use this and make your data property’s setter handle the reload as described in my post about model handling in UIViewController, you should be good to go with this approach.
By making your connection property weak
when using AFNetworking
, you have the benefit that as soon as your connection finished, it will be deallocated as the queue is no longer retaining it. With this trick you do not need to nil
your property and you can easily check if a connection is already running.
No matter which of the described approaches you use, keep in mind that most of your users are in a bad network connection! Give them the ability to retry easily if the connection fails. Also try to make your connections state as easily detectable as possible. Your view controller should be able to handle a connection at every point in time, no matter if your UI is visible or not. If you follow this rule, you don’t have to worry about when to reload your data. You can always call reloadDataIfNeeded
.
I have created a library that is open sourced and maintained by my company and me. It implements the workflow described below. You can use it under the Apache 2.0 License and it is available on github as HRSCustomErrorHandling and via cocoapods. If you do so, I would be happy if you’d let me know.
Error handling on iOS has always been very painful. In general you trigger an action on the users behalf in one of your view controllers and you might get an NSError
in return. There are two common approaches to deal with this error:
The latter is definitely the better one as it gives the user a better feedback than ‘something went wrong’. However, it is a much bigger pain for the developer, so it is also the option that is rarely used.
The approach I took was to first look up existing concepts. There is an error handling that is already available in iOS’s big brother, OS X, that reduces the code to display a custom error message to the user to a single line and also gives the user an option to recover from certain errors.
There already is a sample implementation of this error handling from the people over at RealMac software. However a couple of features are missing in their sample that I find very useful in the OS X version.
We wanted to have a way to handle specific errors in a custom and asynchronous way but at a central place in the app. Imagine a server connection that returns an error to you and tells you, that the user is not logged in to the server. You don’t want to simply tell the user that this is the case and you also don’t want to handle this with a specific dialog in every single part of your app that might be able to get this error. Instead there should be a single place in the application that checks for this error and displays your login dialog.
The error handling I am going to describe in this blog post can deal with this and a couple of other scenarios.
Let’s first check which component of the app is responsible for what. Again, OS X is a good guideline for this.
Let’s imagine there is a method that creates an NSError
instance and passes it to the caller. A method like - (BOOL)save:(NSError **)error
. This method usually is somewhere in your data abstraction layer or in other frameworks. If this method is in your code, this is the place where you have as much context information available as possible. You know exactly why the error happened and what went wrong. You might even know what to do to make the error go away. This is the part of code that should put all these informations into the NSError
when creating it.
The caller of the save:
method mentioned above is your view controller in most of the cases. It is the one that knows what lead to the save action and – if there are any parameters to the method it called – it also knows what parameters have been used and what to do if this action should be executed a second time. This layer does not need to have any information about what went wrong. The only information it is interested in is ‘should I try this again or not?’.
There are a couple of rare cases where your view controller might also want to modify or skip error presentation. For example a login view controller might not want to display the general ‘your credentials are invalid’ error, instead it might want to show an inline error message close to the login screen’s input text fields to show the user that something went wrong. So there needs to be a way to customize error handling in view controllers.
Displaying an error is a task that should be handled in general in an applcation. You do not want every error dialog to look different. You might want to show all your errors in a simple alert view or in a custom overlay that integrates nicely into your app’s ui, but they should all look the same.
How to create an NSError
that has all the information mentioned above? The good thing is, NSError
already has an interface to provide all this, and it is right in front of you: Have a look at the keys you can put into the userInfo
dictionary of an NSError
. There are a couple that are quite common when creating custom errors. The one that is used the most is probably NSLocalizedDescriptionKey
. We are not going to use that – bummer.
Instead, we are going to use almost all of the others:
NSLocalizedFailureReasonErrorKey
This describes what went wrong. This is going to be presented in the title of an alert view, later on.
NSLocalizedRecoverySuggestionErrorKey
This describes what the user can do to fix the error. For example you can tell the user something like ‘Try turning it off and on again’, but also some more useful information like ‘Do you want to overwrite the existing file with your new one? This will delete the existing file permanently.’
NSLocalizedRecoveryOptionsErrorKey
This key should contain an NSArray
with localized strings that describe the actions the user can take. Each element is used later on as the text of a button. You should make sure that this key contains at least one cancel operation that simply does nothing except dismissing the alert. A possible array could look like this: @[ @"Overwrite", @"Cancel" ]
One important fact about this array is, that the first option in the array is the default option. This means that if you are going to present the error in an alert view, it is the option on the right most button that is displayed in bold text. In this case that would be the ‘Overwrite’ option, but be carefull when making a destructive operation the default one!
NSRecoveryAttempterErrorKey
Now this is the most interesting key. It contains an object that conforms to NSErrorRecoveryAttempting
protocol. This protocol basically contains a method that can be called with the index of the option the user has choosen. The recovery attempter should then try to fullfil the action behind this option and report back whether it was able to recover from the error or not. If the recovery attempter was successful in recoverying from the error, it is safe for the presenter to retry the action that lead to the error.
With the above keys we are able to carry all information that is necessary to handle an error case properly inside the NSError
object that describes the error. Besides the information shown to the user we can also add logic to the error that can try to recover from an error and communicate back whether we can safely retry the action or not.
OS X is using the responder chain for handling and displaying the error. The responder chain is something that is rarely used in iOS by applications, yet it is used by the frameworks all the time. It is responsible for delivering actions like text input or motion gestures.
It is very useful for transporting information up to the application from every UIResponder
object through out the app. The good think is: A lot of the objects you are dealing with on a daily basis are in fact inheriting from UIResponder
. The one that is well known for being a responder is probably UIView
. But all your UIViewController
s are responder objects, as well. Even your UIApplication
is a responder object, and in the case you are making a game, SKNode
is one, too. This basically means that all your code that deals with presentation is a responder or has very easy access to a responder.
Another great feature about responders is, that they build a chain. Each responder object implements a method called nextResponder
that returns the next responder in the chain. The chain is build from the deepest point in your application up to the application itself and also, since a couple of iOS releases, even up to your app delegate, as long as it inherits from UIResponder
. This means you can dispatch a message to nextResponder
recursively and it will end up in your app delegate eventually, which is a very great place to handle application wide behaviour such as presenting an error. And because of this, that is exactly what we are going to do.
OS X implements a method on NSResponder
that is called -presentError:
which returns a bool telling you whether error recovery was successful or not. Due to the fact that showing modal dialogs on OS X are a blocking operation, this method is blocking, too. Obviously, if you would do this in an iOS application your UI would not be responding anymore and your app would be killed by the watch dog very fast. Luckily we do have blocks now, so this method could easily be changed to something like -presentError:completionHandler:
.
The completion handler should then tell you whether error recovery was successful or not. If it was, you can simply call the method that lead to the error again. As blocks hold on to their context, this can be done very easily in most scenarios.
You need to implement the present error method yourself, as it is not available on iOS, but it is very simple. You could either use the github project I created or do this yourself. All the method does, is calling -presentError:completionHandler:
on the object returned by -nextResponder
. Well, not all. There are a couple of exceptions. Before it does forward the message up the responder chain it calls willPresentError:
on itself.
-willPresentError:
This method gets the error passed in and returns an error. The base implementation of this method simply returns the error it was passed. You can now override this method to change the error you return or even return nil
. If you return nil
, the error is not forwarded up the responder chain by presentError:completionHandler:
.
You can use this to solve the problem mentioned in the introduction. Your login view controller simply overrides willPresentError:
, checks if the error passed in is a login error, and returns nil
in this case. With this hook, every responder in the chain is capable of modifying the error or even stopping the error presentation.
UIViewController
Another exception in -presentError:completionHandler:
is that it forwards the message to -[UIApplication sharedApplication]
if calling nextResponder
returns nil
. This is because a UIViewController
that is currently not participating in the view controller hierarchy will return nil
when asked for its nextResponder
.
There is one thing I simply dropped up to now: The errors you get from a framework you are using do not contain the informations you need for the custom error presentation. To upgrade these errors to enriched ones, you can once again override -willPresentError:
, but this time do it in your app delegate. Now you have a place where you can add nice error messages to your most common framework errors. You should also add a fallback error message that probably simply tells the user that ‘something went wrong’. That is not a very good error message, but it is for sure better than showing the user a ‘The operation couldn’t be completed. (Cocoa error 1570.)’.
Now that we ensured that your error will get passed to your app delegate, all you have to do is to override -presentError:completionHandler:
in your app delegate and configure a UIAlertView
or any other custom view that can show the error to the user. In this single place you can easily customize your error UI to fit perfectly into your app design. No more ugly default alert views in your nicely crafted user interface!
I have open sourced the little library that is driving the error handling in the HRS iOS app I am working on during the day. It has a couple of nice little features that make your error creation even easier. For example you can simply create a recovery attempter with recovery blocks that are executed once the user taps on an option in the alert view. It is available under the Apache 2.0 License on github as HRSCustomErrorHandling and via cocoapods. If you use it, I would be happy if you’d let me know.
I also gave a talk about custom error handling. The slides are available in the talks section.
]]>UIViewController
subclassing best practices I got used to. This series is about how to write a robust view controller that is as dynamic as possible and can be easily adopted to new technices you might use in your app in the future for whatever reason. E.g. you might have handled you view controller instantiation in code in the past but then decided to do it with storyboards; or you might change your underlying connection handling; or you might change from an NSObject
based model structure to Core Data. These are all cases that will end up in a big refactoring of your view controllers if you don’t be careful when implementing them. I did all these things with view controllers that where build with the best practices I am going to describe in this series, and most of the time I didn’t even had to change a single line of code in my view controllers.
In my experience, developers tend to be sloppy when implementing view controllers from time to time. It seems like many people are thinking about view controllers as the passive part that only needs to forward certain events to various layers in your app. The truth is, view controllers are actually one of the most active parts in your app. They need to decide what to do in certain situations and always need to have a proper overview of the state of the application. Therefore it is very important to have a clean structure in your controllers. Once I got used to this structure, I even got faster in implementing my view controllers.
In today’s post I want to talk about proper model handling and how to keep your view controllers clean and reusable, even if you decide to refactor one of the more fundamental parts of your app, like the initialization of your view controllers.
tldr; Do not write custom init methods.
It is as simple as that! There are lots of reasons why not to do this. The most obvious reason is, you loose the option to initialize your view controllers from a xib or a storyboard. Even though this is nothing you might consider at the moment, this is something that could change completely in the future. Apple is very good in making up new things you end up not knowing how you could have ever lived without them. If there will be a new feature you want to use, and it is implemented in Interface Builder, you have to use initWithCoder:
. So make sure, your view controllers work with all designated initializers!
Most custom initialization methods I came across are used to hand over the model that should be displayed to the view controller. Sometimes you might want to pass a couple of parameters to the controller. In general, these models or parameters end up in a property at some point. Expose these properties or add a configuration method to your view controllers. Your view controller does not need a model to exist; really! It might need a model from a product point of view, but not from a technical. All the labels, buttons, text fields and other controls you have in your controller can render without the model. A label might be empty, a text field might not change a text inside your model, but your view controller does not crash. Put an assert inside viewDidLoad
if you are afraid you might end up with a broken view controller, but in general: Handle these kind of guards in your data flow. Do not enforce things technically that are not technically necessary!
tldr; Do not override loadView
; create an update method for your models and call it in viewDidLoad
.
You are loosing the option to initialize your views from a xib file. Interface Builder is the best WYSIWYG UI editor I have worked with, so far. And it is getting better every year. I am pretty confident, that you will end up using it one day, even if you don’t at the moment.
In my eyes, the only reason to override loadView
is to have a custom view controller whoes view property is something else than UIView
or UITableView
. You could write a MyScrollViewController
class, whoes main view is a UIScrollView
. Of course you could also add a scroll view as a subview to the main view in viewDidLoad
, but if you want to implement your view controller in analogy to UITableViewController
this is a valid argument to me, but be aware of the ramifications.
The more common case is, that you only want to setup the subviews. Setting your UI state to match your model can be done in viewDidLoad
without any negative impact vs. doing it in loadView
. In fact you should split up your view creation code from your view configuration code. Build your view hierarchy in viewDidLoad
if you are not using interface builder. This will make sure that every time the main view is instanciated, the view hierarchy is set up accordingly.
Add a method updateViewWithCurrentModel
, put your configuration code in there and call this method from viewDidLoad
after setting up the view hierarchy. With this approach you have a stateless method that always configures your view according to your current model, regardless of when you call it. We will make use of this benefit in the next step.
Do not setup your view hierarchy in viewWillAppear:
! I have seen this a couple of times; in the best case you will create new views even though this is not needed. In the worst case you will end up with a view hierarchy where every subview exists multiple times because you did not remove the old views properly.
tldr; implement a custom setter for your model and call your update method in it.
You can do the next step either by implementing KVO or through a custom setter. I tend to use a custom setter for mainly two reasons. First, there is not very much you can do wrong in a setter since the existence of ARC and second, I don’t like the fact that it is not save to call super in observeValueForKeyPath:ofObject:change:context:
as you need an inside of your superclass that – in my opinion – is non of your bussiness. However if you like KVO better, do it with KVO.
Implement the setter of the property you exposed in the first step yourself (or watch it with KVO). Write your own setMyModel:
method, update the ivar and call your updateViewWithCurrentModel
method in there if your view is loaded. If your view is not loaded, only set your ivar. As you are calling the updateViewWithCurrentModel
in viewDidLoad
as well, you don’t have to worry about this when your view is not loaded. Your setter should look like this:
1 2 3 4 5 6 7 |
|
In case you are in a UITableViewController
you propably don’t need an update method. Simply call reloadData
on your table view instead, but still make sure, the main view is loaded!
It is important to check whether your view is loaded or not. As the setter can be called by anyone at any time, a call to self.view
could cause the view to load even though it is not displayed, which is a memory overhead that is completely useless. A view controller should only load its view when told so by its parent view controller. You should always check if a view is loaded in every method that is exposed in your public controller api before calling self.view
or any method that does this internally.
With the above view controller design you will get another benefit: If you are loading data asynchronously, you do not need a complex state machine anymore. All you need to do is set your model after the loading is done and your view controller will do the rest. I would recommend loading the data in the previous view controller anyway, but that is a topic I will handle in the next post about UIViewController
subclassing.
I uploaded a basic template for a new view controller as MyViewController.h/m to gist. You could easily create an Xcode template from this or generate custom Xcode snippets if you like.
]]>UIView
position and clipping or CALayer
ordering and really makes your life easier. Oliver’s blog post explained a great way to integrate Reveal into your Xcode project without linking it automatically and without having it automatically starting up during app start. There are a couple of benefits with this approach.
As the library is not linked automatically it is not discovered by Apple’s static analyzer. This is great in the case that you forget to remove it before submitting to the App Store. I don’t know if the Reveal library contains private APIs, but as it is a library that is meant to be used during development, I would consider this a possibility, if not at the moment maybe in the future.
The other benefit is that, as the Reveal service is not automatically run at app launch, you can debug your app without interference of the library and only fire up the library when you really need it. In the case you forget to remove it before the App Store build, people that get your app through the App Store still are not able to analyze your view hierarchy with Reveal.
A third great improvement is very useful for all the people that are working in a big office with dozens of other developers. If you have the Reveal library enabled in every build everybody makes, it becomes quite a challenge to select the right device in Reveal. You only want the Reveal service up and running when you really want to debug something.
Oliver used the LLDB init file ~/.lldbinit
to add a couple of aliases to load and start the Reveal library from the debugger:
reveal_load_sim
– loads the Reveal library in the iOS simulatorreveal_load_dev
– loads the Reveal library on the devicereveal_start
– starts the Reveal libraryreveal_stop
– stops the Reveal libraryHowever there are a couple of issues with the described approach that bothered me and that don’t work very well with the workflow we have in the company I work at:
You still need to add the library to the copy step of your project file. Chances are high that you forget to remove this before running the App Store build. Even though the library is neither linked nor started automatically you don’t want a library that you don’t need and that increases your app by almost 4MB in your App Store ipa. This might make the difference between being downloadable through WiFi only or not. Besides that, it looks unprofessional in the case someone discovers this and it might open some security threads you don’t think of.
When adding the library to your copy step you have two options: Use the library that is located in the Reveal.app
folder or copy it to your project. In the first case you break the build process for everybody that doesn’t have Reveal installed as the file will not be in the place where Xcode is looking for it. With the second approach you will run in to problems when different engineers have different versions of Reveal on their machines. it is pretty likely anyway that you will forget to update the library in the first place and only notice this when you really need it. Updating the library requires you to stop the running application and rebuild it after you updated the library. This reduces the benefit of the automatic approach dramatically.
Last but not least I don’t want to have two load commands, reveal_load_sim
and reveal_load_dev
. This is more complicated than it should be. The debugger should be able to figure that out by itself or at best use the exact same approach on the device and simulator.
Let’s try to fix that little inconvenience first. With Oliver’s approach we use the following aliases:
1 2 |
|
The first alias is reveal_load_sim
. It loads the Reveal library from the Reveal.app
folder. This is a path that you obviously can only reach in the simulator. The second alias, reveal_load_dev
, loads the libReveal.dylib
from the application bundle. This only works when you add the library to your copy step before.
When you need to copy and update the library manually within your project it is quite helpful to be able to use Reveal in the simulator without copying the library first. As we try to improve this behavior in the next step anyway, let’s simply remove the reveal_load_sim
macro and rename reveal_load_dev
to reveal_load
. Our complete list of Reveal aliases in the ~/.lldbinit
now looks like this:
1 2 3 |
|
Now that we optimized the load command let’s make sure that we always have the library version that our currently installed version of Reveal needs and that Reveal is not included if it is not available on the machine that is performing the build. Instead of adding the framework to the copy step we simply use a ‘run script’ phase in our build progress to copy the framework into the .app folder. This script can check for the configuration you are currently building and wether the framework exists in the Reveal.app
folder.
1 2 3 4 5 |
|
The first line defines the path to the .app folder of the application we are building. The second line defines the location of the libReveal.dylib
file if you have a standard Reveal installation. Now we only have to perform two checks:
ReleaseAppStore
? This is the configuration we are using for App Store distribution. You might need to change this to meet your environment. Maybe you are simply using the default Release
build for App Store distribution or maybe you have another configuration name. Simply replace ReleaseAppStore
to whatever configuration you are using. This ensures that the library is only copied when you are not building for the App Store.If you save this file somewhere in your project path, all you have to do is add a ‘run script’ phase to your project and call this project by inserting the following line in the phase:
1
|
|
Adjust this path to meet your environment and you are ready to go.
With this script and the above LLDB aliases everybody that has Reveal installed can simply pause the application at any time in the debugger and simply type reveal_load
followed by reveal_start
. After that, don’t forget to resume your application. You can now immediately attach Reveal to your device. The library that is used in your project will automatically stay up to date with your Reveal version and you don’t have to change anything in your project before submitting to the App Store.
I am only using this approach for a couple of days now and it has already been very useful as you can fire up Reveal anytime you have a UI issue without the need of restarting the application and reproducing the bug first.
I uploaded the two files you need to gist in the case you want to simply copy them:
.lldbinit
: https://gist.github.com/michaelochs/7614557lazy-copy-reveal.sh
: https://gist.github.com/michaelochs/7614574The solution to this problem sounds quite easy at first: As soon as you simply blur the background just a little, the whole thing starts looking great again. This is where CoreImage comes in handy. CoreImage has a ton of useful filters, one of them is a gaussian blur filter. Using this filter is pretty easy. All you need to do is specify you input image and the blur radius you would like to use.
1 2 3 4 |
|
Note that you are dealing with CIImage
’s instead of UIImage
’s. So inputImage
has to be a CIImage*
.
Now that we know how to blur, the question would be: What to blur? And here is the point where things are getting difficult. There are three methods on CALayer that sound promising:
setBackgroundFilters:
“An array of Core Image filters to apply to the content immediately behind the layer”setFilters
: “An array of Core Image filters to apply to the contents of the layer and its sublayers”setCompositingFilter
: “A CoreImage filter used to composite the layer and the content behind it”All three methods are available on iOS, you can call them, the filters are set and they will be returned if you call the getter. But that’s pretty much all these methods do, at least up to iOS6. Under “special considerations” Apple says “This property is not supported on layers in iOS”. Note that there was an error in the documentation on iOS 6.0, where it said “This property relies on the presence of Core Image and its filters. In iOS, Core Image is available in iOS 5 and later only”. They changed this in the iOS 6.1 documentation, so don’t wonder if you see the second text, you selected the old version of the CALayer
documentation.
So using one of these properties won’t work on iOS. The only other approach I am aware of on iOS, is to render the underlaying layers into a context and blur it yourself. You can do this with CALayer
again. The method you are looking for is -[CALayer renderInContext:]
This method renders the receiver and it’s sublayers to the context you specify. ‘But I want to render the views!’ you might think. That’s correct, but UIView
is just the delegate of the underlying layer. Each UIView
has a layer it is responsible for and the layer hierarchy is the same as the view hierarchy – unless you change something about this by yourself. The visible part of a UIView
is the CALayer
! If a view has five subviews, its layer has five sublayers and each of these sublayers belong to one of the subviews.
To render the background of the button we want to blur, we simply walk up the view hierarchy until we find the first view that is opaque. If the view is opaque we can safely render this view and its subviews without worrying about transparent parts from underlaying views.
To achieve this, we create a new graphics context in the size of the button that should get the blurred background. We don’t need to render the whole superview we just found, as we only need to blur the part that is behind our button. The rendered image is what we will use as our input image for the blur filter. Don’t forget to hide the button during rendering, otherwise the button itself will be contained in the button’s background.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
After rendering the view into a CGImage
we convert this to a CIImage
and set it as the input image of our blur filter. If you put all this code into your button’s drawInRect:
method, you can then draw the output image into the current context for displaying. After that, you can call all the other drawing code, if any, you need for your button.
1 2 3 |
|
Not that we render the output image with the extent of the CIImage
as the blur filter will return an image that is larger than your input image. The extent property of you output image has a negative origin so that rendering the image with this extent will place your image exactly above your input image, with a border.
I have set up a GitHub project that adds a method to UIView
that renders the view’s background with a given blur radius into the current context. You can call this method on any UIView in it’s drawRect:
method to get a blurred background. The static library also contains two subclasses, one of UIView and one of UIButton. Those adopt this category and, aside from others, make the blur radius available as a property for easier use.
This approach only works with still backgrounds. If you have any kind of animation in the background, this will not work. If the background changes, you have to call setNeedsDisplay
on the view that has the blurred background. Note that blurring takes time, it is not possible to e.g. put a UIScrollView
in the back of the button and call setNeedsDisplay
on every scroll position change of the scroll view.