I recently asked a question regarding collision detection within subviews, with a perfect answer. I've come to the last point in implementing the collision on my application but I've come across a new issue.
Using convertRect was fine getting the CGRect from the subView. I needed it to be a little more complex as it wasn't exactly rectangles that needed to be detected.
on XCode I created an abstract class called TileViewController. Amongst other properties it has a IBOutlet UIView *detectionView;
I now have multiple classes that inherit from TileViewController, and each class there are multiple views nested inside the detectionView which I have created using Interface Builder.
The idea is an object could be a certain shape or size, I've programatically placed these 'tiled' detection points bottom center of each object. A user can select an item and interactive with it, in this circumstance move it around.
Now the method itself works to an extent but I don't think it's working with the nested values properly as the detection is off.
A simplified version of this method works - Using CGRectIntersectsRect on the detectionView itself so I'm wondering if I'm looping through and checking the views correctly?
I wasn't sure whether it was comparing in the same view but I suspect it is, I did modify the code slightly at one point, rather then comparing the values in self.view I took the viewController.detectView's UIViews into the interactiveView.detectView but the outcome was the same.
It's rigged so the subviews change colour, but they change colour when they are not even touching, and when they do touch the wrong UIviews are changing colour
I worked out my issue when using convertRect.
I thought I'd read the documentation again (believe me I've been reading it) but I missed a key piece of information previously. To use convertRect:toView: to use the method the view needs to be target of the conversion operation as mentioned in the doc, but I was using the view itself as the target instead of the parent view
interactRect = [detectInteractView convertRect:[detectInteractView frame] toView:parentView];
This is wrong, I know there isn't many details in this post but ultimately you can't use the same UIView as the target view, or at least if you can I couldn't get it to work here!
Related
My app is behaving sluggishly. If i pop up a UIActionSheet, for example, instead of rolling in smoothly, it stutters in over about 5 frames. I know ideally you should have as little amount of views on screen as possible, but that's what I've got anyway.
Any suggestions for speeding it up?
EDIT:
On my view i have:
Custom navigation bar in place of the regular one. It's a UIImageView, using an image file. It has a quartzcore shadow. It contains 3 buttons. 2 of these buttons have 2 UIImages each, for normal and highlighted, generated from code when the view is shown. The other button uses an image file for normal and for highlighted.
An image file for a background lies under that. On top of the background is a UITableView. By default, it doesn't have any cells (the user adds them). We'll ignore the cell, since it's slugging regardless of their being there or not.
The header of the tableview contains some labels, and an editable uitextview. The size of the header changes as more lines are added to the textview. It also has a background image, which is transparent to allow you to see the view's background image behind it. It's loaded from a file, and a texture image on top of that is also loaded from a file.
The footer is simply a background image loaded from a file with the same texture on top.
Andrew, I'm afraid you haven't been quite specific enough to isolate the exact problem. However there are a couple of things I have picked out. Firstly, check your table view is set to be opaque. Also try to design your app so your table cells can be opaque. I'm assuming your design will allow this. You need to really know how to optimise view rendering performance if you want your table and it's cells to appear translucent over other content and it may be you would need to develop your own custom specialised alternative to UITableView if that is something you really need to know (can be done but quite advanced stuff).
Also you mention using Quartz shadow. You should be able to use UIKit for drawing shadows around images, unless you have some specialist requirement. Are you sure you need to use Quartz for what you want to do? Apologies if you already know this, but if you are fairly new to iOS development and have been looking up how to do shadows, you may have found the Quartz API's for doing that and assumed that is the solution, when (depending on what you need) you will probably be better off staying with UIKit. As a general rule of thumb, only use Quartz if you are sure you can't do what you want to do with just the UIKit API's.
Another thing to check. If you are using Quartz, then you are probably getting getting the graphics context for the UIImage view and drawing on the views context in drawRect: depending on how your view hierarchy is configured, and if you have your navigation bar view set to be transparent over the top of the UITableView, then your custom drawRect implementation may be getting called unnecessarily with every animation frame and this would be a big drain on performance.
Given the level of information you have given I'm having to guess a bit and can't give a precise answer. However for a definitive understanding of how to optimise UIView performance I recommend checking out this video (though you will need an Apple Developer account to be able to access it):
https://developer.apple.com/videos/wwdc/2011/
Session 121 – Understanding UIKit Rendering
Hope this helps. Paul.
Is there a way to draw on the iPhone screen (on a UIView in a UIWindow) outside of that view's drawRect() method? If so, how do I obtain the graphics context?
The graphics guide mentions class NSGraphicsContext, but the relevant chapter seems like a blind copy/paste from Mac OS X docs, and there's no such class in iPhone SDK.
EDIT: I'm trying to modify the contents of the view in a touch event handler - highlight the touched visual element. In Windows, I'd use GetDC()/ReleaseDC() rather than the full cycle of InvalidateRect()/WM_PAINT. Trying to do the same here. Arranging the active (touchable) elements as subviews is a huge performance penalty, since there are ~hundred of them.
No. Drawing is drawRect:'s (or a CALayer's) job. Even if you could draw elsewhere, it would be a code smell (as it is on the Mac). Any other code should simply update your model state, then set yourself as needing display.
When you need display, moving the display code elsewhere isn't going to make it go any faster. When you don't need display (and so haven't been set as needing display), the display code won't run if it's in drawRect:.
I'm trying to modify the contents of the view in a touch event handler - highlight the touched visual element. In Windows, I'd use [Windows code]. … Arranging the active (touchable) elements as subviews is a huge performance penalty, since there are ~hundred of them.
It sounds like Core Animation might be more appropriate for this.
I dont think ull be able to draw outside drawRect...but to get the current graphic context all you do is CGContextRef c = UIGraphicsGetCurrentContext(); hope that helps.
I'm trying to make a little iPad tool's for drawing simple geometrical objects (rect, rounded rect, ellipse, star, ...).
My goal is to make something very close to Keynote (drawing feature), i.e. let the user add a rect (for instance), resizing it and moving it. I want too the user can select many objects and move them together.
I've thought about at least 3 differents ways to do that :
Extends UIView for each object type, a class for Rect, another for Ellipse, ... With custom drawing method. Then add this view as subview of the global view.
Extends CALayer for each object type, a class for Rect, another for Ellipse, ... With custom drawing method. Then add this layer as sublayer of the global view layer's.
Extends NSObject for each object type, a class for Rect, another for Ellipse, ... With just a drawing method which will get as argument a CGContext and a Rect and draw directly the form in it. Those methods will be called by the drawing method of the global view.
I'm aware that the two first ways come with functions to detect touch on each object, to add easily shadows,... but I'm afraid that they are a little too heavy ? That's why I thought about the last way, which it seems to be straight forward.
Which way will be the more efficient ??? Or maybe I didn't thought another way ?
Any help will be appreciated ;-)
Thanks.
I'd use the UIKit classes to do your drawing, then profile and optimise your code from there.
Apple/iPad info: link text
My first feeling was to make the 3rd way, but to be convinced, just after I've posted my message, I did some tests with just a global view and over 200 geometrical forms (Rect, Rounded Rect and Ellipse) on it and I move only a half with touchMoved event. I did this test with the way 1 (Subclassing UIView) and the way 3 (Subclassing NSObject), the way 2 seems to me too restrictive and not help me at all.
The resuslt is that the way 1 seems to be more efficient... There is no lag when I move 60 objects together ! Moreover using this way would probably help me because using view comes which some interesting functions like touch detection on complex path (see UIBezierPath), object hierarchy handled by the UIView classe...
So I will use that way and come back here to share my results ;-)
Regards
It's better to use CGLayer objects. The benefits are:
It's much faster and more memory efficient. For simple objects, adding a view is much more expensive and complicates the view hierarchy. For complicated objects, the caching done on CGLayers can boost performance.
It's easy to group objects together. you just put everything in a new layer, and voila! There's almost no overhead.
Using CGLayer and other Quartz objects gives you a lot more flexibility.
The only drawback is that you have to directly use Quartz 2D. It's not really difficult, but needs some learning if you haven't used it before.
CAShapeLayer pretty much handles your option 2. It does rect and rounded rect (see cornerRadius) by default, or you can give it a path for any arbitrary shape. For your option 1, you can use a CAShapeLayer with a UIView instead of implementing drawRect and it may be faster.
I found a useful tutorial to get started in understanding how to Cocoa handles touch events. I've used this as a base to create a custom script, I'm trying to make a UIView draggable, very similar to the native Maps application.
I've written a custom script, using
the touchesBegan method it will
capture where the input began and
compare it to the centre point of the
UIView using some conditional
statements.
The touchesMoved method will do some
further conditional statements to
determine whether the touch start
point and the center of the view will
move positively or negative.
I've also captured the views
boundaries so it doesn't go too far
out.
it's lacking the polished finished found in other applications such as Maps, or scrolling a UITable, such as the ease effect after the user has released their fingers, and the snapping effect when it reaches the boundaries is horrible.
Is there a method that takes a view and makes it draggable like this? If not I'll continue to refine my script.
Many thanks!
Maybe you are looking for UIScrollView
Louis - Thanks for your input. Here is what I did, and my current issues, which i don't understand.
The menus (2) are UIImageViews that respond to touch. Each is it's own class. The 'sprites' as you called them also respond to touch.
In the .m where I add the menus and sprites to the layer, I created two container layers, via this code: menuContainer = [CALayer layer].
I ordered them with the menuContainer above the spriteContainer. Now, the sprites do move below the menus - great! But in the process, the sprites stopped responding to touch (I can understand this as they are below the menuContainer layer), and the menus stopped responding to touch as well (don't get that). Continuing to confuse the situation, a layer added to the menuContainer that responds to a multitouch gesture by popping up a slider still reads the multitouch and pops up the slider, but I can't slide the slider. This has me thoroughly confounded.
Thanks.
My app is building a background layer, then building some menu layers that should always be at the top.
After this, I add many moving layers and remove many moving layers very quickly.
My problem is I want my menu layers (there are 4) to always be in front of the other layers. I have thought of building a layer hiearchy and using the insertsublayer: atindex: method, making my menus a notional index of 1000, and inserting the multitude of moving layers at a lower index, like 200. If I insert one layer at 200 and then the next at 200, does the first layer that was assigned to 200 get shifted (to 201) or does it get blown away?
I have tried inserting the layers with the insertsublayer: below:, but that does not give me the desired results.
Thanks for the help.
You can't really do that, the layer index is not a z-order, it is an array index. From the documentation:
This value must not be
greater than the count of elements in
the sublayer array.
I think you would be best served but actually making a true hierarchy of layers as opposed to trying to shove all of you active layers into one super layer. In other, but all of your menulayers into a container layer, then insert that in the root layer where you want it. Likewise, insert all your sprites into one container layer, and put that in the root layer where you want it.
Additional stuff based on your edits:
Layers are a essentially a graphical construct, they do not directly respond to events. You either need to handle that yourself by writing code to hitTest them in the view they are attached to, or you need to use UIViews instead of layers. You were probably getting away with it before because you were manipulating the layers in such a way that the view hierarchy and layer hierarchy were consistent, but it was not clear to me from your original question that you were using views and not a purely layer based setup.
Louis -
Thanks for your input. Here is what I did, and my current issues, which i don't understand.
The menus (2) are UIImageViews that respond to touch. Each is it's own class.
The 'sprites' as you called them also respond to touch.
In the .m where I add the menus and sprites to the layer, I created two container layers, via this code: menuContainer = [CALayer layer].
I ordered them with the menuContainer above the spriteContainer. Now, the sprites do move below the menus - great! But in the process, the sprites stopped responding to touch (I can understand this as they are below the menuContainer layer), and the menus stopped responding to touch as well (don't get that). Continuing to confuse the situation, a layer added to the menuContainer that responds to a multitouch gesture by popping up a slider still reads the multitouch and pops up the slider, but I can't slide the slider. This has me thoroughly confounded.
Thanks.