I'm trying to make a paging UIScrollView display multiple pages at the same time, in effect, making the page size smaller than the UIScrollview's bounds. I've been googling for hours, but nobody seems to have a good solution.
I'm able to get the right effect visually by sizing the UIScrollview to the size I want one page to be, turning off subview clipping, and placing it inside a container that passes all of its taps to the UIScrollview. The problem with this is that with Y pages visible, it lets you scroll the last page all the way to the left, leaving Y-1 empty pages after the last page. Anyone know a way around this, or another approach to the problem?
I think you need something like the previews in the safari mobile browser. Here is a website with some sample code.
Preview Sample Code
For the right end, try reducing the width of the scroll view's contentSize property enough that the scroll view will stop paging before it gets to the last page.
For the left end, reduce the frame.origin.x property of each page by the same amount. The first few pages will have a negative x position within the scroll view.
Essentially, makeing the scroll view think that it's content is only pages 2 through second to last.
For example:
// scrollView has been initialized and added to the view hierarchy according to the link in #richard's answer:
// http://blog.proculo.de/archives/180-Paging-enabled-UIScrollView-With-Previews.html
CGFloat pageNum=10;
CGFloat pageWidth=scrollView.frame.size.width;
CGFloat pageHeight=scrollView.frame.size.height;
CGFloat visibleWidth=320;
// Set scrollView contentSize and create pages:
CGFloat leftShift=floor(((visibleWidth-pageWidth)/2)/pageWidth)*pageWidth;
CGFloat contentSizeReduction=2*leftShift;
scrollView.contentSize = CGSizeMake(pageNum*pageWidth-contentSizeReduction,pageHeight);
for(int i=0; i<pageNum; i++) {
CGRect pageFrame = CGRectMake(i*pageWidth-leftShift, 0, pageWidth, pageHeight);
UIView *pageView = [[[UIView alloc] initWithFrame:pageFrame] autorelease];
// Initialize the page view for the current index here
[scrollView addSubview:pageView];
}
Forgive any typos in the code. I haven't tried this yet myself.
Let me know if it works.
Related
I am trying to generate a scroll controller in a window with the UIScrollView class, which will contain numerous UIButtons, placed vertically. I set the size of the scroll view equal to the current view controller's root view, so that the scroll view covers the entire visible window. Then I generate the UIButtons I am going to add to the scroll view: I add each UIButton just in the below of the previous UIButton and I add the height of the current UIButton to a variable called "totalContentHeight". Finally, I set the height of the contentSize of the scroll view to this value, in the following line of code:
self.scrollViewForNewsButtons.contentSize = CGSizeMake(self.view.frame.size.width, totalContentHeight);
totalContentHeight is equal to numOfButtons*eachButtonsHeight after I add all the buttons to the scroll view.
The problem is, in the simulator, when I run the app and scroll until the end of the last button and release the mouse, the last two buttons bounces back such that they lie outside of the visible window. It is somewhat hard to express with mere words, so here are the images:
1)This is what I get when I scrolled until the end of the content and held the content at the last possible position it could be pushed:
2)This is what I get after I released the mouse and the scroll view bounced back to its final position:
As you can see, the last two buttons are drawn outside of the visible area. It is like the scroll view's area covers the whole window plus the button area of the IPhone. I could not find a reasonable explanation for this. Am I setting the area size wrong or am I missing something else?
just set content size with calculation with your total button and its height...For Ex..
float yheight = totalButton * yourButtonHeight;
[yourScrollView setContentSize:CGSizeMake(320, yheight + 44)];
try this code...
And if you set the scrollview frame size the same as
self.view.bounds
I'm now trying to create my own Text panel (with code highlighting and so on).
In fact everything is ready except one thing - scrolling
What I've done:
created UIScrollView and inserted my own view as a subview using Interface builder.
in ViewController viewDidLoad method I've pointed initial textfield size to be the UIScrollView content size.
And when user writes too much into my text field. I call delegate (which is ViewController) to change size.
To do that I simply write
[scrollView setContentSize:newSize];
And what I get is increasing of the scroll view but not my text field - just blank lines appears. I think I need to set my view's size too but I can't figure out how...
And a second part of my question:
Every time I call [textField setNeedsDisplay] it calls drawRect: method with rectangle which is equal to the whole size of my view (which is much bigger than it's visible part). What will it look like if I try to use my view to modify file which size is more than 3 mb? How can I force it to redraw only visible part?
Thanks for your attention.
Part 1:
The scroll view's content size is not actually related to the size or position of the views it contains. If you want to change the size of the content view as well as the scroll view's content, you need to call two different methods.
CGSize newSize;
UIScrollView *scrollView;
// assume self is the content view
CGRect newFrame = (CGRect){CGPointZero,newSize}; // Assuming you want to start at the top-left corner of the scroll view. Change CGPointZero as appropriate
[scrollView setContentSize:newSize]; // Change scroll view's content size
[self setFrame:newFrame]; // Change views actual size
Part 2:
setNeedsDisplay marks the entire view as needing display. To cause it to display only the visible part, you need to use setNeedsDisplayInRect:visibleRect.
Assuming the view is at the top-left corner (its frame's origin is 0) and the scroll view does not allow zooming, the visible rect can be found using the scroll view's content offset and bounds size.
CGRect visibleRect;
visibleRect.origin = [scrollView contentOffset];
visibleRect.size = [scrollView bounds].size;
[self setNeedsDisplayInRect:visibleRect];
You could also choose to draw a part of the visible rect if only part changes.
It's simpler than you think, for example you have an UITextView called "textDesc" inside your UIScrollView, and you want to know the size of content, so next step will looks like that:
int contentHeight = textDesc.frame.size.height + textDesc.frame.origin.y;
After calculating the size of textDesc, just set it:
[scrollView setContentSize:(CGSizeMake(320, contentHeight))];
That's all, now your scrollView know the size of your text inside him, and will resize automatically, hope this helps, good luck.
Set your text field autoresizingMask property to flexible height and width:
[textField setAutoResizingMask:UIViewAutoResizingFlexibleHeight | UIViewAutoResizingFlexibleWidth];
This should expand the view automatically when you change it's parent's size (the scrollView's contentSize)
Otherwise try:
[textField setFrame:CGRectMake(0, 0, scrollView.contentSize.width, scrollView.contentSize.height)];
just after you change the scroll view's contentsize.
For an app that I am writing I need to display several views (in my case images, yet I guess that should not matter all that much) in a style similar to the way images are displayed in the Fotos app for the iPad. Consider this graphic:
Obviously on portrait view fewer slides can fit in per row than on Landscape view. What I have/want to do now:
Animate the addition of each slide by using a sliding animation
I already have a UIView subclass that represents a slide and that responds to touches the way I need it.
Where I am unsure is as to what is the best way of actually putting the slides onto the screen. I was originally just going to loop through them and place them onto the viewControllers view using the Frame of each slide (then I can do a count and increase the y-value after every 5 slides or so). Yet if I do it like that, I fear that my View will basically only support one screen orientation as I would need to relayout all the slides if the orientation changes, which seems rather a waste of resources to me (?).
How would you solve this problem? Is there maybe any "linebreak on overflow" ability such as with CSS (where a container automatically drops onto the next line if the float property is set and the parent container does not provide enough space)?
I am surely not the first person to think about this :)
Thanks for your answers!
There's no "linebreak on overflow" setting in Cocoa. If you want to make something that does that, you'll need to write code that lays out the slides. Here's a sample of the code I use to do it:
CGRect frame = CGRectMake(margins.left, margins.top, cellSize.width, cellSize.height);
for (UIButton *button in self.buttons) {
button.frame = frame;
// Move frame to next position
frame.origin.x += cellSize.width + cellSpacing;
if (frame.origin.x + frame.size.width > margins.left + contentWidth) {
frame.origin.x = leftMargin;
frame.origin.y += cellSize.height + cellSpacing;
}
}
I'm using UIButtons for each image, hence the self.buttons ivar.
Yes, you will need to relayout when the screen orientation changes. To do that, have a method like this:
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutButtons];
}
Which calls your layout code. In my case, it's layoutButtons. This is in a subclass of UIScrollView, so when the view is autoresized after an autorotation, the layoutSubviews method gets called.
If you're using a UIScrollView, make sure you update the self.contentSize ivar too.
I am trying to create an app with horizontal scrolling, so that one would be able to scroll horizontally through a series of images. I watched the WWDC Session 104 video on this, and while they made an interesting app, they flew through the basics of it very quickly.
I understand using the UIScrollView, and that I have to enable paging. After that they say that I should add more views as subviews of the scrollview, but I am not clear on how to do that. I am also not clear on how I add my images to those views.
As you can probably tell I am pretty new at this so any help would be appreciated.
You want to look into UIImageView. It's a view specifically for holding images.
When you add your images, you want to set their rects (probably using initWithFrame: for each UIImageView) so that:
the first image is at 0,0
the second image is at 320,0
third is at 640,0 (etc)
I.e. each image is 320 pixels right of the previous.
The final step is to set the contentSize for your UIScrollView -- this is a CGSize which describes the total size of the scroll view.
If you have 3 images, you would then set it to (320*3) * 480 using e.g.
myScrollView.contentSize = CGSizeMake(320*3, 480);
A lot of people, when they initialize the scroll view, have a for loop or similar which steps through the images they want to display. These for loops tend to look something like this:
CGFloat scrollWidth = 0.f;
for (UIImage *someImage in someNSArrayWithImages) {
UIImageView *theView = [[UIImageView alloc] initWithFrame:
CGRectMake(scrollWidth, 0, 320.f, 480.f)];
theView.image = someImage;
[myScrollView addSubview:theView];
[theView release];
scrollWidth += 320.f;
}
myScrollView.contentSize = CGSizeMake(scrollWidth, 480.f);
This way you'll get things lined up and you'll get the content size for you at the same time.
If you want to make it so that the scroll view "intelligently" scrolls to each image and stops when people slide left/right, you can do myScrollView.pagingEnabled = YES.
Hope that helps get you going.
Assuming you have "infinite" images, putting them all there at or before launch time in a huge UIScrollView will not be an option. (there is a limit to the size of a UIView)
The way I solved it: Make a UIScrollView covering the whole screen. It's content should be a UIView of 3*320 width and 480 height, extending 320px left and 320px right.
Put 3 UIImageView's in it, left, middle and right. Set paging=YES, so the uiscrollview clips to the 3 "pages" you've created.
Make sure your class is the delegate of the uiscrollview, and listen for
-(void)scrollViewDidEndDragging:(UIScrollView*)sv willDecelerate:(BOOL)notFinished
-(void)scrollViewDidEndDecelerating:(UIScrollView*)sv
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView*)sv
and make the appropriate transitions on hitting paging boundaries; shift images and set ContentOffset so you're looking at the center image again.
I suggest you make this first, and only then read on...
Then you will hit on a bug, documented here UIScrollView - (bounces = NO) seems to override (pagingEnabled = YES) and here http://www.iphonedevsdk.com/forum/iphone-sdk-development/935-paging-uiscrollview.html, which makes that you cannot disable bouncing and have paging enabled at the same time. So enable bouncing, and subclass UIScrollView, overruling setContentOffset in there to prevent bouncing. (having bouncing really enabled will make for a rather unusual user experience)
Have a look at Apple's PageControl sample code. It's fairly short and easy to follow so you'll get the gist of setting up a project where multiple view controllers are loaded as you swipe horizontally.
Once you have this setup then it's the view controller's responsibility to load its own content (in your case, an image). You should make sure you understand how to load images first (using threads, etc) before you tackle paging, etc.
Think of it as two independent tasks. The view control is responsible for loading and displaying an image. The scroll view with paging just tells the appropriate view controller when to load itself (it doesn't care what the view controller does once its loaded)
Good luck!
I want to do something similar to how mobile safari keeps its URL bar anchored at the top of the screen while a page is loading unless you scroll past the top, in which case it scrolls down with the rest of the content.
I, however, want the opposite to happen; I want specific subviews to scroll off the screen if I scroll down, but if I scroll to the top I want the subviews to stay anchored at the top of the screen while the rest of the content continues to scroll down. I suppose I could do some trickery with the view hierarchy where the subviews change their superview when we scroll to the top, but I'm wondering if there are any other more elegant solutions to this.
Very late answer to my own question, but I figured i'd reveal how I was able to do this.
I subclassed UIScrollView and implemented the layoutSubviews method, which is called every time the scroll position changes. Here, all you need to do is identify which view you want to anchor and do something like this:
- (void) layoutSubviews {
[super layoutSubviews];
CGFloat x = 0.0f;
CGFloat y = MIN(self.contentOffset.y,0.0f);
anchorview.frame = CGRectMake(x,y,anchorview.frame.size.width,anchorview.frame.size.height);
}
If you want the anchored view to stay at the top if you scroll past the top (instead of remaining in the same position relative to the scrollview), then this will do just that, since the content offset's y coordinate will be less than zero when you scroll beyond the very top of the view.
UITableView section headers already do this in plain tables, you could leverage that behavior.