I'm trying to display a grid view in a portrait and horizontal view of the iPad. Right now im just trying to pull this off on the portrait view, so you will see the according X, Y in the following code where i'm stuck.
- (void)renderPlaylist {
cellsArray = [[NSMutableArray alloc] init];
CGFloat xPos = 0.0f;
CGFloat yPos = 0.0f;
int i = 4;
int y = 1;
int p = 1;
for (BCVideo *video in [[self playlist] videos]) {
if (i > 0) {
ScrollCellViewController *cell = [[ScrollCellViewController alloc] initWithBCVideo:video];
[cell setDelegate:self];
[[cell view] setFrame:CGRectMake(xPos, yPos, 768.0f, 245.0f)];
[scrollView addSubview:[cell view]];
[cellsArray addObject:cell];
yPos = y * 235.0f;
y++;
i--;
}else{
i = 4;
xPos = p * 245.0f;
yPos = 0.0f;
y = 1;
p++;
}
//blabla lazy
CGSize size = CGSizeMake(2000.0f, 2000.0f);
[scrollView setContentSize:size];
}
}
The total of videos is 17. I want to display 4 videos in each row, and then move right, for the next row, the scroll view will be horizontal (like the TED app). It obviously needs to display four rows of 4, and then one row of 1. This currently outputs 3 rows of 4, and then 1 of 2. For a total of 14?
what?
exactly.
If i adjust the code to rows of 5, you get a total of 15 videos. If i just the code to rows of 6, you get a total of 16 videos.
Screenshot http://i53.tinypic.com/15oj687.png
Your code is wrong in several parts.
By following it and calling U the upper part of the "if" and L the lower part of the if, you repeat this pattern:
UUUULUUUULUUUULUU
Now every 4th U you calculate y=4*235=940 as upper-left corner and it is probably out of your view (e.g. if you have a navigation bar your view height will be exactly 940 pixels height!). So the 4th row is not visible because out of screen.
Besides each U cycle draws a view which is 768px wide and 245 high so it is an horizontal stripe. Now the first 3 Us from the first three videos draw the first three lines, the 4th U draws the line out of screen. Then the "L" does nothing, it skips the video (sic!) and set "p" such that the next sequence of 4 stripes will be shifted 245px right, which is probably buggy as the stripes are still 768px wide, so the next cycles these stripes will be partially out of screen (over the right border). Besides when you will start drawing the second sequence of UUUU you will overwrite them over the existing ones but shifted 245px right.
Well, I don't know what you want to do, but it seems quite complex for a grid and it is obviously buggy. You should take care of your "swiping" code before and then you can debug it by printing in the console the position of all your videos and try to debug it. Don't try to fix it by looking for magic numbers in the parameters.
Solution:
- (void)renderPlaylist {
cellsArray = [[NSMutableArray alloc] init];
CGFloat xPos = 0.0f;
CGFloat yPos = 0.0f;
// y => counter for position of vertical videos
int y = -1;
// p => number of rows
int p = 0;
// r => check
int r = 0;
for (BCVideo *video in [[self playlist] videos]) {
r++;
// vertical rows
if (y < 3) {
y++;
yPos = y * 200.0f;
}else{
// move sideways to the right one row, and reset the vertical row
p++;
xPos = p * 255.0f;
yPos = 0.0f;
y = 0;
}
//print video
ScrollCellViewController *cell = [[ScrollCellViewController alloc] initWithBCVideo:video];
[cell setDelegate:self];
[[cell view] setFrame:CGRectMake(xPos, yPos, 255.0f, 210.0f)];
[scrollView addSubview:[cell view]];
[cellsArray addObject:cell];
CGSize size = CGSizeMake(xPos + 250.0f, 1024.0f);
[scrollView setContentSize:size];
}
}
Related
This has been driving me crazy.. I have a large image, and need to have a view that is both zoomable, and scrollable (ideally it should also be able to rotate, but I've given up on that part). Since the image is very large, I plan on using CATiledLayer, but I simply can't get it to work.
My requirements are:
I need to be able to zoom (on mouse center) and pan
The image should not change its width:height ratio (shouldn't resize, only zoom).
This should run on Mac OS 10.9 (NOT iOS!)
Memory use shouldn't be huge (although up to like 100 MB should be ok).
I have the necessary image both complete in one file, and also tiled into many (even have it for different zoom levels). I prefer using the tiles, as that should be easier on memory, but both options are available.
Most of the examples online refer to iOS, and thus use UIScrollView for the zoom/pan, but I can't get to copy that behaviour for NSScrollView. The only example for Mac OS X I found is this, but his zoom always goes to the lower left corner, not the middle, and when I adapt the code to use png files instead of pdf, the memory use gets around 400 MB...
This is my best try so far:
#implementation MyView{
CATiledLayer *tiledLayer;
}
-(void)awakeFromNib{
NSLog(#"Es geht los");
tiledLayer = [CATiledLayer layer];
// set up this view & its layer
self.wantsLayer = YES;
self.layer = [CALayer layer];
self.layer.masksToBounds = YES;
self.layer.backgroundColor = CGColorGetConstantColor(kCGColorWhite);
// set up the tiled layer
tiledLayer.delegate = self;
tiledLayer.levelsOfDetail = 4;
tiledLayer.levelsOfDetailBias = 5;
tiledLayer.anchorPoint = CGPointZero;
tiledLayer.bounds = CGRectMake(0.0f, 0.0f, 41*256, 22*256);
tiledLayer.autoresizingMask = kCALayerNotSizable;
tiledLayer.tileSize = CGSizeMake(256, 256);
self.frame = CGRectMake(0.0f, 0.0f, 41*256, 22*256);
self.layer = tiledLayer;
//[self.layer addSublayer:tiledLayer];
[tiledLayer setNeedsDisplay];
}
-(void)drawRect:(NSRect)dirtyRect{
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGFloat scale = CGContextGetCTM(context).a;
CGSize tileSize = tiledLayer.tileSize;
tileSize.width /= scale;
tileSize.height /= scale;
// calculate the rows and columns of tiles that intersect the rect we have been asked to draw
int firstCol = floorf(CGRectGetMinX(dirtyRect) / tileSize.width);
int lastCol = floorf((CGRectGetMaxX(dirtyRect)-1) / tileSize.width);
int firstRow = floorf(CGRectGetMinY(dirtyRect) / tileSize.height);
int lastRow = floorf((CGRectGetMaxY(dirtyRect)-1) / tileSize.height);
for (int row = firstRow; row <= lastRow; row++) {
for (int col = firstCol; col <= lastCol; col++) {
NSImage *tile = [self tileForScale:scale row:row col:col];
CGRect tileRect = CGRectMake(tileSize.width * col, tileSize.height * row,
tileSize.width, tileSize.height);
// if the tile would stick outside of our bounds, we need to truncate it so as
// to avoid stretching out the partial tiles at the right and bottom edges
tileRect = CGRectIntersection(self.bounds, tileRect);
[tile drawInRect:tileRect];
}
}
}
-(BOOL)isFlipped{
return YES;
}
But this deforms the image, and doesn't zoom or pan correctly (but at least the tile selection works)...
I can't believe this is so hard, any help would be greatly appreciated. Thanks :)
After a lot of research and tries, I finally managed to get this to work using this example. Decided to post it for future reference. Open the ZIP > CoreAnimationLayers> TiledLayers, there's a good example there. That's how CATiledLayer works with OS X, and since the example there doesn't handle zoom very well, I leave here my zoom code
-(void)magnifyWithEvent:(NSEvent *)event{
[super magnifyWithEvent:event];
if (!isZooming) {
isZooming = YES;
BOOL zoomOut = (event.magnification > 0) ? NO : YES;
if (zoomOut) {
[self zoomOutFromPoint:event.locationInWindow];
} else {
[self zoomInFromPoint:event.locationInWindow];;
}
}
}
-(void)zoomInFromPoint:(CGPoint)mouseLocationInWindow{
if(zoomLevel < pow(2, tiledLayer.levelsOfDetailBias)) {
zoomLevel *= 2.0f;
tiledLayer.transform = CATransform3DMakeScale(zoomLevel, zoomLevel, 1.0f);
tiledLayer.position = CGPointMake((tiledLayer.position.x*2) - mouseLocationInWindow.x, (tiledLayer.position.y*2) - mouseLocationInWindow.y);
}
}
-(void)zoomOutFromPoint:(CGPoint)mouseLocationInWindow{
NSInteger power = tiledLayer.levelsOfDetail - tiledLayer.levelsOfDetailBias;
if(zoomLevel > pow(2, -power)) {
zoomLevel *= 0.5f;
tiledLayer.transform = CATransform3DMakeScale(zoomLevel, zoomLevel, 1.0f);
tiledLayer.position = CGPointMake((tiledLayer.position.x + mouseLocationInWindow.x)/2, (tiledLayer.position.y + mouseLocationInWindow.y)/2);
}
}
in my application I have a class name playGrid which contains three objects, an NSArray of images named plays, and two NSUIntegers rowCount and columCount for the amount of rows and columns in the popover that will be used.
For this example, I have 6 images and I am trying to show them over 2 columns and three rows. I am trying to display these views in a popover. Previously I was able to do this with blocks of colors, but now that I am trying to do with images I am unable to generate the popover correctly. Listed below is my drawRect code for the successful popover that shows the colors.
How would I convert this to work with UIImages instead of the colors?
In the example below, rowCount and columnCount are 2 and 3 just like the one we are trying to make, but the array is named colors, and contains six UIColor items.
- (void)drawRect:(CGRect)rect {
CGRect b = self.bounds;
CGContextRef myContext = UIGraphicsGetCurrentContext();
CGFloat columnWidth = b.size.width / columnCount;
CGFloat rowHeight = b.size.height / rowCount;
for (NSUInteger rowIndex = 0; rowIndex < rowCount; rowIndex++) {
for (NSUInteger columnIndex = 0; columnIndex < columnCount; columnIndex++) {
NSUInteger colorIndex = rowIndex * columnCount + columnIndex;
UIColor *color = [self.colors count] > colorIndex ? [self.colors objectAtIndex:colorIndex] : [UIColor whiteColor];
CGRect r = CGRectMake(b.origin.x + columnIndex * columnWidth,
b.origin.y + rowIndex * rowHeight,
columnWidth, rowHeight);
CGContextSetFillColorWithColor(myContext, color.CGColor);
CGContextFillRect(myContext, r);
}
}
}
I know I will not need certain things like the color or the CGContextSetFillColorWithColor line, but how do i replace myContent with the image, it seems like this is what I would have to do, but I have not been able to successfully do this. Thanks again for your help as I am newer to Objective C.
Assuming that you want to continue to do drawing into the view using drawRect:, I think you just want to use the drawInRect: method on UIImage.
So:
UIImage *image = [plays objectAtIndex:rowIndex * columnCount + columnIndex];
[image drawInRect:r];
I've heard the performance of drawInRect is not great - but I haven't measured it myself. Note also that drawInRect: scales the image to fit the rect as needed; so visually, the results may not be what you intend.
An alternative is to compose your popover's view in a nib where you could layout your view statically. If you always want a 2x3 matrix, you could setup your view there with a 2x3 grid of UIImageView instances.
EDIT: (to clarify that you are drawing images in the rects now, no filling blocks of color)
- (void)drawRect:(CGRect)rect {
CGRect b = self.bounds;
CGContextRef myContext = UIGraphicsGetCurrentContext();
CGFloat columnWidth = b.size.width / columnCount;
CGFloat rowHeight = b.size.height / rowCount;
for (NSUInteger rowIndex = 0; rowIndex < rowCount; rowIndex++) {
for (NSUInteger columnIndex = 0; columnIndex < columnCount; columnIndex++) {
CGRect r = CGRectMake(b.origin.x + columnIndex * columnWidth,
b.origin.y + rowIndex * rowHeight,
columnWidth, rowHeight);
UIImage *image = [plays objectAtIndex:rowIndex * columnCount + columnIndex];
[image drawInRect:r];
}
}
}
I am working on a game and I would like to add a proper slicing feature in it.. so when a sprite sliced, 2 new sprites should be created.. please check here
At the moment, I am just reducing the size and duplicating the sprites.. Something like this.. Thanks in advance..
- (BOOL) sliceSprite: (Sprite *) sprite withPath: (UIBezierPath *) slicePath
{
CGSize size = sprite.size;
size.width /= 2;
size.height /=2;
sprite.size = size;
sprite.sliced = YES;
Sprite *newSprite = [[Sprite alloc] initWithImage: sprite.image];
newSprite.position = sprite.position;
newSprite.size = size;
newSprite.sliced = YES;
newSprite.inView = YES;
newSprite.xVelocity = SLICE_SPEEDUP * sprite.yVelocity;
newSprite.yVelocity = SLICE_SPEEDUP * sprite.xVelocity;
newSprite.angularVelocity = -SLICE_REVUP * sprite.angularVelocity;
[sprites addObject: newSprite];
[newSprite release];
sprite.angularVelocity = SLICE_REVUP * sprite.angularVelocity;
sprite.xVelocity = -SLICE_SPEEDUP * sprite.xVelocity;
sprite.yVelocity = -SLICE_SPEEDUP * sprite.yVelocity;
return YES;
}
- (void) sliceSpritesInSwipePath
{
CGRect swipeRect = [swipePath bounds];
for (NSUInteger i = 0; i < [sprites count]; i++)
{
Sprite *sprite = [sprites objectAtIndex: i];
if ([sprite intersectsWithPathInArray: swipePoints
inRect: swipeRect])
if ([self sliceSprite: sprite withPath: swipePath])
{
[self resetSwipe];
if (![sliceSound isPlaying])
[sliceSound play];
break;
}
}
}
Is the specific line of splitting required? Fruit Ninja just spawns two halves of the fruit, as if it was split down the middle, this would be quite easy to do:
Create two sprites which are half the width of the original sprite
Position them 1/4 and 3/4 of the way along the original sprite's horizontal centre line
Add rotation/acceleration etc.
Modify texture coordinates so that the left sprite has the left half of the texture and the right sprite has the right half of the texture.
Since you're using CoreGraphics here, why not simply use a clipping path when drawing the sprite(s)?
Duplicate the sprite to be sliced, then apply simple polygons masking the two halves as their respective clipping paths. The function you need is called CGContextClip and a short tutorial can be found here.
Edit: The tutorial lists this example:
CGContextBeginPath (context);
CGContextAddArc (context, w/2, h/2, ((w>h) ? h : w)/2, 0, 2*PI, 0);
CGContextClosePath (context);
CGContextClip (context);
This sets the current path to a circle, then applies the current path as the clipping region.
I would like to know the width of the dark grey frame on either side of the inside picker part and the width of the separators between components. This would be at default settings (eg. what's shown in the .xib). Or, how to find such values programmatically. Thank you
To clarify, I'm looking for what the "suggested minimums" might be for those values. I'm putting my UIPickerView in a popover, and the UIPickerView is wider than 320 px (which is what the .xib gives as the "default"). I would like to know these values so I can add them to the popover's width to make sure it doesn't cut off components on the right side of the UIPickerView.
Well this can be a little tricky since the Picker can have a dynamic number of components. So in order to find the widths you have to take into account the number of components. This can be done like so:
NSInteger n = picker.numberOfComponents;
const CGFloat separatorWidth = 8.0f;
const CGFloat sectionExceedWidth = -2.0f;
CGFloat totalWidth = picker.bounds.size.width;
CGFloat panesWidth = totalWidth - separatorWidth * (n - 1);
for (NSInteger i = 0; i < n; i++) {
CGFloat sectionWidth = [picker rowSizeForComponent:i].width + sectionExceedWidth;
panesWidth -= sectionWidth;
}
CGFloat leftPaneWidth = ceilf(panesWidth * 0.5f);
CGFloat rightPaneWidth = panesWidth - leftPaneWidth;
CGFloat totalHeight = picker.bounds.size.height;
CGRect totalRect = CGRectMake(0, 0, totalWidth, totalHeight);
Where the left and right panes widths are found and the separator size is static.
This was pulled from here.
I've dragged a table view into my storyboard scene, but its height is static and won't adjust based on how many cells it contains unless I manually adjust it in storyboard. How can I get this table to adjust its own height based on the total height of its contents?
You're going to have to do some version of this:
Figure out how many cells will be displayed
Figure out the height for each cell
Do the math and set the height of the UITableView
Before you get mad (since I know that's pretty obvious), let me follow up with this:
If you know for sure how many cells will be displayed all the time (and it's a static value), you can just manually adjust the height in Interface Builder.
If you don't know for sure (which I'm guessing is the case, based on your question), you'll have to do #'s 1 thru 3 above at runtime. Then, because UITableView is a subclass of UIView, you can simply adjust its frame property.
For example:
I've got 7 cells.
Cells 1, 3 and 5 (indices 0, 2 and 4) are 20 pixels tall.
Cells 2, 4, 6 and 7 (indices 1, 3, 5 and 6) are 30 pixels tall.
I need a total display height of 180 pixels (you might actually need to play with this because of separators, etc..)
So, I can just call:
CGFloat newHeight = ... // Whatever you calculate
CGRect currentFrame = [[self MyTableView] frame];
[[self MyTableView] setFrame:CGRectMake(
currentFrame.origin.x,
currentFrame.origin.y,
currentFrame.size.width,
newHeight)
];
If you want to get really fancy, you can animate the change by wrapping it in a [UIView animationWithDuration:] block.
Hope this helps!
Update/Edit
Here's a kludgy way to do it... it works, but I'm sure there's a better way to do it. I do not claim this to be the best/fastest/most efficient way. This is more to show a principle. ;-)
- (CGFloat)totalHeightNeededForTableView:(UITableView *)tv {
CGFloat retVal = 0;
int sectionCount = [self numberOfSectionsInTableView:tv];
int rowCount = 0;
NSIndexPath *path = nil;
for (int i = 0; i < sectionCount; i = i + 1) {
rowCount = [self tableView:tv numberOfRowsInSection:i];
for (int j = 0; j < rowCount; j = j + 1) {
path = [NSIndexPath indexPathForRow:j inSection:i];
retVal = retVal + [self tableView:tv heightForRowAtIndexPath:path];
}
}
return retVal;
}