| > `addSubview` That's been my revised working assumption, but it's almost completely unclear from your writings, and also comes from a fairly deep (but understandable!) misunderstanding of GUI frameworks. addSubview is not a defining feature of most GUI frameworks. drawRect is. addSubview is used once in construction, and then you're done. And a lot (if not most) of the time it is hidden, because you just load a GUI definition. For example in Cocoa, you define your GUI in Interface Builder. IB saves a nib/xib, which is an serialised object graph with parameters. You then load that nib and voilà, there's your GUI! The GUI is fully static/declarative. It reacts to dynamic content by drawing it in its "drawRect" method. So where does the misunderstanding come from? It comes from recent changes in how developers (ab-)use GUI frameworks. I haven't full grokked how this came about, but it seems to be part (a) widget toolkits (b) horrible drawing APIs and (c) the iPhone with LayerKit/CoreAnimation. This change has been that for example, when there is just some dynamic text to draw, people have been using text-label widgets instead of just drawing the damn text in drawRect. So suddenly you have people constructing, adding and removing views at runtime as a matter of course, rather than as something done in exceptional circumstances, which I gather is what you (rightly) object to. However, this is not the "programming model" of GUI frameworks, it is an abuse of those GUI frameworks. Which is why your idea that the difference is about programming model, while understandable and somewhat defensible, is ultimately mistaken. To put it succinctly, people are "drawing with widgets", instead of drawing in drawRect: like they're supposed to. So instead of drawRect, they are using addSubview to draw. However, widgets were not meant as a medium for drawing, they were meant as mechanism for constructing the (mostly) static UI that then draws itself, including the dynamic parts. As it is not really the supported way, it is cumbersome and error-prone. If you were to actually adapt the framework APIs to a "drawing with widgets" model, every view would have a "drawSubviews" method in addition to or in lieu of the "drawRect" method. See also: UIs Are Not Pure Functions of the Model - React.js and Cocoa Side by Side https://blog.metaobject.com/2018/12/uis-are-not-pure-functio... There is also a deeper pattern there, which goes back all the way to the beginnings of computer graphics: the back-and-forth between "object-oriented" graphics (see GKS[1], PHIGS[2]) and immediate-mode graphics. (Note that this is not OO in the computer language sense, but in the computer graphics sense). Everybody, it seems has this idea that it would be nice to have a declarative tree of your graphics (and it also happened historically as a result of display-lists for vector graphics). It would also be nice to have a reusable library/API for this. Enter GKS/PHIGS. But then it turns out that things don't quite match up, so you end up having to express your domain graphics as complex (sub-)trees. So you need to imperatively edit the shape tree/database. Which is complex, painful and error-prone. In the end, it becomes easier to just drop the entire shape database and re-create it from scratch every time. At which point the whole idea of a shape database becomes somewhat moot. Enter immediate mode graphics. See OpenGL, Postscript, Quartz, etc. However, drawing everything procedurally is cumbersome. So you add back structure and composable elements. So let's have them be domain-/application-specific, draw themselves in immediate mode and also handle interaction. We might call them "Views". What's neat about Views is that they straddle the "object-oriented" and "immediate" graphics divide, and you can decide yourself where you want to be. You can shift more to the object/widget side and use object-composition, or you can shift towards the immediate-mode side and use procedural drawing. Best of both worlds, at least in theory. And then things happen that make people shift towards the object-graphics side (sucky graphics APIs, phone UIs etc.) and lo-and-behold, we have the same problems that we used to have with GKS/PHIGS! And then we propose the same solution (modulo environmental and accidental differences). And round and round we go. [1] https://en.wikipedia.org/wiki/Graphical_Kernel_System [2] https://en.wikipedia.org/wiki/PHIGS |
drawRect is a primitive but once you start dealing with layout and text measurement I think it can get hairy and at that point you might end up with imperative subview soup again. Somehow people using React don’t fall into that.
drawRect is low level because it only specifies rendering. But UIs usually care about local state and when to destroy or create it. Especially in lists. That’s something I mention in the post which React has a solution for but I don’t think drawRect is sufficient for expressing this generally. See the ShoppingList reordering example.