In viewDidLoad, I can create a gradient with no problem:
CAGradientLayer *blueGradient = [[CAGradientLayer layer] retain];
blueGradient.frame = CGRectMake(gradientStartX,gradientStartY,gradientWidth,gradientHeight);
where gradientWith is device-defined as 320 or 1024 as appropriate.
What I can’t do is resize it inside willRotateToInterfaceOrientation: -– and thus get rid of that empty black space off to the right -- after the user changes to landscape mode. (The nav bar and tab bar behave nicely.)
(1) Recalibrating the gradient’s new dimensions according to the new mid-point, (2) using kCALayerMaxXMargin, and (3) employing bounds all looked like they would do the job. bounds looked a litte more intuitive, so I tried that.
I don’t want to admit that I have made zero progress.
I will say that I’ve been reduced to the brute force method of trying every permutation of self, view, layer, bounds, blueGradient, and CGRect(gradientStartX,gradientStartY,newGradientWidth,newGradientHeight) with zero success.
This is not difficult. My lack of understanding is making it difficult. Anyone out there “Been there, done that”?
Does the layer resize its size automatically? If so, simple
[blueGradient setNeedsDisplay];
should do the trick.
Hope this was helpful,
Paul
Related
I found out how to change the colour of constraints:
draw_options = pymunk.pygame_util.DrawOptions(screen)
draw_options.constraint_color = 200,200,200
But when drawing small objects, the size of the constraint appears to be too large and makes it look bad.
Is there a way to reduce the size of those pin joints? Instead of a radius of 5 pixels, I'd prefer 1 or 2 pixel radius joints/constraints.
An alternative was to make it partly transparent, but adding an alpha component to the colour doesn't seem to work.
draw_options.constraint_color = 200,200,200,50
Unfortunately the debug draw color for constraints doesnt work. https://github.com/viblo/pymunk/issues/160
But in general if you want special drawing its probably easiest to do it yourself. Its mainly meant for debugging and quick prototyping, so if you need more than whats included try drawing it yourself instead. There are some examples that does custom drawing and does not depend on the debug draw code.
I have several UISegmentedControls in different view controllers. On the iPad, when the device changes orientation I realign the segments inside these so they line up with the UITableViews underneath. The problem I'm seeing is that although the resizing mask is set for the UISegmentedControl and without any of my code, it resizes to fill the right width, once I try to change the width of the segments within, the segmented will either not stretch all the way to the end, or they'll be too big and go over.
This only seems to happen when the controls are in a split view.
- (void)orientationChanged:(NSNotification *)notification {
[self setHeaderWidths];
}
-(void)setHeaderWidths{
int totalWidth = self.segSorter.bounds.size.width;
int areaWidth = 100,
priceWidth = 100;
int padding = 35;
[self.segSorter setWidth:totalWidth -padding- areaWidth-priceWidth forSegmentAtIndex:0];
[self.segSorter setWidth:areaWidth forSegmentAtIndex:1];
[self.segSorter setWidth:priceWidth forSegmentAtIndex:2];
}
Initially I thought it might be caused by the animation, but even adding a delay so the rotation animation has totally finished has no effect in the width. Are controls within a splitview given a false width or something?
One option you might consider is simply re-creating the control or removing then re-adding the segments.
Without knowing what's going on inside the control, as you change the widths, what if the first one you're resizing (say increasing width) would cause the sum of the widths to exceed the bounds? Without knowing how that logic is working inside, when I've run across things like this that seem like a fight for a simple change, it's easier to roll a method that just recreates the control as you need it (or removes and re-adds the elements) in the specified sizes.
I subclass UISlider in order to make the thumb smaller, and I want to override minimumValueImageRectForBounds and maximumValueImageRectForBounds to make their width 2px less. So my code is
- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds
{
CGRect stdRect = [super minimumValueImageRectForBounds:bounds];
return CGRectMake(stdRect.origin.x + 2, stdRect.origin.y, stdRect.size.width - 2, stdRect.size.height);
}
The point is that stdRect is empty rectangle (0, 0, 0, 0).
Morover, if I explicitly set some rectangle like that
- (CGRect)minimumValueImageRectForBounds:(CGRect)bounds
{
return CGRectMake(2, 0, 40, 8);
}
It doesn't affect minimum value image position at all.
Any ideas?
Haha, I just figured it out. I wasn't reading closely enough.
Setting the minimumValueImage is not the same as calling setMinimumTrackImage:forState:. The minimumValueImage is an image that gets displayed to the left of the slider and is independent what's going on in the slider. Overriding minimumValueImageRectForBounds changes the dimensions of this image (the default size being 0pt wide) and the sliders frame is made less wide and shifted to the right as a result. As I understand it, there isn't a way to modify the rectangle of the minimumTrackImage (such as to make it extend to the right of the thumb image); it is only possible to change the image.
I haven't really figured out the point of allowing you to set the minimumValueImage. It seems like you could accomplish the same thing by changing the size of the slider and adding separate UIImageViews to the side of the slider. Who knows.
Note that everything I've said here applies in the same way for the maximumValueImage and setMaximumTrackImage:forState methods.
DO NOT DO THIS. This is an old, incorrect answer, by past me. I hate that guy.
--
As a general rule, you probably don't want to subclass the Apple standard UI controls. You might want to build it up and set those properties yourself instead (not sure if they are settable, but seems like they should be).
I am looking for an efficient way to handle the image/frame detection from touch methods. Let's say i am building a keyboard or similar to this. I have 'n' number of images placed on the UI. When someone touches an alphabet (which is an image), i can do the following to detect the corresponding letter
1) CGRectIntersectsRect(..,..) : if i use this, then i need to check each & every letter to find out what letter exists at that touch point (let's say 100,100). This becomes O(n). If i move my finger accross the screen, then i will get m points & all corresponding image detection becomes O(n*m) which is not good.
2) Other way is building a hash for each & every x,y position so that the look up will be simply O(1). But again this will be a memory constraint as i need to store 300*300 ( assuming i am using 300*300 screen size). if i reshuffle my letters, then everything needs to calculated again. So this is not good
In other words, i need some thing like , given a point (x,y), i need some way of finding which rectangle is covering that point efficiently.
Sorry for long post & any help would be grateful.
Thanks
If there are in a regular grid, then integer division by the grid size. Assuming you've a small, fixed screen size, a bucket array gives as similar gain ( a 2D grid, where each entry is a list of the rectangles which intersect that part of the grid ) is very fast if tuned correctly so the lists only have a few members. For unbounded or large spaces, KD trees can be used.
It's useful to have the rects you want as final targets set up as subviews as a larger UIView (or subclass) where you expect all these related hits to occur. For example, if you're building your own keyboard, you could add a bunch of UIButton objects as subviews and hit test those.
So, the easy and traditional way of hit testing a bunch of subviews is to simply have code triggered by someone hitting those buttons. For example, you could add the subviews as UIControl objects (which is a subclass of UIView that adds some useful methods for catching user touch events), and call addTarget:action:forControlEvents: to specify some method to be triggered when the user does something in the rect of that UIControl. For example, you can catch things like UIControlEventTouchDown or UIControlEventTouchDragEnter. You can read the complete list in the UIControl class reference.
Now, it sounds like you might be going for something even more customized. If you really want to start with a random (x,y) coordinate and know which rect it's in, you can also use the hitTest:withEvent: method of UIView. This method takes a point in a view, and finds the most detailed (lowest in the hierarchy) subview which contains that point.
If you want to use these subviews purely for hit testing and not for displaying, then you can set their background color to [UIColor clearColor], but don't hide them (i.e. set the hidden property to YES), disable user interaction with them (via the userInteractionEnabled BOOL property), or set the alpha below 0.1, since any of those things will cause the hitTest:withEvent: method to skip over that subview. But you can still use an invisible subview with this method call, as long as it meets these criteria.
Look at UIView's tag property. If your images are in UIViews or subviews of UIView, then you can set each tag and use the tag to look up in an array.
If not, then you can improve the speed by dividing your array of rectangles into sets that fit into larger rectangles. Test the outer rectangles first, then the inner rectangles. 25 rectangles would need only 10 tests worst case as 5 sets of 5.
Thanks Pete Kirkham & Tyler for your answers which are really helpful. Lets say i dont wanna use buttons as i am mainly displaying images as a small rectangles. To check for the rect # (x,y) , i can trigger that easily by making my grid as a square & finding
gridcolumn = Math.floor(pos.x / cellwidth); gridrow = Math.floor(pos.y / cellheight);
But my problem is with touchesMoved. Lets say i started # grid-1 & dragged till grid-9 (in a 3*3 matrix), in this case, i am assuming i will get 100-300 (x,y) positions, so everytime i need to run above formula to determine corresponding grid. This results in 300 calculations which might effect the performance.
So when i display an image as a rect, can i associate some id for that image? so that i can simply just save the ids in a list (from grid-1 to grid-9) so that i can avoid the above calculation.
Thanks for your help
My data visualization app incurs a large memory consumption spike during redraw (setNeedsDisplay which triggers drawRect). I am currently redrawing the entire view that houses the data plot. This view is much larger then the device display.
Is there any way to tell CoreGraphics to allocate just enough memory to draw each element (each element is a small rectangular block much smaller then the device display) and release the memory when done, rather then my current naive approach?
Thanks in advance.
-Doug
UPDATE 8 Dec 8:28am EST
Here is the relevant code with explanatory wordage. I am running Instruments with ObjectAlloc, Memory Monitor, and Leaks instruments running. The only memory leak I have is due has to do with the NSOperationQueue not releasing mems. This is minor an not relevant.
Architecturally the app consists of a tableView with a list of interesting locations along the human genome to inspect. When a table row is selected I enqueue a data gathering operation that returns data called alignmentData. This data is then plotted as horizontal rectangular slabs.
Initially, when the tableView launches my memory footprint is 5 MB.
- (void)viewWillAppear:(BOOL)animated {
// Initial dimensions for the alignment view are set here. These
// dimensions were roughed out in IB.
frame = self.alignmentView.frame;
frame.origin.x = 0.0;
frame.origin.y = 0.0;
frame.size.width = self.scrollView.contentSize.width;
frame.size.height = 2.0 * (self.containerView.frame.size.height);
}
Note: After viewWillAppear: is called the memory footprint has not budged. Even though the alignmentView is be sized well beyond the dimensions of the display.
This is the method called from the data gathering operation.
- (void)didFinishRetrievingAlignmentData:(NSDictionary *)results {
// Data retrieved from the data server via the data gathering operation
NSMutableData *alignmentData = [[results objectForKey:#"alignmentData"] retain];
NSMutableArray *alignments = [[NSMutableArray alloc] init];
while (offset < [alignmentData length]) {
// ...
// Ingest alignmentData in alignments array
// ...
} // while (offset < [alignmentData length])
[alignmentData release];
// Take the array of alignment objects and position them in screen space
// so that they pack densely creating horizontal rows of alignment objects
// in the process.
self.alignmentView.packedAlignmentRows =
[Alignment packAlignments:alignments basepairStart:self.startBasepairValue basepairEnd:self.endBasepairValue];
[alignments release];
[self.alignmentView setNeedsDisplay];
}
After this line of code:
self.alignmentView.packedAlignmentRows = ...
The memory footprint is 13.8 MB
After this line of code:
[self.alignmentView setNeedsDisplay];
The memory footprint spikes to 21.5 MB, stays there for a few seconds then returns to the pre-existing level of 13.8 MB
The solution I am looking for would allow me to essentially, create a horizontal render buffer window that that is the height of a single row of alignment objects. I would allocate its memory render into it, then discard it. I would do this over and over again for each row of alignment data.
In theory, I could render an infinite amount of data with this approach which of course would be most excellent ;-).
-Doug
Here is the - not so obvious answer to my memory problem. I'll give myself this one because I learned it on the Apple dev forum form Rincewind - a very helpful Apple engineer BTW.
It turns out that by slicing a large view into N smaller pieces and rendering into each in turn I will incur a memory spike that is roughly 1/N the size of the large view.
So, for each smaller view: alloc/init, feed a portion of my data, setNeedsDisplay. Rinse/repeat for all N small views.
Simple, eh?
Prior to learning this I had mistakenly thought that setNeedsDisplay:myRect did this for the large view. Apparently not.
Thanks for all the suggestions gang.
Cheers,
Doug
#dugla
"This view is much larger then the device display."
So you're scrolling through the data representation by moving the view around? You might want to consider making your view the same size as the display and using CGTranslate to adjust the drawing offset within your drawRect function. It sounds like you're drawing tons of stuff, and CoreGraphics can't tell what's visible and what is not.
You'll get much better drawing performance if you make the view smaller and insert checks to avoid drawing things that are outside the view's bounds.
This is very possible, you will need to specify which sections of the screen need to be drawn, you need to call setNeedsDisplayInRect as described here and pass in a CGRect which is the area you wish to be redrawn.
This is much, much faster than re-drawing the entire screen, I had issues with this in an iPhone drawing application I created a year and a half ago.
In addition to Ben's suggestion:
If you're scrolling around your data, consider adding a few smaller views to the scrollview. This way you don't need to redraw most of the time, but only when some area of your scrollview isn't covered any more. Basically, if one of your subviews scrolls completely out of sight you'd move it to the opposite side of the visible area and redraw it accordingly.
In my app I'm only scrolling horizontally, and am using two subviews. Let's say view1 is on the left and view2 on the right. When view2 scrolls out of sight, I move it to the left of view1 and redraw it accordingly. If the user scrolls further in the same direction view1 will scroll out of sight as well and I'll move it to the left of view2 and so on.
If you need to scroll horizontally and vertically you'd need 4 views.
I know you are probably aware of this, but have you looked at the Core Plot framework for your data visualization? We recently added touch-scrolling of graphs and we've tried to be conservative when it comes to memory within the framework. Without knowing more about your specific case, this might be something you could try.