Am having three UIViews(320*568). I want to reuse them on scrollView. Like we do with UItable cells.
AM Having UIscrollView of width 320X10. i.e, it can show 10 views. But I want to use only 3 views. Here's my code, But It Doesnt work well when I scroll too fast. PLease Help.
I added the same code in the - (void)scrollViewDidScroll:(UIScrollView *)sender
It works Properly then. But It Prints NSLog("hello"); about 10-15 times, when I scroll just one page.
.
.
.
.
.
(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSlog("hello");
int pageCurrently = scrollView.contentOffset.x / scrollView.frame.size.width;
if (scrollView.contentOffset.x < 320)
{
a=0;
b=320;
c=640;
}
else if (scrollView.contentOffset.x >= 320)
{
if(scrollView.contentOffset.x == 320)
{
a=0;
b=320;
c=640;
}
else if(toCheckScrollNumber == pageCurrently )
{
}
else if(pageCurrently > intPreviousPage)
{
largeVal=MAX(c, MAX(a, b));
smallVal=MIN(c, MIN(a, b));
if(smallVal==a)
{
a = largeVal + 320;
}
else if(smallVal==b)
{
b = largeVal + 320;
}
else if (smallVal==c)
{
c = largeVal + 320;
}
}
else if(pageCurrently < intPreviousPage)
{
largeVal=MAX(c, MAX(a, b));
smallVal=MIN(c, MIN(a, b));
if(largeVal==a)
{
a = smallVal - 320;
}
else if(largeVal==b)
{
b = smallVal - 320;
}
else if (largeVal==c)
{
c = smallVal - 320;
}
}
}
NSLog(#"a=%d , b=%d , c=%d ",a,b,c);
[_otlSubView1 setFrame:CGRectMake(a, 0, 320, 568)];
[_otlSubView2 setFrame:CGRectMake(b, 0, 320, 568)];
[_otlSubView3 setFrame:CGRectMake(c, 0, 320, 568)];
intPreviousPage=pageCurrently;
}
You should use the scrollViewDidScroll function to do your view reuse, not didEndDecelerating - the latter is only called when the scroll view already slows down.
As your views are quite small (10 pixels high), it might also be that you have to add some views more in advance (eg not only one view that is to become visible, but maybe already 3 in advance - the UITableView also does preload a few lines more.
Related
I want to set 5 buttons on uiview at random position. Buttons need maintain some spacing to each other. I mean buttons should not overlap to each other.
All buttons set on UIView come from corners with rotation animation.
btn1.transform = CGAffineTransformMakeRotation(40);
btn2.transform = CGAffineTransformMakeRotation(60);
btn3.transform = CGAffineTransformMakeRotation(90);
btn4.transform = CGAffineTransformMakeRotation(30);
btn5.transform = CGAffineTransformMakeRotation(20);
I Can rotate buttons using above code but can you pls. help me for set buttons on random position with out overlapping by each other.
If points are fix than I can set buttons with animation by this code but I want random position of buttons.
[AnimationView moveBubble:CGPointMake(18, 142) duration:1 : btn1];
[AnimationView moveBubble:CGPointMake(118, 142) duration:1 : btn2];
[AnimationView moveBubble:CGPointMake(193, 142) duration:1 : btn3];
[AnimationView moveBubble:CGPointMake(18, 216) duration:1 : btn4];
Thanks in advance.
1st, add buttons to an NSArray, only to make things easier:
NSArray *buttonArray = #[btn1,btn2,btn3,btn4,btn5];
Now, this code tries to Arrange them at random positions.
int xTemp, yTemp;
for (int i = 0; i < 5; i++) {
while (YES) {
xTemp = arc4random_uniform(view.frame.size.width - [buttonArray[i] frame].size.width);
yTemp = arc4random_uniform(view.frame.size.height - [buttonArray[i] frame].size.height);
if (![self positionx:xTemp y:yTemp intersectsAnyButtonTillIndex:i inButtonArray:buttonArray]) {
[AnimationView moveBubble:CGPointMake(xTemp, yTemp) duration:1 : buttonArray[i]];
break;
}
}
}
Implement this function somewhere too:
- (BOOL) positionx:(int)xTemp y:(int)yTemp intersectsAnyButtonTillIndex:(int)index inButtonArray:(NSArray *)buttonArray {
//Again please change the < to <= , I'm sorry, doing too many things at once.
for (int i = 0; i <= index; i++) {
CGRect frame = [buttonArray[i] frame];
//EDIT : In the logic earlier, I had wrongly done a minus where I should have done a plus.
if ((xTemp > frame.origin.x && xTemp < (frame.size.width + frame.origin.x)) && (yTemp > frame.origin.y && yTemp < (frame.size.height + frame.origin.y))) return YES;
}
return NO;
OK this is a workign soln., I hope, just added something to WolfLink's answer. Check This.
for (UIButton *button in buttonArray) {
button.frame = CGRectMake(arc4random_uniform(view.frame.size.width - button.frame.size.width), arc4random_uniform(view.frame.size.height - button.frame.size.height), button.frame.size.width, button.frame.size.height);
while ([self button:button intersectsButtonInArray:buttonArray]) {
button.frame = CGRectMake(arc4random_uniform(view.frame.size.width - button.frame.size.width), arc4random_uniform(view.frame.size.height - button.frame.size.height), button.frame.size.width, button.frame.size.height);
}
//another function
-(BOOL)button:(UIButton *)button intersectsButtonInArray:(NSArray *)array {
for (UIButton *testButton in array) {
if (CGRectIntersectsRect(button.frame, testButton.frame) && ![button isEqual:testButton]) {
return YES;
}
}
return NO;
}
Based on spiritofmysoul.wordpress's code:
//in the function where you randomize the buttons
NSArray *buttonArray = #[btn1,btn2,btn3,btn4,btn5];
for (UIButton *button in buttonArray) {
float widthOffset = self.frame.size.width-button.frame.size.width;
float heightOffset = self.frame.size.height-button.frame.size.height;
button.frame = CGRectMake(arc4random()%widthOffset, arc4random()%heightOffset, button.frame.size.width, button.frame.size.height);
while ([self button:button intersectsButtonInArray:buttonArray]) {
button.frame = CGRectMake(arc4random(), arc4random(), button.frame.size.width, button.frame.size.height);
}
//another function
-(BOOL)button:(UIButton *)button intersectsButtonInArray:(NSArray *)array {
for (UIButton *testButton in array) {
if (CGRectIntersectsRect(button.frame, testButton.frame) && ![button isEqual:testButton]) {
return YES;
}
}
return NO;
}
Beware: This will work well for small amounts of buttons on a large space but as you add buttons and you run out of space, this method will take much longer to run. If there is not enough space for all the buttons, it will become an infinite loop.
I have a UIImageView being moved with a timer towards the edge of the view but it carries on and I don't know how to stop the UIImageView as soon as it hits he edge! Please Help?
This is what I have tried so far in my method!
Code:
-(void)moveBack {
CGPoint manCenter = man.center;
if (manCenter.x > 480) {
manCenter.x = 480;
}
man.center = CGPointMake(man.center.x +5, man.center.y);
[self ifCollided];
}
When you do your man.center.x + 5 it will be out of the UIImage frame. When you set this to 480 it should be fitting.
-(void)moveBack {
man.center = CGPointMake(man.center.x + 5, man.center.y);
[self ifCollided];
if (man.center.x > 480) {
man.center = CGPointMake(480 , man.center.y);
}
}
-(void)moveBack {
man.center = CGPointMake(man.center.x + 5, man.center.y);
[self ifCollided];
if (man.center.x > 480) {
man.center = CGPointMake(480 , man.center.y);
}
}
Ok, so I have these powerups that I want to slow/speed up the movement of the other objects in the game for a few seconds.
I have an array of objects that I have a variable called spawnInterval that gets faster and faster as the game progresses, making the ame get harder after a few mins.
But I can't really grasp how to make it so the character in the game will react differently to different objects as in when the fastPowerUp is hit by the character sprite, the spawn interval doesn't change.
And vice versa with the slowPowerUp.
the code I have at the moment is this in a move sequence method that gets called in an update method:
-
(void) updateObstacles:(ccTime)delta{
for (int i = 0; i < 20; i++) {
//int randomizer = CCRANDOM_0_1() * [obstacles count];
//NSLog(#"randomizer: %i",randomizer);
CCSprite* randomObject = [obstacles randomObject];
currentObject = [obstacles indexOfObject:randomObject];
if ([randomObject numberOfRunningActions] == 0) {
[self runObstacleMoveSequence:randomObject withTimer:delta];
break;
}
}
}
-(void) runObstacleMoveSequence:(CCSprite *)object withTimer:(ccTime)delta{
static int time;
//Slowly increase object speed
numObstaclesMoved++;
if (!slowPowerUp && !fastPowerUp) {
time += delta;
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration -= 0.2f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
}
}
}else if (slowPowerUp && !fastPowerUp) {
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration += 3.0f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
if (time >= (delta + 3)) {
slowPowerUp = NO;
obstacleMoveDuration -= 3.0f;
}
}
}
}else if (!slowPowerUp && fastPowerUp) {
if (numObstaclesMoved % 17 == 0 && obstacleMoveDuration > 2.0f) {
obstacleMoveDuration -= 3.0f;
if (spawnInterval > 0.1f) {
[self unschedule:#selector(updateObstacles:)];
[self schedule:#selector(updateObstacles:) interval:spawnInterval];
spawnInterval-=0.1f;
NSLog(#"interval: %f",spawnInterval);
if (time >= (delta + 3)) {
fastPowerUp = NO;
obstacleMoveDuration += 3.0f;
}
}
}
}
CGSize screenSize = [[CCDirector sharedDirector]winSize];
CGPoint aboveScreenPosition = CGPointMake(object.position.x, screenSize.height - object.position.y);
int rotations = (CCRANDOM_0_1()*3) * 360;
float duration = (CCRANDOM_0_1()*5.0f) + 8.0f;
CCMoveTo* move = [CCMoveTo actionWithDuration:obstacleMoveDuration position:aboveScreenPosition];
CCRotateTo* rotate = [CCRotateBy actionWithDuration:duration angle:rotations];
CCSpawn* moveRotate = [CCSpawn actions: move, rotate, nil];
CCCallFuncN* call = [CCCallFuncN actionWithTarget:self selector:#selector(objectAboveScreen:)];
CCSequence* sequence = [CCSequence actions:moveRotate, call, nil];
[object runAction:sequence];
if (time >= (delta + 3)) {
fastPowerUp = NO;
}
}
-(void) objectAboveScreen:(id) sender{
//make sure sender is actually of the right class
NSAssert([sender isKindOfClass:[CCSprite class]], #"sender is not a CCSprite!");
CCSprite* obstacle = (CCSprite*)sender;
//move the back to the bottom of the screen
CGPoint pos = obstacle.position;
CGSize screenSize = [[CCDirector sharedDirector]winSize];
pos.y = (-screenSize.height - [obstacle texture].contentSize.height);
pos.x = CCRANDOM_0_1() * screenSize.width;
obstacle.position = pos;
}
I really just don't know where to go from here... Should I make the powerUps a different class? If so, how would I implement something like this? I really hate trying to ask for someone to solve my question, but I really just can't rack my brain around this and I'm rather new... if it were explained to me, then I know I would be able to implement it in future games on my own...
Thanks in advance, and let me know if more information is needed...
I'd do something like
in the .h file
float speedModifier;
-(void)resetPowerUp;
in the .m
-(void)resetPowerUp
{
speedModifier = 1;
}
wherever you are initializing the level
[self resetPowerUp];
upon collision with powerup:
speedModifier = 2;
[self performSelector:#selector(resetPowerUp) withObject:nil afterDelay:5];
then wherever you are moving whatever it is which speed should be effected by the powerup mode, multiply the speed of the animation (or divide the duration it takes for it to get wherever it's going) by speedModified
hope that helps
i am new in iphone,i am using horizontal scrollview these are horizontally scrolling properly
but i want these horizontally scrolling on previous and next button action.please help me asap.
Thanks:)
Here are the steps that was worked for me
in .h file write following code.
int scrollMove;
UIScrollView *aScrView;
-(IBAction)nextBtnAction:(id)sender;
-(IBAction)previousBtnAction:(id)sender;
in .m file
- (void)viewDidLoad
{
[super viewDidLoad];
scrollMove=50;
aScrView=[[UIScrollView alloc]init];
aScrView.frame=CGRectMake(25,50, 270, 50);
aScrView.delegate=self;
aScrView.contentSize=CGSizeMake(1200, 0);
[self.view addSubview:aScrView];
// Do any additional setup after loading the view from its nib.
}
-(IBAction)nextBtnAction:(id)sender{
float coordinate = 2.0f;
//scrollMove=50;
[aScrView setContentOffset:CGPointMake(scrollMove * coordinate, 0) animated:YES];
scrollMove=scrollMove+50;
}
-(IBAction)previousBtnAction:(id)sender{
float coordinate = 1.0f;
[aScrView setContentOffset:CGPointMake(scrollMove * coordinate, 0) animated:YES];
scrollMove=scrollMove-50;
}
Hope this helps you!!!
Use this one when you want to go on next visible content of Scroll view:
(Put this one inside next button method)
float coordinate = 2.0f;
[nodeScrollView setContentOffset:CGPointMake(460 * coordinate, 0) animated:YES];
move on previous visible content view:
float coordinate = 1.0f;
[nodeScrollView setContentOffset:CGPointMake(460 * coordinate, 0) animated:YES];
(Put this one inside previous button method)
**You need some mathematical calculation with coordinate
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageNumberYouWantToGoTo;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:YES];
- (void)viewDidLoad
{
x=0;
y=320;
}
- (IBAction)next:(id)sender
{
y=320;
[scrl_Knot setContentOffset:CGPointMake(x+320,0) animated:YES];
x+=320;
}
- (IBAction)pre:(id)sender
{
x=0;
[scrl_Knot setContentOffset:CGPointMake(y,0) animated:YES];
y-=320;
}
- (IBAction)pre:(id)sender
{
btnNext.enabled = TRUE;
imageID--;
[scrl_venuelist setContentOffset:CGPointMake(imageID*273, 0) animated:YES];
pagecontrol.currentPage=imageID;
if(imageID <= 0)
{
btnPrevious.enabled = FALSE;
}
}
- (IBAction)next:(id)sender
{
btnPrevious.enabled = TRUE;
imageID++;
[scrl_venuelist setContentOffset:CGPointMake(imageID*273, 0) animated:YES];
pagecontrol.currentPage=imageID;
if(imageID >= imageArr.count-1)
{
btnNext.enabled = FALSE;
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
{
imageID = scrollView.contentOffset.x / scrollView.frame.size.width;
pagecontrol.currentPage=imageID;
if(imageID <= 0)
{
btnPrevious.enabled = FALSE;
imageID = 0;
}
else
{
btnPrevious.enabled = TRUE;
}
if(imageID >= imageArr.count-1)
{
btnNext.enabled = FALSE;
imageID = imageArr.count-1;
}
else
{
btnNext.enabled = TRUE;
}
}
- (void)viewDidLoad
{
scrl_venuelist.delegate=self;
scrl_venuelist.contentSize =CGSizeMake(273 * [imageArr count], 137);
if(imageID == 0)
{
btnPrevious.enabled = FALSE;
if(imageID == imageArr.count-1)
{
btnNext.enabled = FALSE;
}
}
else if(imageID == imageArr.count-1)
{
btnNext.enabled = FALSE;
}
else if(imageID > imageArr.count-1)
{
imageID = [imageArr count]-1;
}
scrl_venuelist.pagingEnabled=YES;
pagecontrol.numberOfPages=[imageArr count];
pagecontrol.currentPage=0;
}
If Any one Looking for way of doing in C# for Xamarin
public int CurrentIndex
{
get => (int)Math.Round(this.scrollView.ContentOffset.X / this.scrollView.Frame.Width);
set => this.scrollView.ScrollRectToVisible(new CGRect(new CGPoint(this.scrollView.Frame.Size.Width * value, 0), this.scrollView.Frame.Size), true);
}
This getter and Setter should provide you current page as well let you scroll to the specified page
On Button Actions Just Update Current Index to new Values, Which could current Index +1/-1.
I'm displaying a series of images in a UIScrollView. I pretty much want to replicate the Photos application.
My current architecture is:
A parent UIScrollView with content size that is wide enough for x number of pages + some extra space for margins in between the images.
Each image is contained in a UIImageView.
Each UIImageView is contained within its own UIScrollview which are then the subviews of the parent UIScrollView.
So I basically have a row of UIScrollViews within a parent UIScrollView.
The parent UIScrollView has paging enabled so I can scroll from page to page without any problems.
The problem is how to seamlessly pan around a zoomed-in image. I have overridden the viewForZoomingInScrollView method to return the appropriate UIImageView when the user pinches in/out. I have overriden the scrollViewDidEndZooming method to set the parent view's canCancelContentTouches property to NO if the zoom scale is greater than 1.
So users are able to pan around an image. However, they must hold their finger down for a moment to get past the small delay the parent scroll view has before sending touch events down to the subviews. Also, once the user is panning in one image, the next/prev images do not enter the viewable area when the user has reached the border of the current image.
Any ideas?
Thanks.
Yay! I tried to approach the problem with just one UIScrollView, and I think I found a solution.
Before the user starts zooming (in viewForZoomingInScrollView:), I switch the scroll view into zooming mode (remove all additional pages, reset content size and offset). When the user zooms out to scale 1.00 (in scrollViewDidEndZooming:withView:atScale:), I switch back to paging view (add all pages back, adjust content size and offset).
Here's the code of a simple view controller that does just that. This sample switches between, zooms and pans three large UIImageViews.
Note that a single view controller with a handful of functions is all it takes, no need to subclass UIScrollView or something.
typedef enum {
ScrollViewModeNotInitialized, // view has just been loaded
ScrollViewModePaging, // fully zoomed out, swiping enabled
ScrollViewModeZooming, // zoomed in, panning enabled
ScrollViewModeAnimatingFullZoomOut, // fully zoomed out, animations not yet finished
ScrollViewModeInTransition, // during the call to setPagingMode to ignore scrollViewDidScroll events
} ScrollViewMode;
#interface ScrollingMadnessViewController : UIViewController <UIScrollViewDelegate> {
UIScrollView *scrollView;
NSArray *pageViews;
NSUInteger currentPage;
ScrollViewMode scrollViewMode;
}
#end
#implementation ScrollingMadnessViewController
- (void)setPagingMode {
NSLog(#"setPagingMode");
if (scrollViewMode != ScrollViewModeAnimatingFullZoomOut && scrollViewMode != ScrollViewModeNotInitialized)
return; // setPagingMode is called after a delay, so something might have changed since it was scheduled
scrollViewMode = ScrollViewModeInTransition; // to ignore scrollViewDidScroll when setting contentOffset
// reposition pages side by side, add them back to the view
CGSize pageSize = scrollView.frame.size;
NSUInteger page = 0;
for (UIView *view in pageViews) {
if (!view.superview)
[scrollView addSubview:view];
view.frame = CGRectMake(pageSize.width * page++, 0, pageSize.width, pageSize.height);
}
scrollView.pagingEnabled = YES;
scrollView.showsVerticalScrollIndicator = scrollView.showsHorizontalScrollIndicator = NO;
scrollView.contentSize = CGSizeMake(pageSize.width * [pageViews count], pageSize.height);
scrollView.contentOffset = CGPointMake(pageSize.width * currentPage, 0);
scrollViewMode = ScrollViewModePaging;
}
- (void)setZoomingMode {
NSLog(#"setZoomingMode");
scrollViewMode = ScrollViewModeInTransition; // to ignore scrollViewDidScroll when setting contentOffset
CGSize pageSize = scrollView.frame.size;
// hide all pages besides the current one
NSUInteger page = 0;
for (UIView *view in pageViews)
if (currentPage != page++)
[view removeFromSuperview];
// move the current page to (0, 0), as if no other pages ever existed
[[pageViews objectAtIndex:currentPage] setFrame:CGRectMake(0, 0, pageSize.width, pageSize.height)];
scrollView.pagingEnabled = NO;
scrollView.showsVerticalScrollIndicator = scrollView.showsHorizontalScrollIndicator = YES;
scrollView.contentSize = pageSize;
scrollView.contentOffset = CGPointZero;
scrollViewMode = ScrollViewModeZooming;
}
- (void)loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
scrollView = [[UIScrollView alloc] initWithFrame:frame];
scrollView.delegate = self;
scrollView.maximumZoomScale = 2.0f;
scrollView.minimumZoomScale = 1.0f;
UIImageView *imageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"red.png"]];
UIImageView *imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"green.png"]];
UIImageView *imageView3 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"yellow-blue.png"]];
// in a real app, you most likely want to have an array of view controllers, not views;
// also should be instantiating those views and view controllers lazily
pageViews = [[NSArray alloc] initWithObjects:imageView1, imageView2, imageView3, nil];
self.view = scrollView;
}
- (void)setCurrentPage:(NSUInteger)page {
if (page == currentPage)
return;
currentPage = page;
// in a real app, this would be a good place to instantiate more view controllers -- see SDK examples
}
- (void)viewDidLoad {
scrollViewMode = ScrollViewModeNotInitialized;
[self setPagingMode];
}
- (void)viewDidUnload {
[pageViews release]; // need to release all page views here; our array is created in loadView, so just releasing it
pageViews = nil;
}
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(setPagingMode) object:nil];
CGPoint offset = scrollView.contentOffset;
NSLog(#"scrollViewDidScroll: (%f, %f)", offset.x, offset.y);
if (scrollViewMode == ScrollViewModeAnimatingFullZoomOut && ABS(offset.x) < 1e-5 && ABS(offset.y) < 1e-5)
// bouncing is still possible (and actually happened for me), so wait a bit more to be sure
[self performSelector:#selector(setPagingMode) withObject:nil afterDelay:0.1];
else if (scrollViewMode == ScrollViewModePaging)
[self setCurrentPage:roundf(scrollView.contentOffset.x / scrollView.frame.size.width)];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)aScrollView {
if (scrollViewMode != ScrollViewModeZooming)
[self setZoomingMode];
return [pageViews objectAtIndex:currentPage];
}
- (void)scrollViewDidEndZooming:(UIScrollView *)aScrollView withView:(UIView *)view atScale:(float)scale {
NSLog(#"scrollViewDidEndZooming: scale = %f", scale);
if (fabsf(scale - 1.0) < 1e-5) {
if (scrollView.zoomBouncing)
NSLog(#"scrollViewDidEndZooming, but zoomBouncing is still true!");
// cannot call setPagingMode now because scrollView will bounce after a call to this method, resetting contentOffset to (0, 0)
scrollViewMode = ScrollViewModeAnimatingFullZoomOut;
// however sometimes bouncing will not take place
[self performSelector:#selector(setPagingMode) withObject:nil afterDelay:0.2];
}
}
#end
Runnable sample project is available at http://github.com/andreyvit/ScrollingMadness/ (if you don't use Git, just click Download button there). A README is available there, explaining why the code was written the way it is.
(The sample project also illustrates how to zoom a scroll view programmatically, and has ZoomScrollView class that encapsulates a solution to that. It is a neat class, but is not required for this trick. If you want an example that does not use ZoomScrollView, go back a few commits in commit history.)
P.S. For the sake of completeness, there's TTScrollView — UIScrollView reimplemented from scratch. It's part of the great and famous Three20 library. I don't like how it feels to the user, but it does make implementing paging/scrolling/zooming dead simple.
P.P.S. The real Photo app by Apple has pre-SDK code and uses pre-SDK classes. One can spot two classes derived from pre-SDK variant of UIScrollView inside PhotoLibrary framework, however it is not immediately clear what they do (and they do quite a lot). I can easily believe this effect used to be harder to achieve in pre-SDK times.
I had to do a similar setup, but I basically custom wrote the whole thing. I'm not sure how you're going to get around the problem of 'handing off' touch events from the child UIScrollView to the parent UISscrollView when you reach the edge. You might try overriding UITouchesBegan:withEvent: in your parent UIScrollView, and dumping directly to the child. Good luck!
This is the Andrey's code with c# translation for monotouch developers...
First you need to edit your xib file.. Insert 3 view controllers and make outlets like _page1, _page2, _mainpage.. and link these outlets with views. note you must referance to view controller's view outlet with _mainpage view. (sorry for my english)
public partial class Test_Details_Controller : UIViewController
{
private UIPageControl _pageCont;
private UIScrollView _scView;
private Object[] _pageViews;
private int _currentPageIndex;
private bool _rotationInProgress;
void InitializeAfterLoad ()
{
this.Title = "Test";
this._pageCont = CreatePageControll();
}
private UIPageControl CreatePageControll()
{
UIPageControl pageControll = new UIPageControl( new RectangleF( 146,348, 38, 20 ) );
pageControll.BackgroundColor = UIColor.Red;
pageControll.Alpha = 0.7f;
return pageControll;
}
private void UpdatePageControll(UIPageControl cont, int current, int pages, UIView showed)
{
cont.CurrentPage = current;
cont.Pages = pages;
cont.UpdateCurrentPageDisplay();
UIPageControl.AnimationsEnabled = true;
UIPageControl.BeginAnimations(string.Empty, this.Handle);
cont.Frame = new RectangleF(showed.Frame.Location.X
, cont.Frame.Location.Y , pageSize().Width, cont.Frame.Height);
UIPageControl.CommitAnimations();
}
private UIView loadViewForPage(int pageIndex){
UIView _view = null;
switch ( pageIndex ) {
case 1:
_view = this._page1;
break;
case 2:
_view = this._page2;
break;
default:
_view = this._page1;
break;
}
return _view;
}
private int numberOfPages(){
return (int)this._pageViews.Count();
}
private UIView viewForPage( int pageIndex ){
UIView pageView;
if(this._pageViews.ElementAt( pageIndex ) == null)
{
pageView = loadViewForPage( pageIndex );
_pageViews[ pageIndex ] = pageView;
}
else{
pageView = (UIView)_pageViews[ pageIndex ];
}
_scView.AddSubview( pageView );
return pageView;
}
private SizeF pageSize(){
return this._scView.Frame.Size;
}
private bool isPageLoaded( int pageIndex ){
return this._pageViews.ElementAt( pageIndex ) != null;
}
private void layoutPage( int pageIndex ){
SizeF pageSize = this.pageSize();
((UIView)this._pageViews[pageIndex]).Frame = new RectangleF( pageIndex * pageSize.Width,0, pageSize.Width, pageSize.Height );
this.viewForPage( pageIndex );
}
private void loadView(){
this._scView = new UIScrollView();
this._scView.Delegate = new ScViewDelegate( this );
this._scView.PagingEnabled = true;
this._scView.ShowsHorizontalScrollIndicator = false;
this._scView.ShowsVerticalScrollIndicator = false;
this._scView.Layer.BorderWidth = 2;
this._scView.AddSubview( _pageCont );
this.View = this._scView;
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
InitializeAfterLoad ();
this._pageViews = new Object[]{ _page1, _page2 };
this.loadView();
}
private void currentPageIndexDidChange(){
this.layoutPage( _currentPageIndex );
if(_currentPageIndex+1 < this.numberOfPages()){
this.layoutPage( _currentPageIndex + 1 );
}
if(_currentPageIndex >0){
this.layoutPage( _currentPageIndex - 1 );
}
this.UpdatePageControll( _pageCont, _currentPageIndex, this.numberOfPages(), ((UIView)this._pageViews[_currentPageIndex]) );
this._scView.BringSubviewToFront( _pageCont );
this.NavigationController.Title = string.Format( "{0} of {1}", _currentPageIndex + 1, this.numberOfPages() );
}
private void layoutPages(){
SizeF pageSize = this.pageSize();
this._scView.ContentSize = new SizeF( this.numberOfPages() * pageSize.Width, pageSize.Height );
// move all visible pages to their places, because otherwise they may overlap
for (int pageIndex = 0; pageIndex < this.numberOfPages(); pageIndex++) {
if(this.isPageLoaded( pageIndex ))
this.layoutPage( pageIndex );
}
}
public override void ViewWillAppear (bool animated)
{
this.layoutPages();
this.currentPageIndexDidChange();
this._scView.ContentOffset = new PointF( _currentPageIndex * this.pageSize().Width, 0 );
}
class ScViewDelegate : UIScrollViewDelegate
{
Test_Details_Controller id;
public ScViewDelegate ( Test_Details_Controller id )
{
this.id = id;
}
public override void Scrolled (UIScrollView scrollView)
{
if(id._rotationInProgress)
return;// UIScrollView layoutSubviews code adjusts contentOffset, breaking our logic
SizeF pageSize = id.pageSize();
int newPageIndex = ((int)id._scView.ContentOffset.X + (int)pageSize.Width / 2) / (int)pageSize.Width;
if( newPageIndex == id._currentPageIndex )
return;
id._currentPageIndex = newPageIndex;
id.currentPageIndexDidChange();
}
}
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
return toInterfaceOrientation != UIInterfaceOrientation.PortraitUpsideDown;
}
public override void WillRotate (UIInterfaceOrientation toInterfaceOrientation, double duration)
{
_rotationInProgress = true;
// hide other page views because they may overlap the current page during animation
for (int pageIndex = 0; pageIndex < this.numberOfPages(); pageIndex++) {
if(this.isPageLoaded( pageIndex ))
this.viewForPage( pageIndex ).Hidden = ( pageIndex != _currentPageIndex );
}
}
public override void WillAnimateRotation (UIInterfaceOrientation toInterfaceOrientation, double duration)
{
// resize and reposition the page view, but use the current contentOffset as page origin
// (note that the scrollview has already been resized by the time this method is called)
SizeF pageSize = this.pageSize();
UIView pageView = this.viewForPage( _currentPageIndex );
this.viewForPage( _currentPageIndex ).Frame = new RectangleF( this._scView.ContentOffset.X, 0, pageSize.Width, pageSize.Height );
}
public override void DidRotate (UIInterfaceOrientation fromInterfaceOrientation)
{
base.DidRotate (fromInterfaceOrientation);
// adjust frames according to the new page size - this does not cause any visible changes
this.layoutPages();
this._scView.ContentOffset = new PointF( _currentPageIndex * this.pageSize().Width, 0 );
//unhide
for (int pageIndex = 0; pageIndex < this.numberOfPages(); pageIndex++) {
if( this.isPageLoaded( pageIndex ) )
this.viewForPage( pageIndex ).Hidden = false;
}
_rotationInProgress = false;
}
public override void DidReceiveMemoryWarning ()
{
//SuperHandle = DidReceiveMemoryWarning();
if(this._pageViews != null)
{
// unload non-visible pages in case the memory is scarse
for (int pageIndex = 0; pageIndex < this.numberOfPages(); pageIndex++) {
if( pageIndex < _currentPageIndex - 1 || pageIndex > _currentPageIndex + 1 )
if( this.isPageLoaded(pageIndex) ){
UIView pageview = (UIView)this._pageViews[ pageIndex ];
this._pageViews[ pageIndex ] = null;
pageview.RemoveFromSuperview();
}
}
}
}
public override void ViewDidUnload ()
{
this._pageViews = null;
this._scView = null;
}
}
I have a sample project and it works perfect.
https://code.google.com/p/uiscrollview-touch-events/