UITabBar changing each time to a new tab, without scrolling to middle - iphone

I want to create an tabBar with 15 items that can be scroll right-left and stop in the middle and not just scrolling each time all the 5 items (320 every time)
I found a code and changed it for displaying 5 items, but when i'm scroll it, all the tab changed and the next tab with 5 items shown, and so on...
- (id)initWithItems:(NSArray *)items {
self = [super initWithFrame:CGRectMake(0.0, 411.0, 320.0, 49.0)];
if (self) {
self.pagingEnabled = YES;
self.delegate = self;
self.tabBars = [[[NSMutableArray alloc] init] autorelease];
float x = 0.0;
for (double d = 0; d < ceil(items.count / 5.0); d ++) {
UITabBar *tabBar = [[UITabBar alloc] initWithFrame:CGRectMake(x, 0.0, 320.0, 49.0)];
tabBar.delegate = self;
int len = 0;
for (int i = d * 5; i < d * 5 + 5; i ++)
if (i < items.count)
len ++;
tabBar.items = [items objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(d * 5, len)]];
[self addSubview:tabBar];
[self.tabBars addObject:tabBar];
[tabBar release];
x += 320.0;
}
self.contentSize = CGSizeMake(x, 49.0);
}
return self;
}
How can i create a 'rubber effect' so i can be stop on the 7 item for example.

You will have to use a different design than the one you quote. If you look at the code carefully you will notice that for each 5 items a new tab bar is created. Clearly, the functionality that you hope for is not feasible with this setup.
The alternative would be to rewrite this with your own UIScrollView. It could be challenging to get exactly the look and feel of the UITabBar, but perhaps that is not such an important constraint. Rather, you would be free to implement your very own design that harmonizes with the look of your app.

Related

Please help! Creating new objects (from array?) after an object completes task

I am writing an iPhone application and here is the overall synopsis:
An object is on the screen and moves based on accelerometer input - I have that working - it moves to the edge of the screen and doesn't go off = perfect. Now, what I want to happen is as soon as that object hits any of the four screen edges, it should stop and stay put on the screen, and a new object should 'appear' and start moving due to the accelerometer input, so now two objects would be on the screen, but only 1 moving. Eventually there could be 20 objects built up around the edge, but only 1 object will be moving at a time.
So I have now gotten the help I needed to check for edge hits etc, but I am now trying to switch the way I was getting boxes to show up on the screen. I originally was putting images on the screen through the view controller, but now what I want to do is start with one box in the center, when it hits an edge, it should stop and stay, and a new image will appear in the center and start moving due to accel input as described above. So do I just use an array to pull the images from? Do I not even put .png's on the view controller and should I just code it? Here is some of what I have trying to do this through an array:
//In my .h
UIImageView *blocks;
NSString *blockTypes[3];
//In my .m
blockTypes[0] = #"greenBox1.png";
blockTypes[1] = #"greenBox2.png";
blockTypes[2] = #"greenBox3.png";
Thanks in advance for any help! The help so far has been great!
You should't test if newX and newY are equal to 30 and 50. You should test if they are less than 30 and 50 respectively.
Edit:
I would do it like this:
loat newX = [mutableBoxArray lastObject].center.x + (accel.x * 12);
float newY = [mutableBoxArray lastObject].center.y + (accel.y * -12);
if(newX > 30 && newY > 50 && newX < 290 && newY < 430) {
[[mutableBoxArray lastObject] setCenter: CGPointMake(newX, newY)];
} else {
MyBox *myBox = [[MyBox alloc] init];
[mutableBoxArray addObject: myBox];
[myBox release];
}
Edit 2:
Add the following in your interface file
NSMutableArray *mutableBoxArray;
NSArray *imageNamesArray;
Then in your implementation file in the loadView add
mutableBoxArray = [[NSMutableArray alloc] init];
imageNamesArray = [[NSArray alloc] initWithObjects: #"orangeBox1.png",
#"blueBox1.png", #"greenBox1.png", #"pinkBox1.png", nil];
Then change the above method to
static NSInteger imageInt = 0;
loat newX = [mutableBoxArray lastObject].center.x + (accel.x * 12);
float newY = [mutableBoxArray lastObject].center.y + (accel.y * -12);
if(newX > 30 && newY > 50 && newX < 290 && newY < 430) {
[[mutableBoxArray lastObject] setCenter: CGPointMake(newX, newY)];
} else {
if (imageInt < [imageNamesArray count]) {
UIImage *image = [UIImage imageNamed: [imageNamesArray objectAtIndex: imageInt++]];
UIImageView *imageView = [[UIImageView alloc] initWithImage: image];
[imageView setCenter: CGPointMake(100.0f, 100.0f)];
[mutableBoxArray addObject: imageView];
[imageView release];
}
}

how to load the images in my view dynamically in a scroll view in iPhone

i want to display many images like thumbnails in a scroll view and and i want the images displayed dynamically we scrolls down or left like a table view cells
can u please tell how to that...
Thanks
with the following code..when we scroll the scroll view im calling this code and able to display the images dynamically (which r only visible) but the problem is.. while scrolling with scroll bars im getting the two images..vertically and horizontally..its only happening when i scroll.. can any body help me out please..?
int tileSize;
int imgSize;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
tileSize = 255;
imgSize = 247;
}else{
tileSize = 120;
imgSize = 116;
}
CGRect visibleBounds = [songsContainer bounds];
for (UIView *tile in [songsContainer subviews]) {
CGRect scaledTileFrame =[tile frame];
if (! CGRectIntersectsRect(scaledTileFrame, visibleBounds)) {
for(UIView *view in [tile subviews])
[view removeFromSuperview];
[recycledCells addObject:tile];
[tile removeFromSuperview];
}
}
int maxRow =[songsDict count]-1; // this is the maximum possible row
int maxCol = noOfRowsInCell-1; // and the maximum possible column
int firstNeededRow = MAX(0, floorf(visibleBounds.origin.y / tileSize));
int firstNeededCol = MAX(0, floorf(visibleBounds.origin.x / tileSize));
int lastNeededRow = MIN(maxRow, floorf(CGRectGetMaxY(visibleBounds) / tileSize));
int lastNeededCol = MIN(maxCol, floorf(CGRectGetMaxX(visibleBounds) / tileSize));
NSLog(#".........MaxRow-%d,MaxCol-%d,firstNeddedRow-%d,firstNeededcol-%d,lNR-%d,lNC%d",maxRow, maxCol, firstNeededRow,firstNeededCol,lastNeededRow,lastNeededCol);
// iterate through needed rows and columns, adding any tiles that are missing
for (int row = firstNeededRow; row <= lastNeededRow; row++) {
NSMutableArray *tempArray = (NSMutableArray *)[songsDict objectAtIndex:row];
for (int col = firstNeededCol; col <= lastNeededCol ; col++) {
BOOL tileIsMissing = (firstVisibleRow > row || firstVisibleColumn > col ||
lastVisibleRow < row || lastVisibleColumn < col);
if (tileIsMissing) {
UIView *tile = (UIView *)[self dequeueReusableTile];
if (!tile) {
// the scroll view will handle setting the tile's frame, so we don't have to worry about it
tile = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
tile.backgroundColor = [UIColor clearColor];
}
//tile.image = image for row and col;
// set the tile's frame so we insert it at the correct position
CGRect frame = CGRectMake(tileSize * col, tileSize * row, imgSize, imgSize);
tile.frame = frame;
if(col<[tempArray count])
[self addContentForTile:tile:row:col];
else tile.backgroundColor = [UIColor clearColor];
[songsContainer addSubview:tile];
}
}
}
firstVisibleRow = firstNeededRow+1; firstVisibleColumn = firstNeededCol+1;
lastVisibleRow = lastNeededRow; lastVisibleColumn = lastNeededCol;
What you have to do is create your scrollview however you want. You have to decide whether you want a grid layout, or linear layout. In addition, if grid, do you want it locked to the horizontal bounds, locked to vertical, so it scrolls either vertical or horizontal, respectively.
Once you have that sorted out, then what I recommend is taking the architecture similar to how a tableview functions. That is, create individual "cells" that will hold your thumbnails. Once you have these cells, you add them as subviews of your scrollview, at certain offsets (you need to do some math on the x/y planes).
Start with a scroll view and add each image in an UIImageView as a subview to the scrollview at a certain location.
One thing to have in mind is to only hold in memory the images that are currently shown and their immediate neighbours.

What is the best programmatically approach to display images/icons horizontally?

I'm trying to place the icons / images horizontally in a view that is intended to show details. Maximum number of pictures per row is 6 pieces and I want to have a mechanism that manage line breaks or similar automatically. I suspect it is a custom cell that solves this?
I have tried the following code below, but the images above to add another when I reload the view. Furthermore, the cell's height is not adjusted by the view that the code returned.
-(UIImageView *)fillView{
collage = nil;
collage = [[[UIImageView alloc] initWithFrame:CGRectMake(11, 7, 0, 0)] autorelease];
int rowCounter = 0;
int colCounter = 0;
int nrOfPictures = [paymentImages count];
//max 6 images per row
while (nrOfPictures > 0) {
while (colCounter <= 6 && nrOfPictures != 0) {
UIImageView *iv = [[UIImageView alloc] initWithImage:[paymentImages objectAtIndex:nrOfPictures-1]];
CGRect frame = iv.frame;
frame.origin.x = (frame.size.width + 4) * colCounter;
frame.origin.y = (frame.size.height + 4) * rowCounter;
iv.frame = frame;
[collage addSubview:iv];
[iv release];
colCounter++;
nrOfPictures--;
if (colCounter > 6) {
colCounter = 0;
rowCounter++;
}
}
}
return collage;
}
Can someone head me in the right direction?
You might want to check out this project. http://github.com/kirbyt/KTPhotoBrowser

How to implement UIScrollView with 1000+ subviews?

I am struggling with writing portion of an app which should behave like the native iphone photo app. Looked at iphone sdk app development book from Orielly which gave an example code for implementing this so-called page-flicking. The code there first created all subviews and then hide/unhide them. At a given time only 3 subviews are visible rest are hidden. After much effort I got it working with app which at that time had only around 15 pages.
As soon as I added 300 pages, it became clear that there are performance/memory issues with that approach of pre-allocating so many subviews. Then I thought may be for my case I should just allocate 3 subviews and instead of hide/unhide them. May be I should just remove/add subviews at runtime. But can't figure out whether UIScrollView can dynamically update contents. For example, at the start there are 3 frames at different x-offsets ( 0, 320, 640 ) from the screen as understood by UIScrollView. Once user moves to 3rd page how do I make sure I am able to add 4th page and remove 1st page and yet UIScrollView doesn't get confused ?
Hoping there is a standard solution to this kind of problem...can someone guide ?
Following what has been said, you can show thousand of elements using only a limited amount of resources (and yes, it's a bit of a Flyweight pattern indeed). Here's some code that might help you do what you want.
The UntitledViewController class just contains a UIScroll and sets itself as its delegate. We have an NSArray with NSString instances inside as data model (there could be potentially thousands of NSStrings in it), and we want to show each one in a UILabel, using horizontal scrolling. When the user scrolls, we shift the UILabels to put one on the left, another on the right, so that everything is ready for the next scroll event.
Here's the interface, rather straightforward:
#interface UntitledViewController : UIViewController <UIScrollViewDelegate>
{
#private
UIScrollView *_scrollView;
NSArray *_objects;
UILabel *_detailLabel1;
UILabel *_detailLabel2;
UILabel *_detailLabel3;
}
#end
And here's the implementation for that class:
#interface UntitledViewController ()
- (void)replaceHiddenLabels;
- (void)displayLabelsAroundIndex:(NSInteger)index;
#end
#implementation UntitledViewController
- (void)dealloc
{
[_objects release];
[_scrollView release];
[_detailLabel1 release];
[_detailLabel2 release];
[_detailLabel3 release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_objects = [[NSArray alloc] initWithObjects:#"first", #"second", #"third",
#"fourth", #"fifth", #"sixth", #"seventh", #"eight", #"ninth", #"tenth", nil];
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 460.0)];
_scrollView.contentSize = CGSizeMake(320.0 * [_objects count], 460.0);
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = YES;
_scrollView.alwaysBounceHorizontal = YES;
_scrollView.alwaysBounceVertical = NO;
_scrollView.pagingEnabled = YES;
_scrollView.delegate = self;
_detailLabel1 = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 460.0)];
_detailLabel1.textAlignment = UITextAlignmentCenter;
_detailLabel1.font = [UIFont boldSystemFontOfSize:30.0];
_detailLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(320.0, 0.0, 320.0, 460.0)];
_detailLabel2.textAlignment = UITextAlignmentCenter;
_detailLabel2.font = [UIFont boldSystemFontOfSize:30.0];
_detailLabel3 = [[UILabel alloc] initWithFrame:CGRectMake(640.0, 0.0, 320.0, 460.0)];
_detailLabel3.textAlignment = UITextAlignmentCenter;
_detailLabel3.font = [UIFont boldSystemFontOfSize:30.0];
// We are going to show all the contents of the _objects array
// using only these three UILabel instances, making them jump
// right and left, replacing them as required:
[_scrollView addSubview:_detailLabel1];
[_scrollView addSubview:_detailLabel2];
[_scrollView addSubview:_detailLabel3];
[self.view addSubview:_scrollView];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[_scrollView flashScrollIndicators];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self displayLabelsAroundIndex:0];
}
- (void)didReceiveMemoryWarning
{
// Here you could release the data source, but make sure
// you rebuild it in a lazy-loading way as soon as you need it again...
[super didReceiveMemoryWarning];
}
#pragma mark -
#pragma mark UIScrollViewDelegate methods
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
// Do some initialization here, before the scroll view starts moving!
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self replaceHiddenLabels];
}
- (void)displayLabelsAroundIndex:(NSInteger)index
{
NSInteger count = [_objects count];
if (index >= 0 && index < count)
{
NSString *text = [_objects objectAtIndex:index];
_detailLabel1.frame = CGRectMake(320.0 * index, 0.0, 320.0, 460.0);
_detailLabel1.text = text;
[_scrollView scrollRectToVisible:CGRectMake(320.0 * index, 0.0, 320.0, 460.0) animated:NO];
if (index < (count - 1))
{
text = [_objects objectAtIndex:(index + 1)];
_detailLabel2.frame = CGRectMake(320.0 * (index + 1), 0.0, 320.0, 460.0);
_detailLabel2.text = text;
}
if (index > 0)
{
text = [_objects objectAtIndex:(index - 1)];
_detailLabel3.frame = CGRectMake(320.0 * (index - 1), 0.0, 320.0, 460.0);
_detailLabel3.text = text;
}
}
}
- (void)replaceHiddenLabels
{
static const double pageWidth = 320.0;
NSInteger currentIndex = ((_scrollView.contentOffset.x - pageWidth) / pageWidth) + 1;
UILabel *currentLabel = nil;
UILabel *previousLabel = nil;
UILabel *nextLabel = nil;
if (CGRectContainsPoint(_detailLabel1.frame, _scrollView.contentOffset))
{
currentLabel = _detailLabel1;
previousLabel = _detailLabel2;
nextLabel = _detailLabel3;
}
else if (CGRectContainsPoint(_detailLabel2.frame, _scrollView.contentOffset))
{
currentLabel = _detailLabel2;
previousLabel = _detailLabel1;
nextLabel = _detailLabel3;
}
else
{
currentLabel = _detailLabel3;
previousLabel = _detailLabel1;
nextLabel = _detailLabel2;
}
currentLabel.frame = CGRectMake(320.0 * currentIndex, 0.0, 320.0, 460.0);
currentLabel.text = [_objects objectAtIndex:currentIndex];
// Now move the other ones around
// and set them ready for the next scroll
if (currentIndex < [_objects count] - 1)
{
nextLabel.frame = CGRectMake(320.0 * (currentIndex + 1), 0.0, 320.0, 460.0);
nextLabel.text = [_objects objectAtIndex:(currentIndex + 1)];
}
if (currentIndex >= 1)
{
previousLabel.frame = CGRectMake(320.0 * (currentIndex - 1), 0.0, 320.0, 460.0);
previousLabel.text = [_objects objectAtIndex:(currentIndex - 1)];
}
}
#end
Hope this helps!
UIScrollView is just a subclass of UIView so it's possible to add and remove subviews at runtime. Assuming you have fixed width photos (320px) and there are 300 of them, then your main view would be 300 * 320 pixels wide. When creating the scroll view, initialize the frame to be that wide.
So the scroll view's frame would have the dimensions (0, 0) to (96000, 480). Whenever you are adding a subview, you will have to change it's frame so it fits in the correct position in its parent view.
So let's say, we are adding the 4th photo to the scroll view. It's frame would be from (960, 480) to (1280, 480). That is easily to calculate, if you can somehow associate an index with each picture. Then use this to calculate the picture's frame where indexes start at 0:
Top-Left -- (320 * (index - 1), 0)
to
Bottom-Right -- (320 * index, 480)
Removing the first picture/subview should be easy. Keep an array of the 3 subviews currently on-screen. Whenever you are adding a new subview to the screen, also add it to the end of this array, and then remove the first subview in this array from the screen too.
Many thanks to Adrian for his very simple and powerfull code sample.
There was just one issue with this code : when the user made a "double scroll" (I.E. when he did not wait for the animation to stop and rescroll the scrollview again and again).
In this case, the refresh for the position of the 3 subviews is effective only when the "scrollViewDidEndDecelerating" method is invoked, and the result is a delay before the apparition of the subviews on the screen.
This can be easily avoided by adding few lines of code :
in the interface, just add this :
int refPage, currentPage;
in the implementation, initialize refPage and currentPage in the "viewDidLoad" method like this :
refpage = 0;
curentPage = 0;
in the implementation, just add the "scrollViewDidScroll" method, like this :
- (void)scrollViewDidScroll:(UIScrollView *)sender{
int currentPosition = floor(_scrollView.contentOffset.x);
currentPage = MAX(0,floor(currentPosition / 340));
//340 is the width of the scrollview...
if(currentPage != refPage) {
refPage = currentPage;
[self replaceHiddenLabels];
}
}
et voilĂ  !
Now, the subviews are correctly replaced in the correct positions, even if the user never stop the animation and if the "scrollViewDidEndDecelerating" method is never invoked !

Justifying UIVIews on the iPhone: Algorithm Help

I have been messing around with a way to justify align a collection of UIView subclasses within a containing view. I am having a little bit of trouble with the algorithm and was hoping someone could help spot my errors. Here is pseudocode of where I am now:
// 1 see how many items there are
int count = [items count];
// 2 figure out how much white space is left in the containing view
float whitespace = [containingView width] - [items totalWidth];
// 3 Figure out the extra left margin to be applied to items[1] through items[count-1]
float margin = whitespace/(count-1);
// 4 Figure out the size of every subcontainer if it was evenly split
float subcontainerWidth = [containingView width]/count;
// 5 Apply the margin, starting at the second item
for (int i = 1; i < [items count]; i++) {
UIView *item = [items objectAtIndex:i];
[item setLeftMargin:(margin + i*subcontainerWidth)];
}
The items do not appear to be evenly spaced here. Not even close. Where am I going wrong?
Here is a shot of this algorithm in action:
alt text http://grab.by/1Wcg
EDIT: The code above is pseudocode. I added the actual code here but it might not make sense if you are not familiar with the three20 project.
#implementation TTTabStrip (JustifiedBarCategory)
- (CGSize)layoutTabs {
CGSize size = [super layoutTabs];
CGPoint contentOffset = _scrollView.contentOffset;
_scrollView.frame = self.bounds;
_scrollView.contentSize = CGSizeMake(size.width + kTabMargin, self.height);
CGFloat contentWidth = size.width + kTabMargin;
if (contentWidth < _scrollView.size.width) {
// do the justify logic
// see how many items there are
int count = [_tabViews count];
// 2 figure out how much white space is left
float whitespace = _scrollView.size.width - contentWidth;
// 3 increase the margin on those items somehow to reflect. it should be (whitespace) / count-1
float margin = whitespace/(count-1);
// 4 figure out starting point
float itemWidth = (_scrollView.size.width-kTabMargin)/count;
// apply the margin
for (int i = 1; i < [_tabViews count]; i++) {
TTTab *tab = [_tabViews objectAtIndex:i];
[tab setLeft:(margin + i*itemWidth)];
}
} else {
// do the normal, scrollbar logic
_scrollView.contentOffset = contentOffset;
}
return size;
}
#end
I was able to get it to work on my own! I was applying the margin wrong to the elements. The issue is that I needed to apply the margin while considering the previous elements origin and width.
#implementation TTTabStrip (JustifiedBarCategory)
- (CGSize)layoutTabs {
CGSize size = [super layoutTabs];
CGPoint contentOffset = _scrollView.contentOffset;
_scrollView.frame = self.bounds;
_scrollView.contentSize = CGSizeMake(size.width + kTabMargin, self.height);
CGFloat contentWidth = size.width + kTabMargin;
if (contentWidth < _scrollView.size.width) {
// do the justify logic
// see how many items there are
int count = [_tabViews count];
// 2 figure out how much white space is left
float whitespace = _scrollView.size.width - contentWidth;
// 3 increase the margin on those items somehow to reflect. it should be (whitespace) / count-1
float margin = whitespace/(count-1);
// apply the margin
for (int i = 1; i < [_tabViews count]; i++) {
// 4 figure out width from the left edge to the right of the 1st element
float start = [[_tabViews objectAtIndex:i-1] frame].origin.x + [[_tabViews objectAtIndex:i-1] frame].size.width;
TTTab *tab = [_tabViews objectAtIndex:i];
[tab setLeft:(start + margin)];
}
} else {
// do the normal, scrollbar logic
_scrollView.contentOffset = contentOffset;
}
return size;
}
#end
coneybeare, thanks for figuring this out, but your solution doesn't really work as expected. It changes the position of tabs on the bar, but the spacing is not right. This seems to work better for me:
#import "TTTabStrip+Justify.h"
#import <Three20UI/UIViewAdditions.h>
// Width returned by [super layoutTabs] is always 10 px more than sum of tab widths
static CGFloat const kContentWidthPadding = 10;
// Adds fixed margin to left of 1st tab, right of last tab
static CGFloat const kHorizontalMargin = 5;
#implementation TTTabStrip (JustifyCategory)
- (CGSize)layoutTabs {
CGSize size = [(TTTabStrip*)super layoutTabs];
CGPoint contentOffset = _scrollView.contentOffset;
_scrollView.frame = self.bounds;
_scrollView.contentSize = CGSizeMake(size.width, self.height);
CGFloat contentWidth = size.width - kContentWidthPadding + 2 * kHorizontalMargin;
if (contentWidth < _scrollView.size.width) {
// do the justify logic
// see how many items there are
int count = [_tabViews count];
// calculate remaining white space
float whitespace = _scrollView.size.width - contentWidth;
// calculate necessary spacing between tabs
float spacing = whitespace / (count + 1);
// apply the spacing
for (int i = 0; i < count; i++) {
CGFloat lastTabRight = kHorizontalMargin;
if (i > 0) {
TTTab *lastTab = [_tabViews objectAtIndex:i-1];
lastTabRight = [lastTab right];
}
TTTab *tab = [_tabViews objectAtIndex:i];
[tab setLeft:(lastTabRight + spacing)];
}
} else {
// do the normal, scrollbar logic
_scrollView.contentOffset = contentOffset;
}
return size;
}
#end
Morgz, I also got several compiler errors. I needed to import UIViewAdditions.h and tell it that super is a TTTabStrip.