I'm trying to make photo album like photo.app in iPhone. I know We have 'PhotoScroller' example from Apple (which I don't think I can copy and paste the code here due to the Non Disclosure Agreement, plus it's bit biggy to play with at the moment). However I found an another example from random search and it seems like based on the PhotoScroller (very simplified tough). The link for the example is ;
http://ykyuen.wordpress.com/2010/05/22/iphone-uiscrollview-with-paging-example/
I applied this code to my project to make photo album (and it scrolls, at least), but I found a problem. The problem is I cannot start from the page I want. The sample code only starts scrolling with Page One. Code itself seems 'really' simple but somehow I just cannot make it start from the page I choose.
Here is main part of the sample code that I found on the Internet. You can of course download the project the link above.
- (void)viewDidLoad {
NSLog(#"ScrollViewWithPagingViewContrller :: viewDidLoad");
[super viewDidLoad];
// view controllers are created lazily
// in the meantime, load the array with placeholders which will be replaced on demand
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < kNumberOfPages; i++) {
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
[controllers release];
// a page is the width of the scroll view
scrollView.pagingEnabled = YES;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.scrollsToTop = NO;
scrollView.delegate = self;
pageControl.numberOfPages = kNumberOfPages;
pageControl.currentPage = 0;
// pages are created on demand
// load the visible page
// load the page on either side to avoid flashes when the user starts scrolling
[self loadScrollViewWithPage:0];
[self loadScrollViewWithPage:1];}
- (void)loadScrollViewWithPage:(int)page {
NSLog(#"ScrollViewWithPagingViewContrller :: loadScrollViewWithPage");
if (page < 0) return;
if (page >= kNumberOfPages) return;
// replace the placeholder if necessary
MyViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[MyViewController alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
//
[controller release];
}
// add the controller's view to the scroll view
if (nil == controller.view.superview) {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
NSLog(#"ScrollViewWithPagingViewContrller :: scrollViewDidScroll");
// We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
// which a scroll event generated from the user hitting the page control triggers updates from
// the delegate method. We use a boolean to disable the delegate logic when the page control is used.
if (pageControlUsed) {
// do nothing - the scroll was initiated from the page control, not the user dragging
return;
}
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor(scrollView.contentOffset.x - pageWidth / 2) / pageWidth;
pageControl.currentPage = page;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
// A possible optimization would be to unload the views+controllers which are no longer visible
}
// At the begin of scroll dragging, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
NSLog(#"ScrollViewWithPagingViewContrller :: scrollViewWillBeginDragging");
pageControlUsed = NO;
}
// At the end of scroll animation, reset the boolean used when scrolls originate from the UIPageControl
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSLog(#"ScrollViewWithPagingViewContrller :: scrollViewDidEndDecelerating");
pageControlUsed = NO;
}
- (IBAction)changePage:(id)sender {
NSLog(#"ScrollViewWithPagingViewContrller :: changePage");
int page = pageControl.currentPage;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
// update the scroll view to the appropriate page
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
// Set the boolean used when scrolls originate from the UIPageControl. See scrollViewDidScroll: above.
pageControlUsed = YES;
}
I have implemented photo album view where in first view i have shown all the thumbnail images and on clicking each image it gives its full screen view. you also scroo the images.
See this answer.
All the best.
The answer is
scrollView1.contentOffset = CGPointMake( , );
quite simple. spent lot of time.
Related
I want to create UIScrollView with scrolling buttons.So when user press left arrow button, scroll must scroll properly.
The issue is: when I click button 3 times quickly scroll can't scroll properly (because of many calls of scrollRectToVisible). May be I can stop current animation before next animation?
P.S. If I set [self scrollScrollViewToIndex:index animated:NO] everything works properly, but I need animation
Here is my code:
- (void)scrollScrollViewToIndex:(int)index animated:(BOOL)animated
{
NSLog(#"scrolled to index: %d", index);
CGFloat offsetX = CGRectGetWidth(_scrollMain.frame) * index;
CGRect scrollRect = CGRectMake(offsetX, 0, CGRectGetWidth(_scrollMain.frame), CGRectGetHeight(_scrollMain.frame));
[_scrollMain scrollRectToVisible:scrollRect animated:animated];
// [self.scrollMain setContentOffset:CGPointMake(offsetX, 0) animated:animated];
}
- (IBAction)leftArrowPressed:(id)sender
{
int indexOfVoucher = [_arrayVouchers indexOfObject:_voucher];
indexOfVoucher--;
self.voucher = [_arrayVouchers objectAtIndex:indexOfVoucher];
[self updateViewWithVoucherWithScrolling:YES];
}
- (IBAction)rightArrowPressed:(id)sender
{
int indexOfVoucher = [_arrayVouchers indexOfObject:_voucher];
indexOfVoucher++;
self.voucher = [_arrayVouchers objectAtIndex:indexOfVoucher];
[self updateViewWithVoucherWithScrolling:YES];
}
- (void)updateViewWithVoucherWithScrolling:(BOOL)withScrolling
{
int indexOfVoucher = [_arrayVouchers indexOfObject:_voucher];
_leftArrowButton.hidden = _rightArrowButton.hidden = NO;
if (indexOfVoucher == 0)
{
_leftArrowButton.hidden = YES;
}
else if (indexOfVoucher == [_arrayVouchers count] - 1)
{
self.rightArrowButton.hidden = YES;
}
if (withScrolling)
{
[self scrollScrollViewToIndex:indexOfVoucher animated:YES];
}
}
update:
working code according to Mar0ux's advice
- (void)scrollScrollViewToIndex:(int)index animated:(BOOL)animated
{
NSLog(#"scrolled to index: %d", index);
CGFloat offsetX = CGRectGetWidth(_scrollMain.frame) * index;
if (animated)
{
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState //Multiple options
animations:^ {
// [self.scrollMain setContentOffset:CGPointMake(offsetX, 0) animated:NO];
CGRect scrollRect = CGRectMake(offsetX, 0, CGRectGetWidth(_scrollMain.frame), CGRectGetHeight(_scrollMain.frame));
[_scrollMain scrollRectToVisible:scrollRect animated:NO];
}
completion:^ (BOOL finished) {
}];
}
else
{
CGRect scrollRect = CGRectMake(offsetX, 0, CGRectGetWidth(_scrollMain.frame), CGRectGetHeight(_scrollMain.frame));
[_scrollMain scrollRectToVisible:scrollRect animated:NO];
}
}
You can always animate the contentOffset property yourself and use UIViewAnimationOptionBeginFromCurrentState option. As soon as the second animation begins, the first will end, and by using current state option, the second animation will start from where the first left off.
A few suggestions:
1) do you really want the user hammering on the button while its scrolling? If so then I suggest that your UI design may need redesign.
2) when you perturb the UI in an action method, its best to post other UI actions by dispatching a block with the code to the main queue - the button hiliting will look better.
3) in your specific case, you could in the action method disable the button, then re-enable it when the scrolling has stopped.
I'm fairly new to iOS development and I'm currently working on my thesis about this topic. My assignment is to build an app for a movie theater. The design has a horizontal slider containing the latest movie posters with the title, and I'm wondering how I should approach this design.
Screenshot
http://cl.ly/2S1b381S2v1x2y0U1q2H
There is an array with the movie poster image and the title, and I wan't them to show like the screenshot and being able to scroll horizontally between them.
I've looked around but I'm getting very confused by different approaches that are not 100% the same as my problem.
The nav bar and tab bar are just placeholders, I know how to implement those.
I'm hoping someone can help me with getting started with this design, how I should approach this.
Thanks in advance,
Lars
You can download and use the demo application at the following url as reference
https://developer.apple.com/library/ios/#samplecode/Scrolling/Introduction/Intro.html
By increasing the size of the UIScrollView will be showing part the contents towards left and right.
In the demo there are showing images, instead of that create a UIView which hold image and title and replace should work.
- (void)loadScrollViewWithPage:(int)page scrollingView:(UIScrollView*)scrollView
{
//NSLog(#"page %d",page);
if (page < 0) return;
if (page >= numberOfPages) return;
// replace the placeholder if necessary
IconsViewController *controller;
if (scrollView == iconsScrollView)
{
controller = [viewControllers objectAtIndex:page];
}
if ((NSNull *)controller == [NSNull null])
{
controller = [[IconsViewController alloc] init];
controller.pageIndex = page;
if (scrollView == iconsScrollView)
{
[viewControllers replaceObjectAtIndex:page withObject:controller];
}
[controller release];
}
// add the controller's view to the scroll view
if (nil == controller.view.superview)
{
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scrollView addSubview:controller.view];
}
}
You should use a UIScrollView with paging enabled.
And then take a look at this approach: https://stackoverflow.com/a/3018776/696440
I have a bunch of webviews with static html content that I'm putting in a scroll view as pages. That works fine, but having 20 something full screen subviews of the scroll view is causing some lag. I solved that by only placing 5 at a time in there. The current view, and the two next and two previous.
The problem now is that any web view that is not a subview of the scroll view is not drawing correctly based on the device orientation. The frame of the webview is printing out correct, but the actual content is drawing in portrait mode. So there is essentially a strip of blank space to the right of the content.
How do I go about re rendering the content without reloading the page?
Here's the relevant code:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation) fromInterfaceOrientation{
[self resizeSubViews];
}
- (void) setupWebViews{
if(_webViews == nil)
_webViews = [[NSMutableArray alloc] init];
// This is where the navigation would come into play as far as loading up available web views
[_webViews removeAllObjects];
// for loop here to create web views
for (int i = 0; i < 20; i++) {
//CDCWebViewController *webView = [[[MyInternalWebView alloc] initWithFrame:_webViewWrapper.frame] retain];
MyInternalWebView *page = [[[MyInternalWebView alloc] init] retain];
[page loadRequest:[NSURLRequest requestWithURL:_url]];
[page setCdcIWVdelegate:self];
[page setTestIndex:i];
[page setPageIndex:i];
[_webViews addObject:page];
[self loadScrollViewWithPage:i];
}
[self clearUnusedWebViews:_pageControl.currentPage];
}
- (void) setupWebViewWrapper{
// a page is the width of the scroll view
_webViewWrapper.pagingEnabled = YES;
_pageControl = [[[UIPageControl alloc] init] retain];
_webViewWrapper.showsHorizontalScrollIndicator = NO;
_webViewWrapper.showsVerticalScrollIndicator = NO;
_webViewWrapper.scrollsToTop = NO;
_webViewWrapper.delegate = self;
_pageControl.numberOfPages = [_webViews count];
_pageControl.currentPage = 0;
}
- (void) resizeSubViews{
// The frame is set in IB
_webViewWrapper.contentSize = CGSizeMake(_webViewWrapper.frame.size.width * [_webViews count], _webViewWrapper.frame.size.height);
// Move the content offset.
_webViewWrapper.contentOffset = CGPointMake(_webViewWrapper.frame.size.width * _pageControl.currentPage, _webViewWrapper.contentOffset.y);
for (MyInternalWebView *subview in _webViewWrapper.subviews) {
// Reset the frame height and width here?
CGRect frame = _webViewWrapper.frame;
frame.origin.x = frame.size.width * subview.pageIndex;
frame.origin.y = 0;
[subview setFrame:frame];
}
}
//*****************************************************
//*
//* ScrollView Functions
//*
//*****************************************************
- (void)loadScrollViewWithPage:(int)page {
// Make sure we're not out of bounds
if (page < 0) return;
if (page >= [_webViews count]) return;
MyInternalWebView *webView = [_webViews objectAtIndex:page];
// Add the preloaded webview to the scrollview if it's not there already
if (nil == [webView superview]) {
CGRect frame = _webViewWrapper.frame;
//NSLog(#"width = %f", frame.size.width);
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
//NSLog(#"setting frame for page %d %#", page, NSStringFromCGRect(frame));
[webView setFrame:frame];
[_webViewWrapper addSubview:[_webViews objectAtIndex:page]];
// Now that the new one is loaded, clear what doesn't need to be here
[self clearUnusedWebViews:page];
}
}
- (void) clearUnusedWebViews: (NSInteger) page{
for (int i = 0; i < [_webViews count]; i++) {
if ((page - i) <= 2 && i - page <= 2) {
continue;
}
[[_webViews objectAtIndex:i] removeFromSuperview];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = _webViewWrapper.frame.size.width;
NSInteger page = floor((_webViewWrapper.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
_pageControl.currentPage = page;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 2];
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
[self loadScrollViewWithPage:page + 2];
}
In resizeSubViews, change frames of web views from _webViews array, not scroll view subviews.
Addendum:
Put check in scrollViewDidScroll if page != _pageControl.currentPage and only then load scrollViewsWithPage.
Orientation change might and probably will trigger scrollViewDidScroll.
In clearUnusedWebViews use _pageControl.currentPage instead of page variable. When you're at 5th view and you're loading 7th, you're removing 4th and 3rd even I'd say your intention was to have 3,4,5,6,7 in scroll view at this time.
reload
Reloads the current page.
- (void)reload
Availability
Available in iOS 2.0 and later.
See Also
#property request
#property loading
– loadRequest:
– stopLoading
Declared In
UIWebView.h
UIWebView Class Reference
pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(153,356,38,36) ];
pageControl.userInteractionEnabled =YES;
pageControl.numberOfPages = 2;
pageControl.currentPage = 1;
pageControl.enabled = TRUE;
[pageControl setHighlighted:YES];
[pageControl addTarget:self action:#selector(changePage:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:pageControl];
}
- (IBAction) changePage:(id)sender
{
}
I'm programmatically creating page control and i want to display new view controllers on click of page control. How i need to implement this changePage method? Can anyone help?
You can show two views instead of showing two different view controller. You can keep first dot selected and show first view and have next view out of screen, to its right. When user taps second dot, make UIView animation similar to pushing in UINavigationController. Thus, you do push and pop with UIView animation.
If you want to show view controllers, then the page control needs to be shown in both the view controller, so that user can switch from one to another. In such case, you need to have the page control in a view, added in the main window, so that its visible everywhere.
The easiest way to program a method to change pages would be the following:
- (IBAction)changePage:(id)sender {
CGrect frame;
frame.origin.x = self.scrollView.frame.size.width * self.pageControl.currentPage;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
[self.scrollView scrollRectToVisible:frame animated:YES];
}
EDIT: if you are trying to simply change the view controller by clicking the dots, you will need to set your page up so that the main view has a UIPageControl at the bottom and another UIView (we will call this controllerView) above it taking up most of the screen, but not overlaying the page control.
You will also want PageOne *pageOneController; and PageTwo *pageTwoController; in your header file. This will help prevent memory leaks.
So when you select another page, you'll call your changePage method
- (IBAction)changePage:(id)sender {
if (sender.currentPage == 1) {
// make sure only one instance exists at a time so there aren't any memory leaks;
if (pageOneController != nil) {
pageOneController = nil;
[pageOneController release];
}
// load up page one;
pageOneController = [[PageOne alloc] initWithNibName:#"PageOneNib" bundle:nil];
// set this as the primary view;
controllerView = viewController.view;
} else {
// do the same for your other page;
}
}
This should do the trick for you
I'm using a UIScrollerView and I found that it tends to "snap" back to a previous position when I scroll it.
I would like to simulate the behavior if the UItableView's scroller, that stays in the position where the user released his finger.
The properties set:
[scroller setCanCancelContentTouches:NO];
scroller.clipsToBounds = YES;
[scroller setScrollEnabled:YES];
scroller.userInteractionEnabled = YES;
scroller.alwaysBounceHorizontal = YES;
scroller.alwaysBounceVertical= NO;
scroller.showsVerticalScrollIndicator = NO;
scroller.scrollsToTop = NO;
scroller.bounces = NO;
scroller.pagingEnabled = NO;
There a few possible reasons for the snap back.
One could be that you have pagingEnabled set to YES which means that when the user lets go their finger, it will snap to the closest page boundary.
Another is that you have the contentSize set incorrectly. This is not the problem if you can actually scroll to the top and bottom and leave it there without it bouncing away.
- (void)scrollViewDidScroll:(UIScrollView *)sender {
int scrollDirection;
if (lastContentOffset > self.scrollView.contentOffset.x){
scrollDirection = RIGHT;
NSLog(#"right");
}
else if (lastContentOffset < self.scrollView.contentOffset.x) {
scrollDirection = LEFT;
NSLog(#"left");
lastContentOffset = self.scrollView.contentOffset.x;
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
lastContentOffset = self.scrollView.contentOffset.x;
}