Best approach for user-placeable custom UIViews? - ios

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

Related

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.

Advanced custom control features in Swift

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.

iOS: Blur with custom shape

I have a GameViewController with some different subviews representing game object, such as scores, players, history, etc...
The user can drag around custom shapes on the GameBoard (CAShapeLayer).
I want to add a blur to the custom shapes. However, to first blur the background with all the game elements and then mask the image to the custom shape is horrible slow.
Is this possible to do with good performance, and how would you do it in that case?
Thanks
You should use GPUImage library if you are concerned about performance. https://github.com/BradLarson/GPUImage
This tutorial will guide you completely about how to use the blur functionality you need:
http://www.raywenderlich.com/60968/ios-7-blur-effects-gpuimage

UIDynamicItem with non-rectangular bounds

So I'm looking into UIKit Dynamics and the one problem I have run into is if I want to create a UIView with a custom drawRect: (for instance let's say I want to draw a triangle), there seems to be no way to specify the path of the UIView (or rather the UIDynamicItem) to be used for a UICollisionBehavior.
My goal really is to have polygons on the screen that collide with one another exactly how one would expect.
I came up with a solution of stitching multiple views together but this seems like overkill for what I want.
Is there some easy way to do this, or do I really have to stitch views together?
Dan
Watch the WWDC 2013 videos on this topic. They are very clear: for the sake of efficiency and speed, only the (rectangular) bounds of the view matter during collisions.
EDIT In iOS 9, a dynamic item can have a customized collision boundary. You can have a rectangle dictated by the frame, an ellipse dictated by the frame, or a custom shape — a convex counterclockwise simple closed UIBezierPath. The relevant properties, collisionBoundsType and (for a custom shape) collisionBoundingPath, are read-only, so you will have to subclass in order to set them.
If you really want to collide polygons, you might consider SpriteKit and its physics engine (it seems to share a lot in common with UIDynamics). It can mix with UIKit, although maybe not as smoothly as you'd like.

Multiple Dynamically drawn Polygon Dragging

I want to put several dynamically drawn polygon in a UIView and make each of such polygon draggable. Should I put each dynamically drawn polygon in a subview and add that to a UIView (Which seems is not possible as one UIView can only display one subview) or should I draw each polygon in different CALayer and cascade the touch event to each layer?
I think your assumptions are wrong. A UIView can certainly contain and display multiple subviews. (That's why subviews is an NSArray -- because there can be many of them.) So yes, you should create a new UIView for each polygon and add them as subviews to your main view.
How do you draw multiple draggable polygons? If you're registered as an iOS developer, you can go to the Apple developer resources and look up the WWDC2011 session videos. In WWDC2001, Session 118, "Making the Most of Multi-Touch on IOS", Ken Kocienda demonstrates an app that does this using gesture recognizers to sense the touches. I don't think the code of the app is available, but he discusses it at length.
It depends on your number of polygons and how dynamic they are. If you have a high number of them, it will be costly to have hundreds of UIViews; in that case you'd be better off with CALayers and tracking the positions manually. If you only have a handful of polygons, then don't complicate your life and go for UIViews.

Resources