Every computer app needs to position stuff on the screen. Usually it takes four coordinates for each item: horizontal and vertical positions, plus height and width. The whole setup is called a frame, since it defines a rectangle that encloses the field, button, window or whatever.
Numbers are fine for computers, but not great for humans. We programmers would rather use a drawing program to lay out the screen appearance. Then we can see what we’re doing.
Goldenseal includes a simple drawing program in the Custom Layouts command. It lets anyone drag around fields, and change the appearance of data entry windows, printed forms or reports. Our staff used Custom Layouts to set up the stock layouts. Users can customize anything with it.
To set up the rest of the interface for Goldenseal, our staff used Constructor. It was a simple drawing program that came with PowerPlant, the library we used to build the first Goldenseal. Constructor set up the rest of the appearance that Custom Layouts couldn’t handle.
For Goldenseal Pro, we use similar visual drawing environments built into Xcode (for Mac) and Visual Studio (for Windows). They save a lot of time.
Frames are good enough for a simple screen layout. Everything has its size and place. But, what happens when you resize the window? These days, people expect stuff to rearrange, and stay useful whatever the size and shape of the window they are in. Unfortunately, a dynamic layout is much harder to set up than a static one. There are many complications.
One layout tool that every programming library uses is containers. All the contents of a container will move together. As a bonus, containers make programming a bit tidier. PowerPlant called them panes, Apple’s Cocoa framework calls them views, and Microsoft’s MFC calls them windows (in Windows programming, everything is a window). Containers are generally a good idea, but they do add some complexity.
Cocoa also has separate controllers for each view, as part of their MVC architecture (model/view/controller). Another good idea, but it adds even more complexity. The main window in Goldenseal Pro has 9 different views, each with their own controller. Even worse, we need to swap several different views into the top bar and main record area, depending on what you are doing. It took a few months to get it all working correctly.
The other layout tool found in modern programming libraries is constraints. Basically, they are a way to bind each item to its neighbors, so it knows where to go when its container moves or resizes.
PowerPlant and Constructor had a very simple constraints system, but we rarely used it.
For the Mac version of Goldenseal Pro, it’s required that we use the constraints built into the Cocoa framework. We tried doing without it, but just got errors. Unfortunately, they are a confusing mess.
Apple started out with a ‘struts and springs’ metaphor, pretty much like connecting screen items with paper clips and rubber bands. Then they replaced it with an auto-layout feature in the drawing view. It works great for some things, but can’t be used everywhere.
In OS 10.7, Apple added a NSLayoutConstraint class. Every view needs at least four constraints, and each looks something like this in the code:
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem : statusBar
attribute : NSLayoutAttributeTrailing
relatedBy : NSLayoutRelationEqual
toItem : theRecordView
attribute : NSLayoutAttributeTrailing
multiplier : 1.0 constant : 0];
Throw in a few hundred of those, and the code gets very cluttered. Finally, in OS 10.11, they added yet another constraint system, with much simpler code. Now each constraint looks like this:
[statusBar.topAnchor constraintEqualToAnchor :
theRecordView.topAnchor].active = YES;
It’s much more palatable, and less prone to stupid errors.
Over the past two years, we used frames to lay out some things in Goldenseal Pro. For others we used constraints, with some of each type. The screens looked OK, but we were definitely bumbling around and didn’t always do the right thing. Sometimes we accidentally added duplicate constraints. Some conflicted with others. Some were just plain wrong. Those inconsistencies are probably why the code crashes when we build with the latest versions of Mac OS.
This week we are redoing all the windows to use the most recent Cocoa constraint system. It’s going to be all anchors. Newer versions of Xcode are better at reporting errors, so I think we can finally get it right.