Not sure if this is the right place, but I could use some help figuring out 
best practices for architecting protocol-oriented / value type frameworks in 
Swift, especially where I used to use KVO/KVC in objective C.  Apologies of the 
long email.  It is a very complex project and I am trying to give enough 
background to understand the context.

I am writing a vector/drawing framework in Swift.  It has the capabilities of 
something like Sketch combined with special shape support (similar to Affinity 
Designer).  I have a specific need which I am writing it for, but I have about 
4-5 other ideas which could use it as well, so I am trying to stay as 
general/flexible as possible.

I am very happy with the architecture for the actual drawing part.  It is 
protocol oriented, and very flexible/powerful.  I am a little stumped on how 
best to add interactivity in a similarly powerful/flexible way though…

I have written large editors before in ObjC, and what I typically did there was 
have self-generating UI which connected via KVO/KVC (I basically made my own 
bindings which also generated the necessary UI on the fly).  This made the 
system extremely easy to add new elements to, which meant it could be iterated 
upon quickly.  I want similar capabilities in my Swift framework, but I am 
struggling.

There are 2 main aspects of interactivity:
1) The drag handles - These are different depending on the type of object (e.g. 
rectangle vs bezier path)
2) The inspector controls for the selected object  (e.g. the line width) - 
again different based on the type of object


So far, the best approaches I have come up with use blocks that get passed 
to/from the object in order to keep things decoupled.  For example, for hit 
testing I have the following protocol method which takes a point and an 
“inclusion block” which takes a result and returns whether it is an acceptable 
result.

        func localHitTest(pt:CGPoint, inclusionBlock:(HitTestResult)->Bool) -> 
HitTestResult?

The object then gives the block increasingly detailed descriptions of how it 
was hit, starting with “my bounding box was hit” and then talking about what 
part was hit, or if it was the background of a group that was hit, etc....  You 
reject the more general result using the block to receive more detailed ones, 
or you accept the general ones for speed if you don’t care about the details 
(or return nil if it wasn’t hit).  This allows you to have different behaviors 
when the user single vs double clicks, etc… by providing different blocks (e.g. 
single click may select a group as a whole and double may select an object 
inside of it).


For drag handles, my current approach is to try something similar.  The object 
has a method which vends all possible drag handles, and each handle has an 
array of all possible operations it could perform.  All of this is narrowed by 
an inclusion block:

        func dragHandles(inclusionBlock:(ActionEffectType, 
DragHandle.HandleType)->Bool) -> [DragHandle]

The “ActionEffectType” is an enum defining the effect of the operation so that 
the tool can choose the desired effect. Different tools may have different 
handles they want to display, and things like the option key may change which 
operation is being used (e.g. corner resize vs. symmetric resize around center).

The operations are all structs with blocks that take a delta (for where the 
handle was dragged) and return an updated version of the object.  This part 
feels a bit off/inelegant to me.

It seems to work well enough so far, but I would really like to find a way for 
extensions to add handles as well (right now the object has to define them all 
in a single method).  I am also making it up as I go along, so I am not even 
sure if I am on the right track.  I could be missing an obvious/better solution…


For the inspector controls, I don’t have anything working yet, but I am 
currently thinking of using something similar to the operations above.  That 
is, the object would vend an array of properties which can be altered, and 
attached to each one would be a block which knows how to actually change that 
property.  I can then build UI based on the property types and call the blocks 
when the UI is edited (and ick… regenerate the UI whenever the object is 
changed).

That is my best attempt (currently) at getting KVO/KVC-style flexibility.

Anyone have better solutions/approaches to any of the above?

Thanks,
Jon





_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to