Blaze — fast, flexible and awesome

Introduction

Hi, my name is Bob and I’ve been developing iPhone Apps since the release of the first iPhone. I’ve developed about 100+ Apps with a few big names in there such as Weathercube (3+ million downloads). This story is about my need for a new framework to code up to 70% faster and eliminate the boilerplate code that’s been stacking up way more since the release of the iPhone6 and iPhone6+. I believe I’ve created a framework that’s so useful and generic that you can code 99% of the screens with it. It’s completely open-source and developed in Objective-C (compatible with Swift). I use Blaze for every screen in every App and other iOS developers that’ve used Blaze can’t imagine coding new Apps without it. Interested? Read on!
Want to go straight to the Github repo? Click here!

Why

Since the release of the iPhone6 and iPhone6+ my coding experience has completely changed. The iPhone5 was already larger than the iPhone4 but that was only the height. But with these two new iPhone sizes and the iPad Pro we suddenly had 6 completely different screen sizes! I almost felt like an Android developer…😅

What also happened around the same time was my switch to Auto-layout. I’ve been there when Interface builder in xCode3 was still a separate application and back in the days I focused on coding everything without using XIB’s. Especially when the larger iPhone5 was released I thought it was even better to keep everything in code. Boy was I wrong…

I fell in love with Auto-layout 😍. Once you know how to use constraints correctly it’s absolutely brilliant and I leaped over to the ‘Configuration over coding’ standpoint. This simply means I try to do as much as I can in Interface Builder and only code appearance/design stuff when absolutely necessary. This way when something in design changes you don’t need to search through hundreds of lines of code, you simply see it and change it. Fantastic.

Alright, back to the why. The effect the iPhone6(+) had was that designers suddenly started designing for the iPhone6. They completely ignored the smaller iPhones and this caused a big issue: the screens they designed didn’t fit on an iPhone4 90% of the time. Consequently, almost every screen needed to be scrollable!

Now to get technical, there are 6 options in iOS to make a screen scrollable:

  1. A viewcontroller with a manually added Webview
  2. A viewcontroller with a manually added Scrollview
  3. A viewcontroller with a manually added Collectionview
  4. A viewcontroller with a manually added Tableview
  5. A Collectionviewcontroller (Collectionview integrated automatically)
  6. A Tableviewcontroller (Tableview integrated automatically)

I’ll give you a hint, one of these is the best option, so let’s start eliminating. We can quickly remove the Webview one, we’re building awesome apps using native code. (If you’re not you might want to stop reading now)

The Scrollview, number 2, is also not an option since Autolayout and scrollviews are like water and fire. Of course, when you first add a view, then add the scrollview in it, then add a subview to the scrollview with constraints based on the superview of the scrollview it’s doable. But come on…🙄

Ok, so we have Tableviews and Collectionviews left. In options 3 and 4 the views are added manually and this causes them to lose one very important feature the integrated viewcontrollers offer — automatic keyboard avoiding! When you use option 5 or 6 and you have a textfield somewhere in a cell and the keyboard pops up, it automatically scrolls so that the keyboard does not overlap the textfield!

So, options 5 and 6 remain: UITableViewController and UICollectionViewController. Since iOS8 the combination of UITableViewAutomaticDimension and correct constraints work fantastic. The Tableview automatically calculates the necessary height and for me it always works 100% of the time. On UICollectionViewController though, not so much… It’s too buggy and the automatic height calculations are simply often incorrect. So we remove UICollectionViewController and we have a winner — UITableViewController 👍.

I know you might think: ‘but what about those designs that always have a button or something in the bottom-right corner’? Don’t worry, when you use ContainerViews in storyboard and simply embed a UITableViewController you still get all the magic it offers, including the automatic height calculations and automatic keyboard avoiding!

So, I’ve started to use a UITableViewController for almost every screen. It’s automatically scrollable, I can use AutoLayout in my XIB’s and heights are calculated automatically. Wonderful! 😄

BUT, as soon as you use the same thing over and over you start to develop a certain pattern of how to use it. And once you establish a pattern you try to isolate the boilerplate code so you don’t have to write it again and again. The problem is that we’re not talking about a small task you can easily isolate, we’re talking about coding an entire screen! There are so many options/features that can be enabled/disabled and so many datasource/delegate methods to take into account. Basically, I had to create a framework to avoid killing myself when I implement the cellForRow method for the millionth time 😵.

How

So I thought, let’s start with a base Tableviewcontroller I can subclass. Create a couple of base UITableViewCells that I can customize with code. How many different cells could there be, right? It depends on the interaction of the cell, so I started with cells containing a UISwitch, UITextField, UITextView, UISegmentedControl, etc. I created these with AutoLayout in different UITableViewCell XIB’s. Then I created section objects (BlazeSection) containing row objects (BlazeRow) that specify what kind of type your cell is — textfield, textview, switch, etc. The base Tableviewcontroller then serves up the right cell based on the rowtype! No additional code necessary!

Example code

BlazeSection *section = [BlazeSection new];
BlazeRow *textfieldRow = [BlazeRow rowWithRowType:RowTextField];
[section addRow:textfieldRow];

I also created completion blocks for each input-type so I didn’t need to look after UITextField delegates or UISwitch-changes. The code within the cells did all that stuff and the BlazeRow object simply gave it back to me:

textfieldRow.value = @”Initial value”;
[textfieldRow setValueChanged:^{
//Get the new value using textfieldRow.value!
}];

I also added a completion block for when the cell was tapped (no need for didSelectRowAtIndexPath anymore!):

[row setCellTapped:^{
//Cell selected!
}];

So I started using this and in the beginning it worked pretty well. I didn’t have to write anymore UITableView datasource or delegate methods. But after a while I found out I was adding way too many cells to the framework. Every time a cell had one additional UIView subclass (e.g. a cell with 2 labels and 2 imageviews) I had to create another cell and add it to the framework before I could use it in my App. The base-cells weren’t flexible enough to take so many different designs into account and I couldn’t reuse them easily.

So I took a step back and thought; I don’t want to create additional cells in code but I do want flexibility in design. Then it hit me: multiple XIB’s can point to the same code files! All I needed to do was create a base UITableViewCell that has many IBOutlets to support all the different designs. If all the different input-cells inherit from this base-class they also have these outlets available. Then for each new design you only need to create the XIB and point it to the correct code-file! When creating a BlazeRow, instead of providing a row-type you simply set the XIB-name to use. The base Tableviewcontroller does the rest:

BlazeRow *row = [BlazeRow rowWithXibName:@”XibName”];

So I had to create the mother of all UITableViewCells — BlazeTableViewCell. This base-cell contains IBOutlets for at least:

  • Three UILabels
  • Three UIButtons
  • Three UIImageViews
  • Three UIViews

That should cover most cells right? I haven’t seen many single UITableViewCells that have more views in it than this. In case I really had a cell that was so crazy different I created another completion block that returns the cell so you can still customize it:

[row setConfigureCell:^(UITableViewCell *aCell) {
//Cast to your custom cell and customize all you need!
}];

All three buttons also had completion blocks of course:

[row setButtonCenterTapped:^{
//The center button has been tapped!
}];

Well, so said and done. I’ve created the base-cell and I adjusted the row object such that you can fill all those labels, imageviews and buttons with all kinds of stuff. The labels support simple NSStrings but also NSAttributedStrings. The UIImageViews support UIImages from the bundle, NSData to support images from CoreData and even URL’s, I used AFNetworking’s awesome UIImageView category to fill these images.

A great start!

Blaze was already really powerful now. I could create static and dynamic cells without any coding simply by designing the XIB and pointing it to the correct base or input-cell. Then simply code a BlazeRow object with the correct Xibname and assign some possible labels/images and voila, you have a new cell with a couple lines of code 😎:

BlazeRow *row = [BlazeRow rowWithXibName:@”XibName”];
row.title = @”Title”;
row.subtitle = @”Subtitle”;
row.imageViewLeft = @”ImageFromBundle”;

Awesome features

Of course I kept updating the framework and kept eliminating lines of code where possible. Here is a list of awesome features Blaze contains at the moment:

Section headers/footers

Section headers and footers in a UITableView can also use UITableViewAutomaticDimension to calculate heights automatically so I could apply the same principle. I created a base UITableHeaderFooterView called BlazeTableHeaderFooterView and again added several base IBOutlets that you can use. In case of great customization I’ve also created the configureHeader of configureFooter completion block that returns the header/footer. Works great!

Automatic registering of cells and headers

Normally when you use a custom UITableHeaderFooterView or a custom UITableViewCell you need to register these before you can use them. Otherwise the App completely crashes. Well no need to worry about this now! Blaze automatically detects the XIB’s used and registers them for you, even when you add them dynamically.

Automatically saving input-changes

One thing that really bothered me was that when an input changed I had to use the completion block to get the new value and save it somewhere. But what if I simply provide the object and the property-name? This even eliminates the need to set the current value! Now the following line of code is all you need:

[row setAffectedObject:object propertName:@”name”];

This works great in combination with CoreData as well. However, you might not want to hardcode the name. So using a category on NSObject that comes with Blaze you can do this:

[row setAffectedObject:object propertName:[object stringForPropertyName:@selector(name)];

This way the compiler warns you when you enter the wrong property-name!

Draggable Zoom-Header view

We’ve all seen that awesome animation that when you scroll down in a Tableview the image at the top zooms in. Not too difficult to set up but with Blaze only 1 line of code:

self.zoomTableHeaderView = [[NSBundle mainBundle] loadNibNamed:@”ZoomHeaderView” owner:nil options:nil].firstObject;

Here it is in action:

Dates & Pickerviews

I wanted to take as many types of input into account so I’ve created datepicker and pickerview-cells as well. They’re both implemented as an inputview for a UITextField. This way users can keep focusing on the keyboard and quickly provide their input. This is especially nice if you have multiple cells with multiple different inputs. You can see it in action in the next section.

Automatic next/previous inputaccessoryview

When I see a screen with several input-fields, I always appreciate it when the keyboard has these next/previous arrows above the keyboard. So I’ve added these to Blaze as well! Whether the fields are in different sections or whether it’s a normal keyboard, a datepicker or pickerview, it all works automagically ✨:

UITextView automatic increasing rowheight

When you use a UITextView instead of a UITextField you have multiple lines. Wouldn’t it be great though if the UITableViewCell would expand as you type? Yes it would, but it’s crazy painful to get this right. So just use Blaze and it all works automagically as long as you set your constraints right. Check out the example project on Github to see a working example!

Dynamic Rows

I wanted to be able to add rows and remove rows as dynamically as possible. For example, I want to be able to add rows based on the numeric input of a UITextField in another row. Sounds crazy right? Normally this would be incredibly annoying to implement correctly so I’ve implemented it in Blaze in such a way that you can add/remove rows with 1 line of code! All you need to do is assign the rows with ID’s and then you can use this ID to add/remove them, resulting in much more readable code:

[self addRow:row afterRowID:RowID withRowAnimation:UITableViewRowAnimationLeft];
[self removeRowWithID:RowID withRowAnimation:UITableViewRowAnimationRight];

Here you can see it in action:

Empty State

You might know the DNZEmptyDataSet CocoaPod which allows you to easily set empty states. Obviously I had to implement this within Blaze! So it’s now accessible with easy properties, for example:

self.emptyTitle = @”EmptyTitle”;
self.emptyImage = [UIImage imageNamed:@”EmptyImage”];

Dynamic row heights

I usually use the UITableViewAutomaticDimension but sometimes I want to make sure the combined cells covered exactly the height of the screen. So I’ve added the following options:

row.rowHeight = 30; //30 pixels high
row.rowHeightRatio = 0.3f; //30% of the total Tableview’s height
row.rowHeightDynamic = TRUE; //Calculates all other rowheights first and sets the remaining space to this row

Pull to refresh

Of course Blaze supports the UIRefreshControl. Activate it with 1 line and use the completion block to know when the user wants to refresh!

Many quick constructors/setters

To remain blazingly fast Blaze offers tons of quick constructors and setters that you can discover by checking out the source files. For example, if you use the same XIB-file for each row in a section you can simply set the rowsXibname property of BlazeSection. Much better than typing the same line of code for each BlazeRow. And if you use the same xibName for the whole screen simply set the rowsXibName of your BlazeTableViewController subclass. I’ll keep updating Blaze with better constructors/methods to eliminate as much redundant code as possible!

Roadmap

Obviously this is a framework that can always be improved. Here’s a list of things I currently have in mind:

  • Swift 3
  • Floating buttons that are always visible
  • Separator left inset customization
  • Completion blocks for deletion of cells
  • Parallax animation option to the draggable zoom-headerview
  • Additional base-cells in code for some controls currently not supported like a UIWebview, UIPageControl and MKMapView
  • Better documentation :)

Conclusion

So that was the story of why and how Blaze was created! I very much enjoyed creating it and I still enjoy improving it every day. Of course Blaze might not work for everyone. It’ll probably take some time to figure out how to use it exactly and you might have some kind of design/development pattern it doesn’t follow. Obviously for some screen designs (e.g. 3 columns) it doesn’t work and you will need a collectionviewcontroller or something else. However, it helped me and people I work with to develop so incredibly much faster and I will keep updating it as I use it. I’d greatly appreciate it if you could try it out and let me know what you think!