UIScrollView - selection like UIPickerView - iphone

I have two little problems with my UIScrollView.
I managed to create scroll view, which is similar to the picker view except that it's horizontal.
The problem n.1 is - How do I get the number which the user tapped?
The problem n.2 is - How do I make the scrollview to go round and round - never-ending?
A question - Is it possible to make some "selection indicator"?
Here is my code:
numberArray = [NSArray arrayWithObjects:#"1", #"2", #"3", #"4", #"5",
#"6", #"7", #"8", #"9", #"10", #"11", #"12", #"13", #"14", #"15",
#"16", #"17", #"18", #"19", #"20", #"21", #"22", #"23", #"24", #"25",
#"26", #"27", #"28", #"29", #"30", #"31", #"32", #"33", #"34", #"35",
#"36", #"37", #"38", #"39", #"40", #"41", #"42", #"43", #"44", #"45",
#"46", #"47", #"48", #"49", #"50", #"51", #"52", #"53", #"54", #"55",
#"56", #"57", #"58", #"59", #"60", #"61", #"62", #"63", #"64", #"65",
#"66", #"67", #"68", #"69", #"70", #"71", #"72", #"73", #"74", #"75",
#"76", #"77", #"78", #"79", #"80", #"81", #"82", #"83", #"84", #"85",
#"86", #"87", #"88", #"89", #"90", #"91", #"92", #"93", #"94", #"95",
#"96", #"97", #"98", #"99", #"100", nil];
masterDividerView = [[UIView alloc] initWithFrame:CGRectMake(0, 480, 320, 44)];
morePeople = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 53)];
morePeople.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
morePeople.delegate = self;
[morePeople setBackgroundColor:[UIColor whiteColor]];
[morePeople setCanCancelContentTouches:NO];
morePeople.showsHorizontalScrollIndicator = NO;
morePeople.showsVerticalScrollIndicator = NO;
morePeople.clipsToBounds = NO;
morePeople.scrollEnabled = YES;
morePeople.pagingEnabled = NO;
NSUInteger nimages = 0;
NSInteger tot=0;
CGFloat cx = 0;
for (; ; nimages++) {
NSString *label = [numberArray objectAtIndex:nimages];
if (tot==99) {
break;
}
if (99==nimages) {
nimages=0;
}
UILabel *labelView = [[UILabel alloc] init];
labelView.text = label;
labelView.lineBreakMode = UILineBreakModeWordWrap;
labelView.numberOfLines = 0;
[labelView sizeToFit];
labelView.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
CGRect rect = labelView.frame;
rect.size.height = 53;
rect.size.width = 40;
rect.origin.x = cx;
rect.origin.y = 0;
labelView.frame = rect;
[morePeople addSubview:labelView];
cx += labelView.frame.size.width+5;
tot++;
}
self.pageControl.numberOfPages = nimages;
[morePeople setContentSize:CGSizeMake(cx, [morePeople bounds].size.height)];
[masterDividerView addSubview:morePeople];
[self.view addSubview:masterDividerView];
If anybody knows a good solution to this, I would be very happy!!!! :))

Your question is too wide to make a detailed answer, but yes, everything of what you're talking about can be solved in a quite easy way.
The problem n.1 is - How do I get the number which the user tapped?
If you mean to know what element is chosen by your UIScrollView at the moment, then it's not a problem: UIScrollView always knows its position (contentOffset), which gives you the easy possibility to define, which object is chosen (which object you're working with) now.
If you mean to know when the user tapped by his finger one of the elements of your "picker view" (UIScrollView), then I would say that the answer to this depends on how you actually represent your data (like if you see one or several elements of your scrollView at a time on your screen). But in any case you can easily solve this by using UITapGestureRecognizer. Smth like:
// add gesture recognizers to the scroll view
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
[singleTap setNumberOfTapsRequired:1];
[self.yourScrollView addGestureRecognizer:singleTap];
[singleTap release];
And then to scroll it programmatically in your selector you do smth like:
- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer
{
CGPoint pointOfTouch = [gestureRecognizer locationInView:self.view];
if ( (pointOfTouch.x > self.rightArrowMin) && (pointOfTouch.x < self.rightArrowMax) ) {
[scrollView setContentOffset:CGPointMake(lastOffset + self.lengthOfLabel, 0) animated:YES];
} else if ( (pointOfTouch.x > self.leftArrowMin) && (pointOfTouch.x < self.leftArrowMax) ) {
[scrollView setContentOffset:CGPointMake(lastOffset - self.lengthOfLabel, 0) animated:YES];
}
}
The problem n.2 is - How do I make the scrollview to go round and round - never-ending?
It can be solved if you know the algorithm of calculating the next/previous or random element in the sequence of elements of your scrollView.
Basically, I solved this same thing for myself in one of my projects (free app "IJCAI11" in the appStore, there you can see at the work of a datePicker in the details of any chosen conference's day: there is applied the algorithm of infinite scrolling view, the limits I applied later only from design point of view).
The scheme I use there is simple, though perhaps a bit weird (before reading ahead, note that in my case I see only one element of scrollView on the screen, when the scrollView is in nonscrolling state):
I create the UIScrollView with 3 (in case you see more than 1 element at a time this can be like 5, 7, ..., 2N+1 according to your very case) labels;
Make the second (central) label active.
Assign proper value to all 3 (2N+1) labels. On this step your second (central) label will contain data of (will show) the default value.
When user scrolls one step left/right (to the position M_pos), you do it in a standard way, since your neighboring to central labels contain correct data (M).
Then you take this new active value (M) and apply it for your central label (this is gonna be behind the scene, therefore the user wont see this on the screen), then you recalculate ALL the other labels properly ( ..., M-1, M, M+1, ...), including your M_pos position(!).
With "animated:NO" mode you shift your scrollView to your central position. In this case user doesn't see anything, when in fact his scrollView was moved from position M_pos to the central position. All neighboring labels are already recalculated and you are ready to repeat the point 4 any number of times you wish, making a strong feeling for user, that he works with the scrollView with a huge number of elements (whereas in practice your just work with 3 (2N+1) labels, changing their content after every movement).
A question - Is it possible to make some "selection indicator"?
If I understand this question correctly, then just consider the View/Subview thing and in particular the function addSubview of View class ;)
Hope this helps!

I'm not sure if I understood your question right, but did you already think of using a NSTimer to repeat events with a certain speed or during a certain period of time? (Pointing at your "round and round thing".)

Related

Slideshow using UIScrollView and AutoLayout

I wanted to create a simple slideshow of images using an UIScrollView and I noticed that, using the new AutoLayout mode, the operation became way more complicated compared to iOS5. I wasn't able to find any SIMPLE and SHORT example/tutorial for accomplishing it. There was a lot of material about "pure" and "hybrid" approaches,but honestly nothing really worked for me. Maybe the material is not clear enough, maybe I'm not good enough..who knows. Anyway, I thought it could be useful sharing my finding and the consequent snippet of code which is currently doing the job for me. Here we go:
- (void)setupSlideshow {
NSInteger nPhotos = [self.profilePhotos count];
UIScrollView *scrollView;
UIImageView *imageView;
NSDictionary *viewsDictionary;
// Create the scroll view and the image view.
scrollView = self.slideShow;
CGFloat sWidth = scrollView.frame.size.width;
CGFloat sHeight = scrollView.frame.size.height;
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, nPhotos * sWidth, sHeight)];
scrollView.pagingEnabled = YES;
CGFloat cx = 0;
for (int i = 0; i < nPhotos; i++) {
imageView = [[UIImageView alloc] init];
// Add an image to the image view.
[imageView setImage:[UIImage imageNamed:self.profilePhotos[i]]];
CGRect imFrame = imageView.frame;
imFrame.origin.x = cx;
imFrame.origin.y = 0;
imFrame.size.width = sWidth;
imFrame.size.height = sHeight;
imageView.frame = imFrame;
[container addSubview:imageView];
cx += sWidth;
}
container.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:container];
// Set the constraints for the scroll view and the image view.
viewsDictionary = NSDictionaryOfVariableBindings(scrollView, container);
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-0-[container(%f)]-0-|",cx]
options:0
metrics: 0
views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[container]-0-|" options:0 metrics: 0 views:viewsDictionary]];
}
The scrollView has been instantiated in the StoryBoard.
As you can notice, I adopted an approach based on a UIView container for the pictures I want to use which has been added to a scrollview. I have then added some constraints using the Visual Constraints Format notation. I tried to match the width of the container to the scroller's one using this:
[NSString stringWithFormat:#"H:|-0-[container(==scrollView)]-0-|"]
but it doesn't work (to be precise it doesn't scroll but sticks to the first image), so I opted for a dynamic approach using a simple string format.
As I said, it does the job, but I'm sure I can make it way more elegant. Besides that, is it possible to fix the problem relative the dynamic width?
I hope to receive some useful feedback, in order to refine this example. I will then setup a GitHub for making it publicly available for all of those who spent 3 days banging their head against a wall...like I did.

set postion of no of the UITextviews in iphone

I have a UITextView added on my UIView programmatically. The textview added is not editable, it is just to display some data. The data displayed in the textview is getting from array.The no of the textviews are same as texts which I Stored in Array.Textview is displaying from start to end one by one.I need to set the postion of textview one by one means first textview come and set at some postion,second will be set as just before first textview,third will be just before on second and so on.. Note: Something like using Animation. suppose one textview come from top and it stops at Y postion at 250 then next textview will set at just before that..it cant come upto 250 of Y postion. and so on. I have no clue how to do this. Please give me some ideas.
Don't Consider the array and the UITextView data.... its fot temporary..
NSMutableArray *array = [[NSMutableArray alloc]initWithObjects:#"One",#"Two",#"three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine",#"Ten", nil];
for(int i=0;i<[array count];i++) {
UITextView *textView = [[UITextView alloc]init];
[textView setFrame:CGRectMake(20, 20*i+height, 200, height)];
[textView setBackgroundColor:[UIColor redColor]];
[textView setText:[array objectAtIndex:i]];
[self.view addSubview:textView];
}
you want to add textviews programmatically in your view based on the array,if you are adding textviews to your view then there is a problem(if u are having more number of textviews then you must take scroll view ) that's why better to take scroll view and add scroll view to your view after that you will add textviews to your scroll view ,
now take constant x-coordinate value ,width and height only change y-coordinate value and you must give the content size to scrollview .
for(int i=1;i<[array length];i++){
text_desc = [[UITextView alloc] initWithFrame:CGRectMake(63,64*i,370,60)];
text_desc.font = [UIFont fontWithName:#"Helvetica" size:13.0];
text_desc.editable=NO;
text_desc.tag=1;
}
and then set srollview content size,
scroll.contentSize = CGSizeMake(0,total*87);
//loop over the substrings of type Array to add textfields at run time
for (int i = 0; i < [substrings count]; i++)
{
CGRect frame = CGrectMake(0, i * 40, 100, 30);
UITextField * txtDynamic = [[UITextField alloc] initWithFrame: frame];
txtDynamic.text = [substrings objectAtIndex:i];
//add as subview
[view addSubview:txtDynamic];
//if you are not using ARC release the txtDynamic
}
Note
If number of UITextField more i.e it goes out of screen, then Add UITextField to UIScrollView and you can make its Size and content size dynamic.
Hope this will give you some clue.
hey mate then take one scrollview and in it just set all textview with its ContentSize like bellow
here i give an example ,
Note: this is just example here take one UIScrollView and give name scrview and after add your all this UITextField in it.
you add this code after you add data in textfield
txt1.frame = CGRectMake(txt1.frame.origin.x, txt1.frame.origin.y, txt1.frame.size.width, txt1.contentSize.height);
float txtscreen1 = txt1.frame.origin.y + txt1.contentSize.height + 10;
txt2.frame = CGRectMake(txt2.frame.origin.x, txtscreen1, txt2.frame.size.width, txt2.contentSize.height);
float txtscreen2 = txt2.frame.origin.y + txt2.contentSize.height + 10;
txt3.frame = CGRectMake(txt3.frame.origin.x, txtscreen2, txt3.frame.size.width, txt3.contentSize.height);
//.....and so on to 10
float txtscreen10 = txt3.frame.origin.y + txt3.contentSize.height + 10;
txt10.frame = CGRectMake(txt10.frame.origin.x, txtscreen10, txt10.frame.size.width, txt10.contentSize.height);
float scrollviewscreen = txtscreen10.frame.origin.y + txtscreen10.frame.size.height + 20;
scrview.contentSize=CGSizeMake(320, scrollviewscreen);//take scrollview
i hope this help you....
:)

iOS : How to do proper paging in UIScrollView?

I've a UIScrollView of size (320,160). I'm adding some UIImageView into it, which are of size (213,160). The first UImageView starting from 54 (x) and so on, I've added a space of 5.0 in between each UIImageView. I've also enabled pagingEnable in IB & in coding. What my problem is its not properly working as per its property! When I scroll it should show me UIImageViews in each single page instead it showing me something like see screenshot I want output something like this see output screenshot
Where I'm doing wrong? I also having function of< (previous) & > (next) there to show images. I've asked one question yesterday which was I accepted however my requirement is little change and it won't become my solution. see my question.
Is there any special property that I've to set, or some logic I should implement? All examples I've checked and tried but I find that my requirement is some special. Point me! Thanks.
EDITED:
- (void) setImages
{
CGFloat contentOffset = 0.0f;
for (int i=0; i<[arrImgUrls count]; i++)
{
CGRect imageViewFrame = CGRectMake(contentOffset, 0.0f, 213, scrollImages.frame.size.height);
AsyncImageView *asyncImageView = [[AsyncImageView alloc] initWithFrame:imageViewFrame];
[asyncImageView.layer setMasksToBounds:YES];
NSString *urlImage = [arrImgUrls objectAtIndex:i];
NSURL *url = [NSURL URLWithString:urlImage];
[asyncImageView loadImageFromURL:url];
[scrollImages addSubview:asyncImageView];
contentOffset += asyncImageView.frame.size.width+increment;
[asyncImageView release];
}
scrollImages.contentSize = CGSizeMake([arrImgUrls count] * scrollImages.frame.size.width, scrollImages.frame.size.height);
scrollImages.pagingEnabled = YES;
scrollImages.clipsToBounds = NO;
}
-(IBAction)prevImage:(id)sender
{
_currentImage--;
[btnNext setEnabled:YES];
if (_currentImage==0)
{
[btnPrev setEnabled:NO];
[scrollImages setContentOffset:CGPointMake((_currentImage*imageWidth), 0) animated:YES];
return;
}
NSLog(#"previous:mult %d inc %d current %d",_currentImage*imageWidth,increment*_currentImage,_currentImage);
int nextImage=_currentImage+2;
[scrollImages setContentOffset:CGPointMake((((_currentImage*imageWidth)-(increment*_currentImage)))+(nextImage*increment), 0) animated:YES];
}
-(IBAction)nextImage:(id)sender
{
_currentImage++;
NSLog(#"next:mult %d inc %d current %d",_currentImage*imageWidth,increment*_currentImage,_currentImage);
[scrollImages setContentOffset:CGPointMake((_currentImage*imageWidth)+(increment*_currentImage), 0) animated:YES];
[btnPrev setEnabled:YES];
if (_imageCount-1 == _currentImage)
{
[btnNext setEnabled:NO];
}
}
Paging scroll views alway page multiples of their frame size. So in your example paging is always +320.
This behavior is good if you have content portions matching the frame of the scroll view.
What you have to do, is giving your scroll view a width of 213 and set its clipsToBounds property to NO.
After that your scroll view pages exactly how you want and you see what's left and right outside the frame.
Additionally you have to do a trick to make this left and right area delegate touches to the scroll view.
It's greatly explained in this answer.
You are forgetting to set scrollview content size.
make sure that you have set the content size to fit N number of images.
if you want to scrollview to scroll for 10 images with an image on each page
set scrollView contentSize as
[scrollView setContentSize:CGSizeMake(320 * 10,200)];

Use a pool to spawn images

Hi every one here is my code:
-(void) createNewImage {
[imageView setCenter:[self randomPointSquare]];
[[self view] addSubview:imageView];
[views addObject:imageView];
[imageView release];
ix=imageView.center.x;
iy=imageView.center.y;
[XArray addObject:[NSNumber numberWithFloat:(240 - ix)/diviseurVitesse]];
[YArray addObject:[NSNumber numberWithFloat:(160 - iy)/diviseurVitesse]];
}
-(void)moveTheImage{
for (NSUInteger i = 0; i < [views count]; i++) {
imageView = [views objectAtIndex:i];
X = [[XArray objectAtIndex:i] floatValue];
Y = [[YArray objectAtIndex:i] floatValue];
imageView.center=CGPointMake(imageView.center.x + X, imageView.center.y + Y);
}
With this code, image are created and move to the center of the screen but after some seconds the performance decrease and come to 30fps. I would like to keep fps at 60 but I don't know how to proceed.Some people say that I can use pool but it's hard for me to use it because I don't really know how to use it :/ sorry for my english I'm french :/
Your problem is most likely here - [views addObject:imageView];. Adding object to a persistent dataset like NSMutableArray retains the object, so memory consumption actually grows. Try manipulating your images in [self.view subviews] without storing them in array.
EDIT: Your code should look something like this
- (void)createAndMove {
UIImageView *imageView = [UIImageView alloc] initWithImage:image];
[imageView setCenter:[self randomPointSquare]];
[self.view addSubview:imageView];
[UIView animateWithDuration:5 animations:^{
imageView.center = self.view.center;
}];
}
EDIT 2:
- (void)moveImages {
for (UIImageView *img in [self.view subviews]) {
[UIView animateWithDuration:5 animations:^{
img.center = self.view.center;
}];
}
}
What happens when your object reaches the centre of the screen? If it just sits there and the other images overlap it, then you could then reset it to be at the start position rather than creating a new image. If you are moving them to the centre and then leaving them there, pretty soon you will have many many views and that will just kill the performance.
So, imagine you have 3 views. View 1 is created and appears at edge of screen and moves towards centre. When that is half-way to the centre, view 2 is created and appears at the edge of the screen and starts to move towards the centre. When view 2 is half-way to the centre, view 1 is at the centre and view 3 is created and appears at the edge of the screen. Now, when view 2 reaches the centre, view 1 is reset to be at the edge of the screen (it will be covered by view 2 so it won't be noticed). Then when view 3 reaches the centre, it will cover view 2, so that can be reset to the edge of the screen.
Hope this makes sense - it's a bit difficult without drawing pictures or knowing exactly what you are moving around on the screen, etc.

iOS: How To swipe through an image with pagination on

Basically, I want to have an app with only one view that has an image on it. I want to be able to swipe left or right and have the first image go out of the view and the second image to come in. The images are the same and I want it to look like they are connected (like scrolling down a rope where the pattern just repeats, but it looks like a constant scroll). I need it to be able to change the image or restart after a series of swipes. I know that I need to turn pagination ON in the UIScrollView, but I am new to iOS and am having trouble.
Ultimately, I want to have the iPhone vibrate every so-and-so swipes (and restart the pattern).
I'm sure that there are a lot of ways to do this (i.e. a TableView) so feel free to just point me in the direction of some references if the answer is tedious to explain.
Thanks!
FOLLOW UP:
I found an Apple example that did very nearly what I wanted to do. I made a lot of adjustments to it, but I'm banging my head against a wall trying to get the images to cycle. Here is what I think is the offending code, but I'm not sure what the solution is, as the ScrollView is functional, it just doesn't reset the center to the current view. Any ideas?
- (void)layoutScrollImages
{
UIImageView *view = nil;
NSArray *subviews = [scrollView1 subviews];
// reposition all image subviews in a horizontal serial fashion
CGFloat curXLoc = 0;
for (view in subviews)
{
if ([view isKindOfClass:[UIImageView class]] && view.tag > 0)
{
CGRect frame = view.frame;
frame.origin = CGPointMake(curXLoc, 0);
view.frame = frame;
curXLoc += (kScrollObjWidth);
}
}
// set the content size so it can be scrollable
[scrollView1 setContentSize:CGSizeMake((kNumImages * kScrollObjWidth), [scrollView1 bounds].size.height)];
}
I'd just use a UIScrollView. Set the contentWidth to be 3 times the width/height of the view (for 3 pages) and set the contentOffset to be the center 'page' (view.bounds.size.width or view.bounds.size.height depending on whether you're scrolling horizontally/vertically respectively) . You'll need to setup a delegate for the UIScrollView (probably the view controller) and implement - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView. This will be called when the scroll view has finished decelerating. Once it has finished decelerating, reset the contentOffset back to the center view. This should give the impression of an infinite scroll. You can also set a counter to increment in the scrollViewDidEndDecelerating method to increment the counter or initiate the vibration.
You shouldn't need to keep repositioning the images. Just set the images once in the scrollView:
//Horizontal arrangement
UIImage *image = [UIImage imageNamed:#"nameOfImage.png"];
UIImageView *imageView1 = [[UIImageView alloc] initWithImage:image];
UIImageView *imageView2 = [[UIImageView alloc] initWithImage:image];
UIImageView *imageView3 = [[UIImageView alloc] initWithImage:image];
NSArray *imageViews = [NSArray arrayWithObjects:imageView1, imageView2, imageView3];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[self.view addSubview: scrollView]; //This code assumes it's in a UIViewController
CGRect cRect = scrollView.bounds;
UIImageView *cView;
for (int i = 0; i < imageViews.count; i++){
cView = [imageViews objectAtIndex:i];
cView.frame = cRect;
[scrollView addSubview:cView];
cRect.origin.x += cRect.size.width;
}
scrollView.contentSize = CGSizeMake(cRect.origin.x, scrollView.bounds.size.height);
scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0); //should be the center page in a 3 page setup
So the images are setup, you don't need to mess with them anymore. Just reset the contentOffset when the scroll views stops (note: you need to make sure you're the delegate of the scroll view or you'll not receive the message when the scroll view stops):
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0);
}
Please forgive any typos. I wrote it out by hand.
Look on cocoacontrols.com for a custom photo album view. As for the vibration, this code snippet vibrates the phone (make sure you link to and #import <AudioToolbox/AudioToolbox.h>):
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);