Advanced custom control features in Swift - ios

I'm working on building a custom control. Basically I want to allow the application to generate rectangles (positioned at x = 0 with a variable y value that increases as each rectangle is added).
I'd like them to respond to gestures where they have two positions (closed - which mostly hidden, open - expanded fully so that the entire rectangle is still visible but tethered to the side).
I've already designed an application with this in mind. Seeing as the rectangles will be generated by the users, I assume core graphics would be best for the job. Also, I want the rectangles to display different information based on their gesture-related position.
Is it possible to combine core graphics with these types of controls? I know this is asking a lot.
It's just that I'm having trouble determining how to combine each component in code.
Any advice would be greatly appreciated. Thanks!

Clearly, we're not here to write code for you, but a few thoughts:
You say that you assume Core Graphics would be best for the job. You definitely could, but you could also use CAShapeLayer, too.
So you might create a gesture recognizer whose handler:
Creates a CAShapeLayer when the gesture's state is UIGestureStateBegan and adds it as a sublayer of the view's layer.
Replace that shape layer's path property with the CGPath of a UIBezierPath which is created on the basis of updated location that the gesture recognizer handler captures when the gesture's state is UIGestureStateChanged.
I'd suggest you take a crack at that (googling "CAShapeLayer tutorial" or "UIPanGestureRecognizer example" or what have you, if any of these concepts are unfamiliar).
If you really want to use Core Graphics, you would have a custom UIView subclass whose drawRect draws all of the rectangles. Conceptually it's very similar to the above, but you have to also writing your own rectangle drawing code that you'll put in drawRect, rather than letting CAShapeLayer do that for you.

Related

Best approach for user-placeable custom UIViews?

I'm consider the following usage scenario:
The user adds a new shape to a page
He or she places the shape freely on the current view (e.g. using a pan gesture)
Also, the user can resize the shape (e.g. using a pinch gesture on it)
The shape snaps to already existing shapes on the drawing
Implementation details: A page is a custom UIView. The user can add as many shapes as he or she likes. Shapes are instances of different custom UIViews which needs to adapt their content to the new size (see step 3). Overlapping shapes are not possible. And the user must be able to select shapes for operations like "remove".
Question:
I could simply instantiate the shape UIViews and add them as subview to the page UIView. But then I would need to implement all the details like tracking touches to move UIViews to new positions, running the collision detection, handle resize and so on.
Are there controls/classes/... in UIKit, CoreGraphics or the like available which would make my life easier?
I found UIDynamicBehaviors in UIKit. Would this is be way to go or would you suggest a different approach?
Thanks a lot in advance!
Cheers,
Chris

Usage UIView vs. CAShapeLayer (drawing, animating, user interaction)

I have a pretty basic question: How do you choose between using UIView and CAShapeLayer when you want to draw shapes (I'm not talking about textfields, switches or other controls, just drawing)?
My understanding is that UIView (as part of UIKit) uses a normal CALayer under the hood to draw its content. If this is correct, then CAShapeLayer (or CALayer in general) would be the exact same thing, only without the extras UIKit gives you.
Then, when does using a UIView make sense, and when does using CAShapeLayer make sense?
Is CAShapeLayer faster? Is UIKit more optimised for gesture recognition or user interaction in general?
To give you more context, here's what I was trying to do when this question came up:
I want these red circles to rotate around the center circle. However, the user should be able to tap on the red circles while they are rotating.
Here, I see two main options (there may be more) to add one of those red circles:
Create a UIView, manipulate its layer's cornerRadius and rotate it using CGAffineTransform.
Create a CAShapeLayer with a UIBezierPath. I could rotate it using CATransform3D
The only problem I have here is the user interaction. As it's constantly moving (rotating), I'd have to access the correct frame. I can do that using the presentation layer (which I think UIView is also using under the hood).
At this point, I'm not sure whether to use UIViews or CAShapeLayers. Also, I'm not sure if animating it this way is the correct way in this case. There may be better options that will also erase the question about which one to use.
Thanks for your thoughts about this.
Quoting from Apple's doc
Layers are not a replacement for your app’s views—that is, you cannot
create a visual interface based solely on layer objects. Layers
provide infrastructure for your views. Specifically, layers make it
easier and more efficient to draw and animate the contents of views
and maintain high frame rates while doing so. However, there are many
things that layers do not do. Layers do not handle events, draw
content, participate in the responder chain, or do many other things.
For this reason, every app must still have one or more views to handle
those kinds of interactions.
In iOS, every view is backed by a corresponding layer object but in OS
X you must decide which views should have layers. In OS X v10.8 and
later, it probably makes sense to add layers to all of your views.
However, you are not required to do so and can still disable layers in
cases where the overhead is unwarranted and unneeded. Layers do
increase your app’s memory overhead somewhat but their benefits often
outweigh the disadvantage, so it is always best to test the
performance of your app before disabling layer support.
When you enable layer support for a view, you create what is referred
to as a layer-backed view. In a layer-backed view, the system is
responsible for creating the underlying layer object and for keeping
that layer in sync with the view. All iOS views are layer-backed and
most views in OS X are as well. However, in OS X, you can also create
a layer-hosting view, which is a view where you supply the layer
object yourself. For a layer-hosting view, AppKit takes a hands off
approach with managing the layer and does not modify it in response to
view changes.
Read : https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/CoreAnimationBasics/CoreAnimationBasics.html#//apple_ref/doc/uid/TP40004514-CH2-SW3
Now time for Some QNA
Question 1:
My understanding is that UIView (as part of UIKit) uses a normal
CALayer under the hood to draw its content. If this is correct, then
CAShapeLayer (or CALayer in general) would be the exact same thing,
only without the extras UIKit gives you
Again Quoting Apple doc
Layer-backed views create an instance of the CALayer class by default,
and in most cases you might not need a different type of layer object.
However, Core Animation provides different layer classes, each of
which provides specialized capabilities that you might find useful.
Choosing a different layer class might enable you to improve
performance or support a specific type of content in a simple way
Clearly when you try to draw various shapes using CAShapeLayer is beneficial in terms of performance when compared to CALayer.
Question 2
Is CAShapeLayer faster? Is UIKit more optimised for gesture
recognition or user interaction in general?
Clearly Layers do not handle events, draw content, participate in the responder chain, or do many other things. So none of the layer can recognize user interactions no matter whether its CALayer or CAShapeLayer
Question 3
At this point, I'm not sure whether to use UIViews or CAShapeLayers
As you have specified in your question you want user interactions of red circles and because we are now aware of the fact that CAShapeLayer/CALayer will not respond to user interaction its pretty clear that you have to use UIView rather than layer.

Swift - Detect the user drawing through defined points/areas.

My app already has the drawing enabled for the user on the screen. How can I define certain points in the screen and detect when the user draws through that point? Read about a few swift methods but can't quite grasp if they are applicable for what I need, also I can't find any "collision" methods.
You can use the .containspoint method. However I would recommend you to use a rectangle and not a point. It`s very difficult to draw over one certain point. So you could use CGRect for a rectangle and then again .containspoint(from the user touched point).

Is using CAShapeLayer backed UIView's a better approach than using plain CAShapeLayer?

I am working on a prototype for building a DFD builder. This will have a palette of different objects for drawing DFD. (Rectangle boxes, arrows, Ellipses,circles).
I initially planned to use plain CAShapeLayer's to create objects and add it to the superview's layer (with help of CGPaths or UIBezierPath), so that I will reduce the memory footprint of the application.
Then I realized that using CAShapeLayer and adding them directly in super view's layer makes me to handle touches and use hit test to find the CAShapeLayer that was interacted by the user.
So I am planning to create a Custom UIView backed by CAShapeLayer so that I do not bother about handling the touches and spotting the specific object interacted by user.
Is this approach better than using plain CAShapeLayer? and if yes, please provide the reasons?
UPDATE:
#matt I did a memory testing and the results show that using CAShapeLayer will save memory
Neither approach is "better". A layer cannot exist without a view, and you can hit-test a layer just as well as you can hit-test a view. If you have multiple tappable drawn objects then it is going to be simplest if they are all separate layers so that you don't have to calculate which one was tapped; but that does not mean that they all need to be separate views.
As for "memory footprint", you are optimizing prematurely, a cardinal sin. Have you actual evidence that 100 layers (let's say) takes up less "memory footprint" than 100 views? Unless you do, you can't use that as a decision criterion.

UIView animation vs CALayers

I'm struggling with conceptualizing animations with a CALayer as opposed to UIView's own animation methods. Throw "Core Animation" into this and, well, maybe someone can articulate these concepts from a high level so I can better visualize what's happening and why I'd want to migrate UIView animations (which I'm quite familiar with now) to CALayer animations on the iPhone. Every view in Cocoa-Touch automatically gets a layer. And, it seems, you can animate one and/or the other?!? Even mix them together?!? But why? Where's the line? What's the pro/con to each?
The Core Animation Programming Guide immediately jumps into Layer & Timing Classes and I think need to take a step back and understand why these varied pieces exist and how relate to each other.
Use views for control and layers for eye candy. Layers don't receive events so it's easier to use a view for those cases, but when you want to animate a sprite or backgrounds, etc., layers make sense. Events pass right through layers to the backing view so you can have a pretty visual representation without messing up your events. Try to overlay a view that you're just using for visual representation and you'll have to pass tap events through to the underlying view yourself.
An UIView is always rendered to a CALayer. When you use UIView methods to animate a view, you are effectively manipulating the underlying CALayer.
If you need to do simple things, use the UIView methods. For more complex situatins, or if you want layers not associated with any view in particular, use CALayers.
I've done a bunch of apps in the past year. Here's my rule of thumb:
Use UIView until it doesn't do what you want.
Then move to CoreAnimation. But before you get into it too much...
If you write more than a few animations, use Cocos2D.
UIView transforms are only 2D and are restricted to that, LAyer transforms however can be 3D and you should use those if you want to do 3D stuff, UIView animation will work if you change either the UIView transform or the CALayer transform. So at a basic level, you can do a lot more manipulation when you are working with a Layer rather than the View.
I am not sure if I am misunderstanding Chris' response to "What's Cocos2D doing better? Don't you have other problems then, regarding the touch event handling and many other stuff that misses in openGL ES?"
It sounds like the answer suggests Cocos2D is not based on the OpenGL ES framework when in fact it actual is. While it is a great 2D game engine it does implement OpenGL for much of it's rendering - attached to a physics library it allows for a lot of very interesting possibilities for animation - and Chris is correct - it is a lot less coding indeed.

Resources