How to programmatically set the current page for UIPageControl? - iphone

I have 3 "pages" (page 0, page 1, page 2) in a UIScrollView that are snapped to by finger swipes. There is a UIPageControl there too. The UIScrollView starts off presenting page 0. What I want to do is present page 3 FIRST sometimes. How can I do this programmatically.
Simply setting currentPage (of UIPageControl) to the page number does nothing by the way.

(From the PageControl example)
To go directly to a particular section of the scrollView use
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
I just implemented the same, e.g scrollview and page control and used the
pageControl.currentPage = {pageNumber};
which worked perfectly, are you sure that the pageControl outlet is set correctly in Interface Builder (if you're using it).
If the problem is that the scrollView is not working, and not the pageControl. Then make sure that you have the contentsize of the scrollView set correctly.
scrollView.contentSize = CGSizeMake(320*numberOfPages, scrollView.frame.size.height);
To this scrollview you then add the content to each section
page1ViewController.view.frame = CGRectMake(0, 0, 320, 200);
[scrollView addSubview:page1ViewController.view];
Next page gets pushed over 1 screen width
page2ViewController.view.frame = CGRectMake(320, 0, 320, 200);
[scrollView addSubview:page2ViewController.view];

Related

ScrollView Detecting Scrolling Everywhere

I have a UIScrollView which is 208pt wide and 280pt tall that contains custom buttons that are 200pt wide and 280 pt tall with 8pt gaps between them. This scrollview has paging enabled but doesn't clip the subviews so that it always snaps to having one button centered but shows the other ones that go off screen. I am trying to make the field in which you can swipe through the buttons take up the full width of the screen, and I am trying to accomplish this with a secondary custom subclass of UIScrollView called PagingView which just has a UIScrollView property and passes all hits on it down to its scrollview. For whatever reason, though, when I try it without the paging view like this:
unsigned height = self.view.frame.size.height;
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(60, height - 308, 208, 280)];
scrollView.pagingEnabled = true;
scrollView.clipsToBounds = false;
scrollView.showsHorizontalScrollIndicator = false;
[self.view addSubview:scrollView];
It works, albeit with the field I can interact with the scrollview limited to its frame. However, when I try it with the scrollview:
unsigned height = self.view.frame.size.height;
pagingView = [[PagingView alloc] initWithFrame:CGRectMake(0, height - 308, 320, 280)];
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(60, height - 308, 208, 280)];
scrollView.pagingEnabled = true;
scrollView.clipsToBounds = false;
scrollView.showsHorizontalScrollIndicator = false;
pagingView.scrollView = scrollView;
[self.view addSubview:pagingView];
[self.view addSubview:scrollView];
It works, but I am able to swipe anywhere on the screen to move through the scrollview. How do I remedy this?
Not clear what you are doing here. You are adding two scroll views to self.view but actually you need only one.
The easiest way with least code is to use one plain UIScrollView, present your buttons as subviews of correctly sized UIViews (you need the gaps around the buttons), and enable paging for the scroll view. Done.
Please note that the measurements you describe do not work out. The scroll view width is 208, the button 200, so the wrapper view should have width 208 and the origin.x of the button should be 4. The scroll view height is 280, the button as well, so there is no vertical margin: wrapper height also 280, button origin.y is 0.

UIScrollView scrollrecttovisible method

I have an UIScrollview(Horizontal) with a UIview as a subview, here i can able to drag the uiview inside the scrollview, the contentSize of scrollview is 2400. When i dragging that uiview it's not visible i.e, it's going inside the scrollview because my scrollview width is small, i need to scroll the scrollview to make visible of UIView. For this i have use
[myscroll scrollRectToVisible:myview.frame animated:YES];
but still its not working, please help me out.
In Your case,[myscroll scrollRectToVisible:myview.frame animated:YES]; will not work because of myview is a sub-view of myscroll. myview.frame will return the CGRect which is only related to the myscroll.
My suggestion is , you can acheive the functionality through UIpageControl + UIScrollView . You can set UIPageControl as Hidden.
SampleCode
int page = sidePager.currentPage + 1;
CGRect frame = scroller.frame;
frame.origin.x = frame.size.width * page;
if (0 != UpAndDownPager.currentPage) {
frame.origin.y = frame.size.height * (UpAndDownPager.currentPage + 1 );
}
scroller scrollRectToVisible:frame animated:YES];
sidePager.currentPage = sidePager.currentPage + 1;
Note:
sidePager: UIPageControl For Right-Left position
UpAndDownPager: UIPageControl For Top-Bottom position
scrollView.contentSize.width != 0
&&
scrollView.contentSize.height != 0

View is moved 3 pixels

In my app I move the table view (in order to make the text fields visible when the keyboard appears). The view is looks following:
alt text http://img34.imageshack.us/img34/5845/view2.png
This is the code I use for resizing the view and moving it up:
static const NSUInteger navBarHeight = 44;
CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
tableView.frame = CGRectMake(0, navBarHeight, appFrame.size.width, appFrame.size.height-navBarHeight-216); //216 for the keyboard
NSIndexPath *indPath = [self getIndexPathForTextField:textField]; //get the field the view should scroll to
[tableView scrollToRowAtIndexPath:indPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
The problem is that when the view is moved up it also moves 3 pixels into right direction (it is hard to see the difference in the screenshot, but it is visible when the animation is on and I measured the difference with PixelStick tool). Here it is how it looks after the move:
alt text http://img179.imageshack.us/img179/3310/iphonesimulator.png
My analysis shows that scrolling the table does not influence the move to the right.
Any ideas what is wrong in the code above that makes the view move to the right?
perhaps try changing this line :
tableView.frame = CGRectMake(0, navBarHeight, appFrame.size.width, appFrame.size.height-navBarHeight-216); //216 for the keyboard
to
tableView.frame = CGRectMake(tableView.frame.origin.x, navBarHeight, appFrame.size.width, appFrame.size.height-navBarHeight-216); //216 for the keyboard
just in case the origin.x of the tableview isn't at 0?

UIButtons at the bottom of a UIScrollView are not receiving touches

I am writing an iPhone app with a tab bar and navigation bar. At a certain point, I am pushing an instance of my DetailsViewController class onto the navigation controller stack to show it.
This controller creates its view hierarchy in code: the controller's view property is set to a UIScrollView, which contains a plain UIView (let's call it "contentView") sized to hold all the content to be shown. At the bottom of this contentView, I have four UIButtons.
Now when I run the app (in the simulator at present), and scroll to the bottom of the view, the top two buttons respond to touches; the third responds to touches only in the top portion of it, and the lower button doesn't respond to touches at all. By clicking in various parts of the third button, it appears that the lower 93 pixels of the scroll view is not passing touch events through to its subviews.
93 is suspicious: it's also the combined height of the tab bar (49 pixels) and navigation bar (44 pixels). Yet the navigation bar and tab bar are outside the scroll view. Any suggestions why this might be happening?
Here's the code in question:
- (void)loadView
{
CGRect frame = [[UIScreen mainScreen] applicationFrame];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
scrollView.delegate = self;
self.view = scrollView;
UIView *contentView = [[UIView alloc] initWithFrame:scrollView.bounds];
contentView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[scrollView addSubview:contentView];
CGSize viewSize = contentView.bounds.size;
CGSize size;
CGFloat y = 0;
/* Snip creating various labels and image views */
/* Actions view creates and lays out the four buttons; its sizeThatFits:
** method returns the frame size to contain the buttons */
actionsView = [[PropertyDetailActionsView alloc] initWithFrame:CGRectZero];
actionsView.autoresizingMask = (UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth);
actionsView.delegate = self;
size = [actionsView sizeThatFits:viewSize];
actionsView.frame = CGRectMake(0, y, size.width, size.height);
[contentView addSubview:actionsView];
y += size.height;
[contentView setFrame:CGRectMake(0, 0, viewSize.width, y)];
scrollView.contentSize = contentView.frame.size;
[contentView release];
[scrollView release];
}
As I suggested on Twitter yesterday, it may have something to do with the flexible bottom margin set to the actionsView.
That suggestion did not resolve the problem, yet it lead to the right direction. By removing the flexible height of the contentView the problem has been fixed.
So if anyone out there is having similar problems, try to play with your autoresizingMasks.
also make sure all your content views are the height that covers the bottom button.
I make each view a different color to see them.

Photos app-like gap between pages in UIScrollView with pagingEnabled

UIScrollView in paging mode assumes the pages are located right next to each other, with no gap. However if you open a photo in the Photos app and swipe through photos, you can see that it has some gap between pages. I want these gaps too.
I'm looking for existing solutions if any, or for some more bizarre ideas about implementing the page gaps besides the one I have explained below. Or maybe there's some obvious easy way I am missing?
To be clear: I want the gap to only be visible while scrolling, so I cannot simply inset the page content.
My plan is to try moving the page content from inside scrollViewDidScroll callback, so that (assuming you're scrolling to the right) initially the target page is slightly offset to the right of its page boundaries, and by the time you arrive at the target page it's back at its proper location, and the source page is slightly offset to the left of its boundaries. (Or maybe instead of moving things continuously, I'll be better off shifting the offsets, say, exactly halfway between pages.)
I'm the author of the ScrollingMadness article+example that I've been referring some people to here. I've implemented progammatic zooming, and got in-photo zooming+scrolling working together with inter-photo paging. So I know how to play with UIScrollView, and am looking for the advanced stuff.
Please don't point me at TTScrollView. I've already pointed many people to it myself, but I consider it's feel too far from the native UIScrollView behaviour, and do not want to use it in my projects.
Note that this answer is quite old. The basic concept still works but
you should not be hard coding view sizes in iOS7 and 8. Even if you ignore
that advice, you should not use 480 or 330.
Have you tried making the frame of the UIScrollView slightly larger than the screen (assuming that you want to display your images fullscreen and then arranging your subviews on the same slightly-larger-than-the-screen boundaries.
#define kViewFrameWidth 330; // i.e. more than 320
CGRect scrollFrame;
scrollFrame.origin.x = 0;
scrollFrame.origin.y = 0;
scrollFrame.size.width = kViewFrameWidth;
scrollFrame.size.height = 480;
UIScrollView* myScrollView = [[UIScrollView alloc] initWithFrame:scrollFrame];
myScrollView.bounces = YES;
myScrollView.pagingEnabled = YES;
myScrollView.backgroundColor = [UIColor redColor];
UIImage* leftImage = [UIImage imageNamed:#"ScrollTestImageL.png"];
UIImageView* leftView = [[UIImageView alloc] initWithImage:leftImage];
leftView.backgroundColor = [UIColor whiteColor];
leftView.frame = CGRectMake(0,0,320,480);
UIImage* rightImage = [UIImage imageNamed:#"ScrollTestImageR.png"];
UIImageView* rightView = [[UIImageView alloc] initWithImage:rightImage];
rightView.backgroundColor = [UIColor blackColor];
rightView.frame = CGRectMake(kViewFrameWidth * 2,0,320,480);
UIImage* centerImage = [UIImage imageNamed:#"ScrollTestImageC.png"];
UIImageView* centerView = [[UIImageView alloc] initWithImage:centerImage];
centerView.backgroundColor = [UIColor grayColor];
centerView.frame = CGRectMake(kViewFrameWidth,0,320,480);
[myScrollView addSubview:leftView];
[myScrollView addSubview:rightView];
[myScrollView addSubview:centerView];
[myScrollView setContentSize:CGSizeMake(kViewFrameWidth * 3, 480)];
[myScrollView setContentOffset:CGPointMake(kViewFrameWidth, 0)];
[leftView release];
[rightView release];
[centerView release];
Apologies if this doesn't compile, I tested it in a landscape app and hand edited it back to portrait. I'm sure you get the idea though. It relies on the superview clipping which for a full screen view will always be the case.
So I don't have enough "rep" to post a comment on the answer above. That answer is correct, but there is a BIG issue to be aware of:
If you're using a UIScrollView in a viewController that's part of a UINavigationController, the navigation controller WILL resize the frame of your scrollView.
That is, you have an app that uses a UINavigationController to switch between different views. You push a viewController that has a scrollView and you create this scrollView in the viewController's -init method. You assign it a frame of (0, 0, 340, 480).
Now, go to your viewController's -viewDidAppear method, get the frame of the scrollView you created. You'll find that the width has been reduced to 320 pixels. As such, paging won't work correctly. You'll expect the scrollView to move 340 pixels but it will, instead, move 320.
UINavigationController is a bit notorious for messing with subviews. It moves them and resizes them to accommodate the navigation bar. In short, it's not a team player -- especially in this case. Other places on the web suggest that you not use UINavigationController if you need precise control over your views' size and locations. They suggest that, instead, you create your own navigationController class based on UINavigationBar.
Well that's a ton of work. Fortunately, there's an easier solution: set the frame of the scrollView in your viewController's -viewDidAppear method. At this point, UINavigationController is done messing with the frame, so you can reset it to what it should be and the scrollView will behave properly.
This is relevant for OS 3.0. I have not tested 3.1 or 2.2.1. I've also filed a bug report with Apple suggesting that they modify UINavigationController with a BOOL such as "-shouldAutoarrangeSubviews" so that we can make that class keep its grubby hands off subviews.
Until that comes along, the fix above will give you gaps in a paginated UIScrollView within a UINavigationController.
Apple has released the 2010 WWDC session videos to all members of the iphone developer program. One of the topics discussed is how they created the photos app!!! They build a very similar app step by step and have made all the code available for free.
It does not use private api either. Here is a link to the sample code download. You will probably need to login to gain access.
http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645
And, here is a link to the iTunes WWDC page:
http://insideapple.apple.com/redir/cbx-cgi.do?v=2&la=en&lc=&a=kGSol9sgPHP%2BtlWtLp%2BEP%2FnxnZarjWJglPBZRHd3oDbACudP51JNGS8KlsFgxZto9X%2BTsnqSbeUSWX0doe%2Fzv%2FN5XV55%2FomsyfRgFBysOnIVggO%2Fn2p%2BiweDK%2F%2FmsIXj
The way to do this is like you said, a combination of a few things.
If you want a gap of 20px between your images, you need to:
First, expand your scroll view's total width by 20px and move it left by 10px.
Second, when you lay out the xLoc of your images, add 20px for each image so they're spaced 20px apart.
Third, set the initial xLoc of your images to 10px instead of 0px.
Fourth, make sure you set the content size of your scroll view to add 20px for each image. So if you have kNumImages images and each is kScrollObjWidth, then you go like this:
[scrollView setContentSize:CGSizeMake((kNumImages * (kScrollObjWidth+20)), kScrollObjHeight)];
It should work after that!
This is just a hunch, so apologies if completely wrong, but is it possible that the contentSize is just set to slightly wider than the screen width.
The correct information is then rendered within the view to the screen width and UIScrollView takes care of the rest ?
Maybe you want to try UIScrollView's contentInset property?
myScrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 10.0);
I just thought I'd add here for posterity the solution I ended up going with. For a long time I've been using Bryan's solution of adjusting the frame in -viewDidAppear, and this has worked brilliantly. However since iOS introduced multitasking I've been running into a problem where the scroll view frame gets changed when the app resumes from the background. In this case, -viewDidAppear was not being called and I couldn't find a delegate method that would be called at the right time to reverse the change. So I decided to make my scroll view a subview of my View Controller's view, and this seemed to fix the problem. This also has the advantage of not needing to use -viewDidAppear to change the frame - you can do it right after you create the scroll view. My question here has the details, but I'll post them here as well:
CGRect frame = CGRectMake(0, 0, 320, 460);
scrollView = [[UIScrollView alloc] initWithFrame:frame];
// I do some things with frame here
CGRect f = scrollView.frame;
f.size.width += PADDING; // PADDING is defined as 20 elsewhere
scrollView.frame = f;
[self.view addSubview:scrollView];
To avoid messing with UIScrollView's frame, you could subclass UIScrollView and override layoutSubviews to apply an offset to each page.
The idea is based on the following observations:
When zoomScale !=1, the offset is zero when it is at the left / right edge
When zoomScale ==1, the offset is zero when it is at the visible rect centre
Then the following code is derived:
- (void) layoutSubviews
{
[super layoutSubviews];
// Find a reference point to calculate the offset:
CGRect bounds = self.bounds;
CGFloat pageGap = 8.f;
CGSize pageSize = bounds.size;
CGFloat pageWidth = pageSize.width;
CGFloat halfPageWidth = pageWidth / 2.f;
CGFloat scale = self.zoomScale;
CGRect visibleRect = CGRectMake(bounds.origin.x / scale, bounds.origin.y / scale, bounds.size.width / scale, bounds.size.height / scale);
CGFloat totalWidth = [self contentSize].width / scale;
CGFloat scrollWidth = totalWidth - visibleRect.size.width;
CGFloat scrollX = CGRectGetMidX(visibleRect) - visibleRect.size.width / 2.f;
CGFloat scrollPercentage = scrollX / scrollWidth;
CGFloat referencePoint = (totalWidth - pageWidth) * scrollPercentage + halfPageWidth;
// (use your own way to get all visible pages, each page is assumed to be inside a common container)
NSArray * visiblePages = [self visiblePages];
// Layout each visible page:
for (UIView * view in visiblePages)
{
NSInteger pageIndex = [self pageIndexForView:view]; // (use your own way to get the page index)
// make a gap between pages
CGFloat actualPageCenter = pageWidth * pageIndex + halfPageWidth;
CGFloat distanceFromRefPoint = actualPageCenter - referencePoint;
CGFloat numOfPageFromRefPoint = distanceFromRefPoint / pageWidth;
CGFloat offset = numOfPageFromRefPoint * pageGap;
CGFloat pageLeft = actualPageCenter - halfPageWidth + offset;
view.frame = CGRectMake(pageLeft, 0.f, pageSize.width, pageSize.height);
}
}