Why design patterns from web development makes crappy iOS or Android apps.

Increasingly I’ve been encountering software developed for iOS which was written by someone who cut their eye-teeth on writing UI software for the web in Javascript. And I’ve noticed a common pattern which arises from that code which makes their code overly complex and hard to maintain on iOS.

Basically, they treat UIView-based objects as passive objects.

And if you started live writing software on the web using Javascript, this makes sense. After all, the view structure represented in the DOM hierarchy is by and large a passive participant in the user interface. When you want to present a list of items in a scrolling region, for example, you would create the scrolling region, then populate the items in that region, setting the attributes of each of the <div> sections and <li> sections with the appropriate CSS styles for overflow and font information.

(In something like GWT it’s not hard to create your own custom view objects, just as you can create custom view objects in Javascript. But at the bottom of the stack, the Menu class or the Table class you built simply declares a <div> and manipulates the subset of the DOM within that <div> tag. The view hierarchy is still a passive participant; you simply wrap part of that passive content into something with some logic.)


The problem is when you move all this onto iOS or onto Android or onto Windows or onto MacOS X, you lose one of the most powerful elements of those platforms–and that is Views are active objects rather than passive participants.

Meaning that a child class of a UITableViewCell in iOS can actively know how to present a data record passed to it; you do not need to put the code to populate the table view cell within the UITableViewController class. You can build an Image View which knows how make a network call and load and cache an image without putting image loading and caching code into the UIViewController. You can create a single view which has complex behavior–without having to put the behavior code inside the view controller.

And this allows you to create very complex user interface elements and reuse them throughout your code.

Of course this also needs to be balanced with the available tools. For example, it makes no sense to create a custom UIButton which presents a button with a different font, when you can set the font and appearance of a default button within the NIB.

But it does indicate that in many cases the proper thing to do is to push functionality down into the view, rather than make the view controller responsible for that logic.


You can’t do this in Javascript. You cannot create a new <myTableTag> which takes special arguments and presents the contents in a unique way, which can be reused throughout your site.

And there is nothing inherently “wrong” with putting all the logic for your button’s behavior, the way table view cells are laid out, and the responsibility for loading images in the background into your view controller.

It just makes life far more complex than it has to be, because you’re leaving one of the most important tools in your arsenal on the floor.

Dragging and dropping objects in GWT

If you want to add a click-and-drag handler in GWT, so that (for example) if you click on an image object, you can move it around (and drag content logically associated with it), it’s fairly straight forward.

First, you need to implement a MouseDownEvent, a MosueMoveEvent and a MouseUpEvent handler, and attach them to your image. (Me, I like putting this into a single class, which contains the state associated with the dragging event.) Thus:

	Image myImage = ...
     
	EventHandler h = new EventHandler(myImage);
	myImage.addMouseDownHandler(h);
	myImage.addMouseMoveHandler(h);
	myImage.addMouseUpHandler(h);

Now the event handler needs to do some things beyond just tracking where the mouse was clicked, where it is being dragged to, and how the universe should be changed as the dragging operation takes place. We also need to trap the event so we can handle dragging outside of our object (by capturing drag events), and we also have to prevent the event from percolating upwards, so we get the dragging events rather than the browser.

This means that our event dragging class looks something like:

private class EventHandler implements MouseDownHandler, MouseMoveHandler, MouseUpHandler
{
	private boolean fIsClicked;
	private Widget fMyDragObject

	EventHandler(Widget w)
	{
		fMyDragObject = w;
	}
		
	@Override
	public void onMouseUp(MouseUpEvent event)
	{
		// Do other release operations or appropriate stuff

		// Release the capture on the focus, and clear the flag
		// indicating we're dragging
		fIsClicked = false;
		Event.releaseCapture(fMyDragObject.getElement());
	}

	@Override
	public void onMouseMove(MouseMoveEvent event)
	{
		// If mouse is not down, ignore
		if (!fIsClicked) return;

		// Do something useful here as we drag
	}

	@Override
	public void onMouseDown(MouseDownEvent event)
	{
		// Note mouse is down.
		fIsClicked = true;

		// Capture mouse and prevent event from going up
		event.preventDefault();
		Event.setCapture(fMyDragObject.getElement());

		// Initialize other state we need as we drag/drop
	}

A FlexTable that handles mouse events.

Reverse engineering the GWT event handler code to add new events is simple. Here’s a Flex Table which also handles mouse events:

	/**
	 * Internal flex table declaration that syncs mouse down/move/up events
	 */
	private static class MouseFlexTable extends FlexTable implements HasAllMouseHandlers
	{
		@Override
		public HandlerRegistration addMouseDownHandler(MouseDownHandler handler)
		{
			return addDomHandler(handler, MouseDownEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseUpHandler(MouseUpHandler handler)
		{
			return addDomHandler(handler, MouseUpEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseOutHandler(MouseOutHandler handler)
		{
			return addDomHandler(handler, MouseOutEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseOverHandler(MouseOverHandler handler)
		{
			return addDomHandler(handler, MouseOverEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler)
		{
			return addDomHandler(handler, MouseMoveEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler)
		{
			return addDomHandler(handler, MouseWheelEvent.getType());
		}
	}

Addendum:

If you want the location (the cell) in which the mouse event happened, you can extend MouseFlexTable with the additional methods:

		public static class HTMLCell
		{
			private final int row;
			private final int col;
			
			private HTMLCell(int r, int c)
			{
				row = r;
				col = c;
			}
			
			public int getRow()
			{
				return row;
			}
			
			public int getCol()
			{
				return col;
			}
		}
		
		public HTMLCell getHTMLCellForEvent(MouseEvent event) 
		{
			Element td = getEventTargetCell(Event.as(event.getNativeEvent()));
			if (td == null) {
				return null;
			}

			int row = TableRowElement.as(td.getParentElement()).getSectionRowIndex();
			int column = TableCellElement.as(td).getCellIndex();
			return new HTMLCell(row, column);
		}

GWT and tall images revisited.

As a follow-up to Things to Remember: Why cells with an inserted image are taller than the image in GWT, the answer is:

VerticalPanel panel;
...
Image image = new Image("images/mydot.png");
DOM.setStyleAttribute(image.getElement(), "display", "block");
panel.add(image);

For whatever reason, the image object has ‘inline’ formatting by default, and when GWT assembles the table cell, the cell’s height is being derived from the font height rather than from the image height. Setting the image to block seems to resolve this issue.

GWT Window.Location values.

Arcane knowledge: I wanted to know what values are returned for GWT’s Window.Location class.

For the URL “http://127.0.0.1:8888/index.html?gwt.codesvr=127.0.0.1:9997&foo=555&#8221; the values are:

getHash:
getHost: 127.0.0.1:8888
getHostName: 127.0.0.1
getHref: http://127.0.0.1:8888/index.html?gwt.codesvr=127.0.0.1:9997&foo=555
getPath: /index.html
getPort: 8888
getProtocol: http:
getQueryString: ?gwt.codesvr=127.0.0.1:9997&foo=555

FYI…

Things to Remember: Why cells with an inserted image are taller than the image in GWT

So here’s a mystery.

Suppose you create in GWT a vertical panel or a flex table, and you add an image which is less than 15 pixels tall:

VerticalPanel panel;

...
panel.add(new Image("images/mydot.png"));

But for whatever reason, the cell displays as 15 pixels tall.

Apparently what happens is that the way the image object is inserted into the

object that makes up the vertical panel, an extra bit of text winds up being inserted alongside the image object.

And that blank has vertical height.

If you write the following, you can limit the vertical spacing, allowing for tighter heights:

Image image = new Image("images/mydot.png");
panel.add(image);
DOM.setStyleAttribute(image.getParent().getElement(),"fontSize","1px");

In testing this seems to tighten things up quite a bit.

I need to investigate this further. But apparently when DOM objects are being inserted during the construction of GWT objects, unwanted extra junk (in the form of blank text spaces) is being inserted at the same time.