Can I have a UICollectionViewFlowLayout with scrollDirection different from layout direction? - iphone

The UICollectionViewFlowLayout is great, however, I want it tweaked slightly so that the scrollDirection is different from the layout direction. Examples of what I'd like is the springboard home screen or the emoji keyboards where you can swipe left/right, but the cells are laid out left to right, top to bottom (instead of top to bottom, left to right as they are with the UICollectionViewFlowLayout with scrollDirection set to horizontal). Anyone know how I can easily subclass the FlowLayout to make this change? I'm disappointed they don't have a layoutDirection in addition to scrollDirection on the UICollectionViewFlowLayout object :(
#rdelmar I implemented something similar to your solution, and that works (not as elegant as I'd like), but I noticed that occasionally items disappear from the collection unless re-drawn, which is why I didn't accept the answer. Here is what I ended up with:
#interface UICollectionViewPagedFlowLayout : UICollectionViewFlowLayout
#property int width;
#property int height;
#end
#implementation UICollectionViewPagedFlowLayout
#warning MINOR BUG: sometimes symbols disappear
- (CGSize)collectionViewContentSize {
CGSize size = [super collectionViewContentSize];
// make sure it's wide enough to cover all the objects
size.width = self.collectionView.bounds.size.width * [self.collectionView numberOfSections];
return size;
}
- (NSArray*) layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *array = [super layoutAttributesForElementsInRect:rect];
CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
for (UICollectionViewLayoutAttributes* attributes in array) {
if (CGRectIntersectsRect(attributes.frame, rect)) {
// see which section this is in
CGRect configurableRect = UIEdgeInsetsInsetRect(visibleRect, self.sectionInset);
int horizontalIndex = attributes.indexPath.row % self.width;
int verticalIndex = attributes.indexPath.row / self.width;
double xspace = (configurableRect.size.width - self.width * self.itemSize.width) / self.width;
double yspace = (configurableRect.size.height - self.height * self.itemSize.height) / self.height;
attributes.center = CGPointMake(attributes.indexPath.section * visibleRect.size.width + self.sectionInset.left + (self.itemSize.width + xspace) * horizontalIndex + self.itemSize.width / 2, self.sectionInset.top + (self.itemSize.height + yspace) * verticalIndex + self.itemSize.height / 2);
}
}
return array;
}
#end

IIRC, exactly this is demonstrated in the WWDC 2012 sessions on UICollectionView.

I don't know about easily, depends on your definition I guess. I made a project where I had the layout that's close to what you're looking for. This lays out the rows horizontally, left to right, top to bottom. My data source was an array of arrays, where each subarray provided the data for one line.
#import "SimpleHorizontalLayout.h" // subclass of UICollectionViewFlowLayout
#define kItemSize 60
#implementation SimpleHorizontalLayout
-(CGSize)collectionViewContentSize {
NSInteger xSize = [self.collectionView numberOfItemsInSection:0] * (kItemSize + 20); // the 20 is for spacing between cells.
NSInteger ySize = [self.collectionView numberOfSections] * (kItemSize + 20);
return CGSizeMake(xSize, ySize);
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {
UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
attributes.size = CGSizeMake(kItemSize,kItemSize);
NSInteger xValue = kItemSize/2 + path.row * (kItemSize +20) ;
NSInteger yValue = kItemSize + path.section * (kItemSize +20);
attributes.center = CGPointMake(xValue, yValue);
return attributes;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSInteger minRow = (rect.origin.x > 0)? rect.origin.x/(kItemSize +20) : 0; // need to check because bounce gives negative values for x.
NSInteger maxRow = rect.size.width/(kItemSize +20) + minRow;
NSMutableArray* attributes = [NSMutableArray array];
for(NSInteger i=0 ; i < self.collectionView.numberOfSections; i++) {
for (NSInteger j=minRow ; j < maxRow; j++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:j inSection:i];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
}
return attributes;
}

Until Apple adds a simple flag to change the layout orientation on flow layouts, I ended up sub-classing. Some of the stuff is specific to my implementation, but it might help someone else:
#interface UICollectionViewPagedFlowLayout : UICollectionViewFlowLayout
#property int width;
#property int height;
#end
#implementation UICollectionViewPagedFlowLayout
#warning MINOR BUG: sometimes items disappear
- (CGSize)collectionViewContentSize {
CGSize size = [super collectionViewContentSize];
size.width = self.collectionView.bounds.size.width * [self.collectionView numberOfSections];
return size;
}
- (NSArray*) layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *array = [super layoutAttributesForElementsInRect:rect];
CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
for (UICollectionViewLayoutAttributes* attributes in array) {
if (CGRectIntersectsRect(attributes.frame, rect)) {
// see which section this is in
CGRect configurableRect = UIEdgeInsetsInsetRect(visibleRect, self.sectionInset);
int horizontalIndex = attributes.indexPath.row % self.width;
int verticalIndex = attributes.indexPath.row / self.width;
double xspace = (configurableRect.size.width - self.width * self.itemSize.width) / self.width;
double yspace = (configurableRect.size.height - self.height * self.itemSize.height) / self.height;
attributes.center = CGPointMake(attributes.indexPath.section * visibleRect.size.width + self.sectionInset.left + (self.itemSize.width + xspace) * horizontalIndex + self.itemSize.width / 2, self.sectionInset.top + (self.itemSize.height + yspace) * verticalIndex + self.itemSize.height / 2);
}
}
return array;
}
#end

Related

Segmented Controllers

Trying to set an int value using a Segmented Controller. I've seen several tuts on how to change labels, but I need to set an int value.
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize caseCost;
#synthesize dilution;
#synthesize returnMsg;
#synthesize opcValue;
//synthesize opc; < -- Tried
//int opc; <--- tried
- (IBAction)opcView:(id)sender {
if (opcValue.selectedSegmentIndex == 0) {
int opc = 320;
}
if (opcValue.selectedSegmentIndex == 1) {
int opc = 128;
}
if (opcValue.selectedSegmentIndex == 2) {
int opc = 135;
}
if (opcValue.selectedSegmentIndex == 3) {
int opc = 88;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//int opc; <--- tried
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)finishBtn:(id)sender {
//int opc = 320;
float case_cost = ([caseCost.text floatValue]);
float dilutionValue = ([dilution.text floatValue]);
float gpc = (opc / dilutionValue);
float gCost = (case_cost / gpc);
float bCost = (gCost / 4);
float bpc = (gpc * 4);
NSNumberFormatter *formatterCur = [[NSNumberFormatter alloc] init];
NSNumberFormatter *formatterInt = [[NSNumberFormatter alloc] init];
[formatterCur setNumberStyle:NSNumberFormatterCurrencyStyle];
[formatterInt setNumberStyle:NSNumberFormatterDecimalStyle];
NSString *bottlesCost = [formatterCur stringFromNumber:[NSNumber numberWithFloat:bCost]];
NSString *gallons = [formatterInt stringFromNumber:[NSNumber numberWithInt:gpc]];
NSString *gallonsCost = [formatterCur stringFromNumber:[NSNumber numberWithFloat:gCost]];
NSString *bottles = [formatterInt stringFromNumber:[NSNumber numberWithInt:bpc]];
returnMsg.text = [NSString stringWithFormat:#"%# gallons per case at %# per gallon and %# - 32 oz bottles at %# per bottle.", gallons, gallonsCost, bottles, bottlesCost];
}
- (IBAction)opcView:(id)sender {
}
#end
in the line "float gpc = (opc / dilutionValue);" is shows as an unknown value of opc, even though I think it should from the segmented controller. I'm using the segmented controller instead of Radio Buttons i've used in Java. I used the "//int opc=320" to make sure the rest of the code worked.
In each of the if blocks in your method - (IBAction)opcView:(id)sender you are creating a local int variable named opc. So when execution leaves the if block, the local variable disappears. Thus, in - (IBAction)finishBtn:(id)sender there is no variable named opc in scope.
You should declare opc to be a property as well. You will set this property when the segment control changes selection. Later, you can read the property's value in your finish button's handler.
#import "SecondViewController.h"
#interface SecondViewController()
#property (nonatomic) int opc;
#end
#implementation SecondViewController
// this method is wired to the segment control's UIControlEventValueChanged event
- (IBAction)opcView:(id)sender
{
if (opcValue.selectedSegmentIndex == 0) {
self.opc = 320;
}
if (opcValue.selectedSegmentIndex == 1) {
self.opc = 128;
}
if (opcValue.selectedSegmentIndex == 2) {
self.opc = 135;
}
if (opcValue.selectedSegmentIndex == 3) {
self.opc = 88;
}
}
- (IBAction)finishBtn:(id)sender
{
float case_cost = ([caseCost.text floatValue]);
float dilutionValue = ([dilution.text floatValue]);
float gpc = (self.opc / dilutionValue);
// lots more code
}

cut a sprite sheet on cocos2d for animation

I want to cut a sprite in 6 equivalent parts just with one image, a .png file which I found on the web, no with texturepacker, (the image below by example)
I can take other way, but I want to know if I can do that. any one haves idea?
I'll revise my answer if this isn't what you were asking about, but I think you are asking how to 'manually run an animation' using a spritesheet without a plist.
Here's one way. It would be better if you encapsulated this into it's own class, but this can push you in the right direction I think:
ManualAnimationTest.h
#interface ManualAnimationTest : CCLayer
{
CCSprite *animatedSprite;
int x,y;
float animatedSpriteWidth, animatedSpriteHeight;
int animatedSpriteColumns, animatedSpriteRows;
}
#end
ManualAnimationTest.m
#import "ManualAnimationTest.h"
#implementation ManualAnimationTest
-(id) init
{
if( (self=[super init]))
{
CGSize s = [CCDirector sharedDirector].winSize;
x = 0;
y = 0;
animatedSpriteColumns = 3;
animatedSpriteRows = 2;
animatedSpriteWidth = 95.0f;
animatedSpriteHeight = 125.0f;
animatedSprite = [CCSprite spriteWithFile:#"animal_animation.png" rect:CGRectMake(x * animatedSpriteWidth,y * animatedSpriteHeight,animatedSpriteWidth,animatedSpriteHeight)];
[self addChild:animatedSprite];
[animatedSprite setPosition:ccp(s.width / 2.0f, s.height / 2.0f)];
[self schedule:#selector(animateAnimatedSprite) interval:0.5f];
}
return self;
}
-(void) animateAnimatedSprite
{
[animatedSprite setTextureRect:CGRectMake(x * animatedSpriteWidth, y * animatedSpriteHeight, animatedSpriteWidth, animatedSpriteHeight)];
x +=1;
if(x > (animatedSpriteColumns - 1))
{
x = 0;
y +=1;
}
if(y > (animatedSpriteRows - 1))
{
y = 0;
}
}
#end

How to add views between UICollectionViewCells in a UICollectionView?

I'm trying to add UIViews between my UICollectionViewCells in my UICollectionView and I don't know how I could do that. I'm trying to accomplish something like this:
I'll probably need to write a custom UICollectionViewLayout, but I don't really know where to start.
I studied more of how UICollectionViewLayouts work and figured out how to solve it. I have an UICollectionReusableView subclass called OrangeView that will be positioned between my views, than I wrote an UICollectionViewFlowLayout subclass called CategoriesLayout that will deal with my layout.
Sorry for the big block of code, but here is how it looks like:
#implementation CategoriesLayout
- (void)prepareLayout {
// Registers my decoration views.
[self registerClass:[OrangeView class] forDecorationViewOfKind:#"Vertical"];
[self registerClass:[OrangeView class] forDecorationViewOfKind:#"Horizontal"];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
// Prepare some variables.
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:indexPath.row+1 inSection:indexPath.section];
UICollectionViewLayoutAttributes *cellAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
UICollectionViewLayoutAttributes *nextCellAttributes = [self layoutAttributesForItemAtIndexPath:nextIndexPath];
UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];
CGRect baseFrame = cellAttributes.frame;
CGRect nextFrame = nextCellAttributes.frame;
CGFloat strokeWidth = 4;
CGFloat spaceToNextItem = 0;
if (nextFrame.origin.y == baseFrame.origin.y)
spaceToNextItem = (nextFrame.origin.x - baseFrame.origin.x - baseFrame.size.width);
if ([decorationViewKind isEqualToString:#"Vertical"]) {
CGFloat padding = 10;
// Positions the vertical line for this item.
CGFloat x = baseFrame.origin.x + baseFrame.size.width + (spaceToNextItem - strokeWidth)/2;
layoutAttributes.frame = CGRectMake(x,
baseFrame.origin.y + padding,
strokeWidth,
baseFrame.size.height - padding*2);
} else {
// Positions the horizontal line for this item.
layoutAttributes.frame = CGRectMake(baseFrame.origin.x,
baseFrame.origin.y + baseFrame.size.height,
baseFrame.size.width + spaceToNextItem,
strokeWidth);
}
layoutAttributes.zIndex = -1;
return layoutAttributes;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *baseLayoutAttributes = [super layoutAttributesForElementsInRect:rect];
NSMutableArray * layoutAttributes = [baseLayoutAttributes mutableCopy];
for (UICollectionViewLayoutAttributes *thisLayoutItem in baseLayoutAttributes) {
if (thisLayoutItem.representedElementCategory == UICollectionElementCategoryCell) {
// Adds vertical lines when the item isn't the last in a section or in line.
if (!([self indexPathLastInSection:thisLayoutItem.indexPath] ||
[self indexPathLastInLine:thisLayoutItem.indexPath])) {
UICollectionViewLayoutAttributes *newLayoutItem = [self layoutAttributesForDecorationViewOfKind:#"Vertical" atIndexPath:thisLayoutItem.indexPath];
[layoutAttributes addObject:newLayoutItem];
}
// Adds horizontal lines when the item isn't in the last line.
if (![self indexPathInLastLine:thisLayoutItem.indexPath]) {
UICollectionViewLayoutAttributes *newHorizontalLayoutItem = [self layoutAttributesForDecorationViewOfKind:#"Horizontal" atIndexPath:thisLayoutItem.indexPath];
[layoutAttributes addObject:newHorizontalLayoutItem];
}
}
}
return layoutAttributes;
}
#end
I also wrote a category with some methods to check if an index path is the last in a line, in the last line or the last in a section:
#implementation UICollectionViewFlowLayout (Helpers)
- (BOOL)indexPathLastInSection:(NSIndexPath *)indexPath {
NSInteger lastItem = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:indexPath.section] -1;
return lastItem == indexPath.row;
}
- (BOOL)indexPathInLastLine:(NSIndexPath *)indexPath {
NSInteger lastItemRow = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:indexPath.section] -1;
NSIndexPath *lastItem = [NSIndexPath indexPathForItem:lastItemRow inSection:indexPath.section];
UICollectionViewLayoutAttributes *lastItemAttributes = [self layoutAttributesForItemAtIndexPath:lastItem];
UICollectionViewLayoutAttributes *thisItemAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
return lastItemAttributes.frame.origin.y == thisItemAttributes.frame.origin.y;
}
- (BOOL)indexPathLastInLine:(NSIndexPath *)indexPath {
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:indexPath.row+1 inSection:indexPath.section];
UICollectionViewLayoutAttributes *cellAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
UICollectionViewLayoutAttributes *nextCellAttributes = [self layoutAttributesForItemAtIndexPath:nextIndexPath];
return !(cellAttributes.frame.origin.y == nextCellAttributes.frame.origin.y);
}
#end
And this is the final result:
Looks like if your collectionView background was green and contentView white you could get the horizontals with a space between the cells minimumLineSpacing. The vertical gap would be the tricky part, but if you were creative with your contentView and set the minimumInteritemSpacing carefully you could get it.
If you're using sections and the layout is appropriate for it, you might use section headers and footers.
But based on your illustration, it looks like you just need to define the UICollectionViewCell to contain those views. So, where you register your class:
[collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:#"Cell"];
put those border images in the UICollectionView cell subclass (in the above case, "CollectionViewCell"). That seems like the easiest approach.
Here's one I use:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.restorationIdentifier = #"Cell";
self.backgroundColor = [UIColor clearColor];
self.autoresizingMask = UIViewAutoresizingNone;
const CGFloat borderWidth = 3.0f;
UIView *bgView = [[UIView alloc] initWithFrame:frame];
bgView.layer.borderColor = [UIColor blackColor].CGColor;
bgView.layer.borderWidth = borderWidth;
bgView.layer.cornerRadius = 6.0f;
self.selectedBackgroundView = bgView;
}
return self;
}
Wow. That's a lot of code in the other answers just for a separator line between rows..
This is how I solved it. First you'll need to add the line separator inside the cell. Make sure you keep dragging it making it wider than the actual cell width so if your cell width is 60p your separator line will be 70.
#implementation CollectionViewController
{
NSArray *test;
int currentLocInRow;
}
inside cellForItemAtIndexPath:
if((indexPath.row+1) % 4 == 0)
{
cell.clipsToBounds = YES;
}
else
{
cell.clipsToBounds = NO;
}
currentLocInRow++;
if([test count] - indexPath.row+1 < 4 - currentLocInRow)
{
cell.lineSeparator.alpha = 0;
}
else
{
cell.lineSeparator.alpha = 1;
}
if(currentLocInRow==4)currentLocInRow=0;
If you want a separator at the end of the collection view but there's a chance that you won't get 4 cells at the last row you can add a simple Collection Reusable View as Footer.

How can I test if the scroll view is bouncing?

How can I test if the scroll view is bouncing? Is there a notification or something when the bounce ends?
Here is how I detected if the scroll view is bouncing horizontally:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.x < 0) {
NSLog(#"bouncing left");
}
if (scrollView.contentOffset.x > (scrollView.contentSize.width - scrollView.frame.size.width)) {
NSLog(#"bouncing right");
}
}
I've implemented extension for UIScrollView to handle this for vertical and horizontal scrolling. This will work even with non-zero contentInsets and in case when content is not big enough for covering scrollView insets:
Objective-C
#interface UIScrollView (Bouncing)
#property (nonatomic, readonly) BOOL isBouncing;
#property (nonatomic, readonly) BOOL isBouncingTop;
#property (nonatomic, readonly) BOOL isBouncingLeft;
#property (nonatomic, readonly) BOOL isBouncingBottom;
#property (nonatomic, readonly) BOOL isBouncingRight;
#end
#implementation UIScrollView (Bouncing)
- (BOOL)isBouncing
{
return self.isBouncingTop || self.isBouncingLeft || self.isBouncingBottom || self.isBouncingRight;
}
- (BOOL)isBouncingTop
{
return self.contentOffset.y < - self.contentInset.top;
}
- (BOOL)isBouncingLeft
{
return self.contentOffset.x < - self.contentInset.left;
}
- (BOOL)isBouncingBottom
{
BOOL contentFillsScrollEdges = self.contentSize.height + self.contentInset.top + self.contentInset.bottom >= CGRectGetHeight(self.bounds);
return contentFillsScrollEdges && self.contentOffset.y > self.contentSize.height - CGRectGetHeight(self.bounds) + self.contentInset.bottom;
}
- (BOOL)isBouncingRight
{
BOOL contentFillsScrollEdges = self.contentSize.width + self.contentInset.left + self.contentInset.right >= CGRectGetWidth(self.bounds);
return contentFillsScrollEdges && self.contentOffset.x > self.contentSize.width - CGRectGetWidth(self.bounds) + self.contentInset.right;
}
#end
Swift 3.0+
extension UIScrollView {
var isBouncing: Bool {
return isBouncingTop || isBouncingLeft || isBouncingBottom || isBouncingRight
}
var isBouncingTop: Bool {
return contentOffset.y < -contentInset.top
}
var isBouncingLeft: Bool {
return contentOffset.x < -contentInset.left
}
var isBouncingBottom: Bool {
let contentFillsScrollEdges = contentSize.height + contentInset.top + contentInset.bottom >= bounds.height
return contentFillsScrollEdges && contentOffset.y > contentSize.height - bounds.height + contentInset.bottom
}
var isBouncingRight: Bool {
let contentFillsScrollEdges = contentSize.width + contentInset.left + contentInset.right >= bounds.width
return contentFillsScrollEdges && contentOffset.x > contentSize.width - bounds.width + contentInset.right
}
}
For RxSwift you can just map scrollViewDidScroll with isBouncing and filter with distinctUntilChanged.
A minor amendment to Justin's method, allowing for contentInset:
if( scrollView.contentOffset.x < -scrollView.contentInset.left )
{
NSLog( #"bounce left" );
}
if( scrollView.contentOffset.x > scrollView.contentSize.width - scrollView.frame.size.width + scrollView.contentInset.right )
{
NSLog( #"bounce right" );
}
Yes... check out the UIScrollViewDelegate spec, implement the methods including the two below, and set your UIScrollView's delegate accordingly:
// User stops dragging the table view.
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate;
// Control slows to a halt after the user drags it
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
You will probably be most interested in scrollViewDidEndDecelerating. These also work in UITableView which is where I originally found them (UITableView inherits from UIScrollView).
Old question but I just ran into a similar issue and wanted to add that it is a good idea to also check that the scroll views content is larger than the scroll view's frame:
+ (BOOL) isScrollViewBouncing:(UIScrollView *)scrollView
{
return scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.frame.size.height
&& scrollView.contentSize.height > scrollView.frame.size.height;
}
This now makes sure the scroll view is large enough to be in a scroll-bouncing state, so that if the scroll view is small it does NOT always evaluate to true.
Cheers
For those of you who would be able to "bounce" downwards in a scrollview in order to update contents of the view. (And the event is only fired once.)
- (void)scrollViewDidEndDragging:(UIScrollView *)aScrollView
willDecelerate:(BOOL)decelerate{
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
//Distance in points before update-event occur
float reload_distance = 50;
//
if(y > h + reload_distance) {
NSLog(#"load more rows");
}
}
Using Glavid's answer, I added bouncing to bottom also, and added as a category
#import "UIScrollView+Additions.h"
#implementation UIScrollView (Additions)
- (BOOL)isBouncing
{
BOOL isBouncingBottom = self.contentOffset.y >= self.contentSize.height - self.frame.size.height
&& self.contentSize.height >= self.frame.size.height;
BOOL isBouncingTop = self.contentOffset.y <= 0;
BOOL isBouncing = isBouncingBottom || isBouncingTop;
return isBouncing;
}
#end

iphone code - CGPoint question

i have 10 moving objects (UIImageView),
is there a better way to write this code?
- (void) jumpOnTimer {
jumpBall1.center = CGPointMake(jumpBall1.center.x+pos1.x,jumpBall1.center.y+pos1.y);
if(jumpBall1.center.x > 60 || jumpBall1.center.x < 0)
pos1.x = -pos1.x;
if(jumpBall1.center.y > 211 || jumpBall1.center.y < 82)
pos1.y = -pos1.y;
jumpBall2.center = CGPointMake(jumpBall2.center.x+pos2.x,jumpBall2.center.y+pos2.y);
if(jumpBall2.center.x > 40 || jumpBall2.center.x < 0)
pos2.x = -pos2.x;
if(jumpBall2.center.y > 206 || jumpBall2.center.y < 82)
pos2.y = -pos2.y;
and so on...
Judging by that code snippet, it looks like you have a single controller which "owns" the ten balls, and you want the balls to bounce around according to a set of rules that are unique to each ball. A more object-oriented approach would be as follows:
#interface JumpBallClass
{
CGPoint center;
CGPoint speed;
CGPoint lowerLimit;
CGPoint upperLimit;
}
#property (assign) CGPoint lowerLimit;
#property (assign) CGPoint upperLimit;
- (void)update;
#end
#implementation JumpBallClass
- (void)update
{
center.x += speed.x;
center.y += speed.y;
if (center.x > upperLimit.x || center.x < lowerLimit.x)
{ speed.x = -speed.x; }
if (center.y > upperLimit.y || center.y < lowerLimit.y)
{ speed.y = -speed.y; }
}
#end
This setup would allow you to configure all of the balls once, by setting their upper and lower limits:
[jumpBall1 setUpperLimit:CGPointMake(60, 211)];
[jumpBall1 setLowerLimit:CGPointMake(0, 82)];
...
And then simply calling update on each ball in your timer method:
- (void) jumpOnTimer {
[jumpBall1 update];
[jumpBall2 update];
...
}
You can simplify this even further by storing all of the balls in an NSArray:
NSArray * balls = [NSArray arrayWithObjects:jumpBall1, jumpBall2, ..., nil];
And then calling makeObjectsPerformSelector:
[balls makeObjectsPerformSelector:#selector(update)];
You can make an array of jumpBalls and then loop through each and do the code for that. You can do something like this:
JumpBallClass *myjumpballs[10];
for (i=0; i<10; i++) {
myjumpballs[i].center = CGPointMake(myjumpballs[i].center.x+pos1.x,myjumpballs[i].center.y+pos1.y);
if(myjumpballs[i].center.x > 60 || myjumpballs[i].center.x < 0)
pos1.x = -pos1.x;
if(myjumpballs[i].center.y > 211 || myjumpballs[i].center.y < 82)
pos1.y = -pos1.y;
}
Looks like you're trying to manually animate. Have a look at using UIView animations instead