Orion98MC / MCCGalleryViewDemo

A Demo XCode project for MCCGalleryView (a ScrollView gallery for iOS 4+)
2 stars 0 forks source link

Adaption for programmatic scrolling #1

Closed mindbrix closed 12 years ago

mindbrix commented 12 years ago

Hi Thierry. Thank you for sharing your code. I have a project where I need to programatically scroll to any page. To test your gallery code I updated the demo single tap handler to this:

gv.onSingleTap = ^{ gv.contentOffset = CGPointMake( gv.contentOffset.x + gv.bounds.size.width * 5.0f, 0.0f ); };

However, this causes the following crash:

2012-10-14 16:21:05.176 MCCGalleryViewDemo[3950:11303] * Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection <__NSDictionaryM: 0x7597bb0> was mutated while being enumerated.' *\ First throw call stack: (0x1ca1012 0x10dee7e 0x1d29cc5 0x1c8eca8 0x1c8e87d 0x1c8e7c5 0x843c 0x45d7 0x433d 0x67a9 0x7dce3 0x3a87 0x7461 0xb4a02a 0xb27b90 0x1c60376 0x1c5fe06 0x1c47a82 0x1c46f44 0x1c46e1b 0x1bfb7e3 0x1bfb668 0x2665c 0x211a 0x2025 0x1) libc++abi.dylib: terminate called throwing an exception

Is the cache handler built on the assumption of a single page change at a time?

Many thanks, Nigel.

Orion98MC commented 12 years ago

Hi Nigel,

The onSingleTap you wrote should work. At least on the iOS6 Simulator it works. Did you also change the onVisibleRangeChanged handler?

Regards, Thierry

Le 14 oct. 2012 à 17:30, Nigel Timothy Barber notifications@github.com a écrit :

gv.onSingleTap = ^{ gv.contentOffset = CGPointMake( gv.contentOffset.x + gv.bounds.size.width * 5.0f, 0.0f ); };

mindbrix commented 12 years ago

Hi Thierry. It crashes for me on the iOS6 simulator. Reading the crash report again, it looks like the NSMutableDictionary objects in MCCIndexedCache are being accessed by multiple threads simultaneously.

I have not changed the onVisibleRangeChanged handler. Should I?

Best, Nigel.

Orion98MC commented 12 years ago

Nigel,

The gallery should not be created on a custom thread (like any UI element). Your crash log shows a thread id 11303 but it may not be the main thread. Please check that you setup the gallery and handlers on the main thread.

Thierry

Le 14 oct. 2012 à 19:49, Nigel Timothy Barber notifications@github.com a écrit :

Hi Thierry. It crashes for me on the iOS6 simulator. Reading the crash report again, it looks like the NSMutableDictionary objects in MCCIndexedCache are being accessed by multiple threads simultaneously.

I have not changed the onVisibleRangeChanged handler. Should I?

Best, Nigel.

— Reply to this email directly or view it on GitHub.

mindbrix commented 12 years ago

Hi Thierry. I only changed 3 lines of MCCViewController.m.

Line 45. gv.pagesCount = 37;

Line 65. UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"%u.jpg", pageIndex % 7 ]];

Line 114. gv.onSingleTap = ^{ gv.contentOffset = CGPointMake( gv.contentOffset.x + gv.bounds.size.width * 5.0f, 0.0f ); };

Best, Nigel.

Orion98MC commented 12 years ago

Nigel,

This is strange. Does the demo run when no changes are made?

Can you change the onSingleTap to to:

gv.onSingleTap = ^{ NSLog(@"%@", gv); [gv setContentOffset:CGPointMake( gv.contentOffset.x + gv.bounds.size.width * 1.0f, 0.0f ) animated:YES]; };

Send me any crash log

Regards, Thierry

Le 14 oct. 2012 à 20:56, Nigel Timothy Barber notifications@github.com a écrit :

Hi Thierry. I only changed 3 lines of MCCViewController.m.

Line 45. gv.pagesCount = 37;

Line 65. UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"%u.jpg", pageIndex % 7 ]];

Line 114. gv.onSingleTap = ^{ gv.contentOffset = CGPointMake( gv.contentOffset.x + gv.bounds.size.width * 5.0f, 0.0f ); };

Best, Nigel.

— Reply to this email directly or view it on GitHub.

mindbrix commented 12 years ago

Hi Thierry. The demo works fine out of the box. That change works too. It only breaks on multi-page jumps. Can you make it crash with this?

gv.onSingleTap = ^{ gv.contentOffset = CGPointMake( gv.contentOffset.x + gv.bounds.size.width * 5.0f, 0.0f ); };

Best, Nigel.

mindbrix commented 12 years ago

I'm on Xcode 4.5 BTW.

Orion98MC commented 12 years ago

Nigel,

I was able to reproduce your issue. The problem lies in the IndexedCache recycling enumeration and I've been able to fix it. I will post an update on github on tuesday as I am off and AFK until then.

Thank for your feedback. Thierry

Le 14 oct. 2012 à 21:21, Nigel Timothy Barber notifications@github.com a écrit :

I'm on Xcode 4.5 BTW.

— Reply to this email directly or view it on GitHub.

mindbrix commented 12 years ago

Hi Thierry. Thanks for the very quick fix.

Best, Nigel.

Orion98MC commented 12 years ago

Nigel,

Please update the submodule to check if the fix is working for you. You may need to checkout the master branch in the submodule directory.

Let me know if you succeed. Thierry

mindbrix commented 12 years ago

Hi Thierry. That fix works for me. Many thanks for your quick response.

Nigel.

Orion98MC commented 12 years ago

Great, you're welcome. I'll update the Demo soon to bind the new MCCGalleryView commit.

Are you planing on using MCCGalleryView for your project ?

Thierry Le 16 oct. 2012 à 15:33, Nigel Timothy Barber notifications@github.com a écrit :

Hi Thierry. That fix works for me. Many thanks for your quick response.

Nigel.

— Reply to this email directly or view it on GitHub.

mindbrix commented 12 years ago

Hi Thierry. I was investigating the gallery, but I've switched to using this method, called from scrollViewDidScroll. It works remarkably well. I love geometry, which is why I used CGRect rather than NSRange. The thumbnail cache uses SDImageCache from https://github.com/rs/SDWebImage.

Best, Nigel.

-(void)syncThumbnails:(NSArray *)thumbnails forScrollView:(UIScrollView *)scrollView
{
    for( int i = 0; i < thumbnails.count; i++ )
    {
    int thumbnailTag = STS_THUMBNAIL_TAG_BASE + i;
    CGRect thumbnailFrame = CGRectMake( i * scrollView.bounds.size.width, 0.0, scrollView.bounds.size.width, scrollView.bounds.size.height );
    UIImageView *thumbnailView = (UIImageView *)[ scrollView viewWithTag:thumbnailTag ];

    /* Thumbnail view not in order?
     */
    if( thumbnailView && !CGRectEqualToRect( thumbnailView.frame, thumbnailFrame ))
    {
        [ thumbnailView removeFromSuperview ];
        thumbnailView = nil;
    }

    /* Create a thumbnail view if necessary
     */
    if( !thumbnailView )
    {
        thumbnailView = [[ UIImageView alloc ] initWithFrame:thumbnailFrame ];
        thumbnailView.contentMode = UIViewContentModeScaleAspectFit;
        thumbnailView.userInteractionEnabled = NO;
        thumbnailView.tag = thumbnailTag;
        [ scrollView addSubview:thumbnailView ];
        [ thumbnailView release ];
    }

    /* Is the thumbnail in the visible frame?
     */
    CGRect visibleFrame = CGRectInset( scrollView.bounds, -scrollView.bounds.size.width, 0.0f );

    BOOL isVisible = CGRectIntersectsRect( thumbnailFrame, visibleFrame );

    if( isVisible  )
    {
        if( thumbnailView.image == nil )
        {
            NSURL *url = [ thumbnails objectAtIndex:i ];

            thumbnailView.image =  [ STSThumbnailView cachedThumbnailAtTime:0.0f atSize:thumbnailView.bounds.size forURL:url withScale:[ UIScreen mainScreen].scale ];
        }
    }
    else
    {
        thumbnailView.image = nil;
    }
}
}
Orion98MC commented 12 years ago

Nigel,

You can do that but it's not the same as using MCCGalleryView.

The gallery not only displays images of labels or whatever, but does handle rotations, caching of pages and preloading. If you were to display 10 big pictures you would quickly starve the idevice memory. MCCGalleryView handles that much like a UITableView does by reusing already built pages and load them on demand. You also gain from the preloading each time a page is loaded it also loads page + 1 and page -1 if not loaded so that the scrolling is smooth. Apart from that, well it's a scrollview.

Thierry

Le 16 oct. 2012 à 19:11, Nigel Timothy Barber notifications@github.com a écrit :

Hi Thierry. I was investigating the gallery, but I've switched to using this method, called from scrollViewDidScroll. It works remarkably well. I love geometry, which is why I used CGRect rather than NSRange. The thumbnail cache uses SDImageCache from https://github.com/rs/SDWebImage.

Best, Nigel.

-(void)syncThumbnails:(NSArray )thumbnails forScrollView:(UIScrollView )scrollView { for( int i = 0; i < thumbnails.count; i++ ) { int thumbnailTag = STS_THUMBNAIL_TAG_BASE + i; CGRect thumbnailFrame = CGRectMake( i * scrollView.bounds.size.width, 0.0, scrollView.bounds.size.width, scrollView.bounds.size.height ); UIImageView thumbnailView = (UIImageView )[ scrollView viewWithTag:thumbnailTag ];

/* Thumbnail view not in order?
 */
if( thumbnailView && !CGRectEqualToRect( thumbnailView.frame, thumbnailFrame ))
{
    [ thumbnailView removeFromSuperview ];
    thumbnailView = nil;
}

/* Create a thumbnail view if necessary
 */
if( !thumbnailView )
{
    thumbnailView = [[ UIImageView alloc ] initWithFrame:thumbnailFrame ];
    thumbnailView.contentMode = UIViewContentModeScaleAspectFit;
    thumbnailView.userInteractionEnabled = NO;
    thumbnailView.tag = thumbnailTag;
    [ scrollView addSubview:thumbnailView ];
    [ thumbnailView release ];
}

/* Is the thumbnail in the visible frame?
 */
CGRect visibleFrame = CGRectInset( scrollView.bounds, -scrollView.bounds.size.width, 0.0f );

BOOL isVisible = CGRectIntersectsRect( thumbnailFrame, visibleFrame );

if( isVisible  )
{
    if( thumbnailView.image == nil )
    {
        NSURL *url = [ thumbnails objectAtIndex:i ];

        thumbnailView.image =  [ STSThumbnailView cachedThumbnailAtTime:0.0f atSize:thumbnailView.bounds.size forURL:url withScale:[ UIScreen mainScreen].scale ];
    }
}
else
{
    thumbnailView.image = nil;
}

} } — Reply to this email directly or view it on GitHub.

mindbrix commented 12 years ago

Hi Thierry. That's almost exactly what syncThumbnails does, just in a different way which was easier to integrate into my existing project. Had I been starting from scratch I would have almost certainly used MCCGalleryView.

Best, Nigel.

On 16 October 2012 18:25, Thierry Passeron notifications@github.com wrote:

Nigel,

You can do that but it's not the same as using MCCGalleryView.

The gallery not only displays images of labels or whatever, but does handle rotations, caching of pages and preloading. If you were to display 10 big pictures you would quickly starve the idevice memory. MCCGalleryView handles that much like a UITableView does by reusing already built pages and load them on demand. You also gain from the preloading each time a page is loaded it also loads page + 1 and page -1 if not loaded so that the scrolling is smooth. Apart from that, well it's a scrollview.

Thierry

Le 16 oct. 2012 à 19:11, Nigel Timothy Barber notifications@github.com a écrit :

Hi Thierry. I was investigating the gallery, but I've switched to using this method, called from scrollViewDidScroll. It works remarkably well. I love geometry, which is why I used CGRect rather than NSRange. The thumbnail cache uses SDImageCache from https://github.com/rs/SDWebImage.

Best, Nigel.

-(void)syncThumbnails:(NSArray )thumbnails forScrollView:(UIScrollView )scrollView { for( int i = 0; i < thumbnails.count; i++ ) { int thumbnailTag = STS_THUMBNAIL_TAG_BASE + i; CGRect thumbnailFrame = CGRectMake( i * scrollView.bounds.size.width, 0.0, scrollView.bounds.size.width, scrollView.bounds.size.height ); UIImageView thumbnailView = (UIImageView )[ scrollView viewWithTag:thumbnailTag ];

/* Thumbnail view not in order? */ if( thumbnailView && !CGRectEqualToRect( thumbnailView.frame, thumbnailFrame )) { [ thumbnailView removeFromSuperview ]; thumbnailView = nil; }

/* Create a thumbnail view if necessary */ if( !thumbnailView ) { thumbnailView = [[ UIImageView alloc ] initWithFrame:thumbnailFrame ]; thumbnailView.contentMode = UIViewContentModeScaleAspectFit; thumbnailView.userInteractionEnabled = NO; thumbnailView.tag = thumbnailTag; [ scrollView addSubview:thumbnailView ]; [ thumbnailView release ]; }

/* Is the thumbnail in the visible frame? */ CGRect visibleFrame = CGRectInset( scrollView.bounds, -scrollView.bounds.size.width, 0.0f );

BOOL isVisible = CGRectIntersectsRect( thumbnailFrame, visibleFrame );

if( isVisible ) { if( thumbnailView.image == nil ) { NSURL *url = [ thumbnails objectAtIndex:i ];

thumbnailView.image = [ STSThumbnailView cachedThumbnailAtTime:0.0f atSize:thumbnailView.bounds.size forURL:url withScale:[ UIScreen mainScreen].scale ]; } } else { thumbnailView.image = nil; } } } — Reply to this email directly or view it on GitHub.

— Reply to this email directly or view it on GitHubhttps://github.com/Orion98MC/MCCGalleryViewDemo/issues/1#issuecomment-9498797.

vectoria.co.uk

concentrichron.com

Mindbrix -- Dream it, draw it, build it, love it

69 Derby Street Beeston Nottingham NG9 2LG

+44 7905 311 352 nigel@mindbrix.co.uk www.mindbrix.co.uk Skype: ntbarber twitter.com/mindbrix

Orion98MC commented 12 years ago

Nigel,

syncThumbnails does not reuse pages and does not handle rotations. Don't misunderstand me, it's not that it's not doable without any custom class to display a gallery of pictures but it's better to reuse small specialized objects. By using an object, you have better encapsulation, better readability and reuse. By using MCCGalleryView you don't need to subclass any scrollview. That's 2 files less!

As for easy integration, that's the mojo of MCCGalleryView… "easy to integrate".

MCCGalleryView gv = [[[MCCGalleryView alloc]initWithFrame:…]autorelease]; gv.pagesCount = N; gv.pageBlock = ^UIView(NSInteger index){ … } gv.recycleBlock = ^NSString(UIView page){ static NSString *identifier = @"Identifier"; return identifier; }

That's potentially 4 lines of code to setup a gallery in any view controller code.

Hope next time you will give it a shot and help us improve it.

Thierry

Le 16 oct. 2012 à 19:38, Nigel Timothy Barber notifications@github.com a écrit :

Hi Thierry. That's almost exactly what syncThumbnails does, just in a different way which was easier to integrate into my existing project. Had I been starting from scratch I would have almost certainly used MCCGalleryView.

Best, Nigel.

On 16 October 2012 18:25, Thierry Passeron notifications@github.com wrote:

Nigel,

You can do that but it's not the same as using MCCGalleryView.

The gallery not only displays images of labels or whatever, but does handle rotations, caching of pages and preloading. If you were to display 10 big pictures you would quickly starve the idevice memory. MCCGalleryView handles that much like a UITableView does by reusing already built pages and load them on demand. You also gain from the preloading each time a page is loaded it also loads page + 1 and page -1 if not loaded so that the scrolling is smooth. Apart from that, well it's a scrollview.

Thierry

Le 16 oct. 2012 à 19:11, Nigel Timothy Barber notifications@github.com a écrit :

Hi Thierry. I was investigating the gallery, but I've switched to using this method, called from scrollViewDidScroll. It works remarkably well. I love geometry, which is why I used CGRect rather than NSRange. The thumbnail cache uses SDImageCache from https://github.com/rs/SDWebImage.

Best, Nigel.

-(void)syncThumbnails:(NSArray )thumbnails forScrollView:(UIScrollView )scrollView { for( int i = 0; i < thumbnails.count; i++ ) { int thumbnailTag = STS_THUMBNAIL_TAG_BASE + i; CGRect thumbnailFrame = CGRectMake( i * scrollView.bounds.size.width, 0.0, scrollView.bounds.size.width, scrollView.bounds.size.height ); UIImageView thumbnailView = (UIImageView )[ scrollView viewWithTag:thumbnailTag ];

/* Thumbnail view not in order? */ if( thumbnailView && !CGRectEqualToRect( thumbnailView.frame, thumbnailFrame )) { [ thumbnailView removeFromSuperview ]; thumbnailView = nil; }

/* Create a thumbnail view if necessary */ if( !thumbnailView ) { thumbnailView = [[ UIImageView alloc ] initWithFrame:thumbnailFrame ]; thumbnailView.contentMode = UIViewContentModeScaleAspectFit; thumbnailView.userInteractionEnabled = NO; thumbnailView.tag = thumbnailTag; [ scrollView addSubview:thumbnailView ]; [ thumbnailView release ]; }

/* Is the thumbnail in the visible frame? */ CGRect visibleFrame = CGRectInset( scrollView.bounds, -scrollView.bounds.size.width, 0.0f );

BOOL isVisible = CGRectIntersectsRect( thumbnailFrame, visibleFrame );

if( isVisible ) { if( thumbnailView.image == nil ) { NSURL *url = [ thumbnails objectAtIndex:i ];

thumbnailView.image = [ STSThumbnailView cachedThumbnailAtTime:0.0f atSize:thumbnailView.bounds.size forURL:url withScale:[ UIScreen mainScreen].scale ]; } } else { thumbnailView.image = nil; } } } — Reply to this email directly or view it on GitHub.

— Reply to this email directly or view it on GitHubhttps://github.com/Orion98MC/MCCGalleryViewDemo/issues/1#issuecomment-9498797.

vectoria.co.uk

concentrichron.com

Mindbrix -- Dream it, draw it, build it, love it

69 Derby Street Beeston Nottingham NG9 2LG

+44 7905 311 352 nigel@mindbrix.co.uk www.mindbrix.co.uk Skype: ntbarber twitter.com/mindbrix — Reply to this email directly or view it on GitHub.

mindbrix commented 12 years ago

Hi Thierry. I like what you've done, it just didn't fit in with the project this time. Definitely one for the future though.

Best, Nigel.

On 16 October 2012 19:41, Thierry Passeron notifications@github.com wrote:

Nigel,

syncThumbnails does not reuse pages and does not handle rotations. Don't misunderstand me, it's not that it's not doable without any custom class to display a gallery of pictures but it's better to reuse small specialized objects. By using an object, you have better encapsulation, better readability and reuse. By using MCCGalleryView you don't need to subclass any scrollview. That's 2 files less!

As for easy integration, that's the mojo of MCCGalleryView… "easy to integrate".

MCCGalleryView gv = [[[MCCGalleryView alloc]initWithFrame:…]autorelease]; gv.pagesCount = N; gv.pageBlock = ^UIView(NSInteger index){ … } gv.recycleBlock = ^NSString(UIView page){ static NSString *identifier = @"Identifier"; return identifier; }

That's potentially 4 lines of code to setup a gallery in any view controller code.

Hope next time you will give it a shot and help us improve it.

Thierry

Le 16 oct. 2012 à 19:38, Nigel Timothy Barber notifications@github.com a écrit :

Hi Thierry. That's almost exactly what syncThumbnails does, just in a different way which was easier to integrate into my existing project. Had I been starting from scratch I would have almost certainly used MCCGalleryView.

Best, Nigel.

On 16 October 2012 18:25, Thierry Passeron notifications@github.com wrote:

Nigel,

You can do that but it's not the same as using MCCGalleryView.

The gallery not only displays images of labels or whatever, but does handle rotations, caching of pages and preloading. If you were to display 10 big pictures you would quickly starve the idevice memory. MCCGalleryView handles that much like a UITableView does by reusing already built pages and load them on demand. You also gain from the preloading each time a page is loaded it also loads page + 1 and page -1 if not loaded so that the scrolling is smooth. Apart from that, well it's a scrollview.

Thierry

Le 16 oct. 2012 à 19:11, Nigel Timothy Barber < notifications@github.com> a écrit :

Hi Thierry. I was investigating the gallery, but I've switched to using this method, called from scrollViewDidScroll. It works remarkably well. I love geometry, which is why I used CGRect rather than NSRange. The thumbnail cache uses SDImageCache from https://github.com/rs/SDWebImage.

Best, Nigel.

-(void)syncThumbnails:(NSArray )thumbnails forScrollView:(UIScrollView )scrollView { for( int i = 0; i < thumbnails.count; i++ ) { int thumbnailTag = STS_THUMBNAIL_TAG_BASE + i; CGRect thumbnailFrame = CGRectMake( i scrollView.bounds.size.width, 0.0, scrollView.bounds.size.width, scrollView.bounds.size.height ); UIImageView thumbnailView = (UIImageView *)[ scrollView viewWithTag:thumbnailTag ];

/* Thumbnail view not in order? */ if( thumbnailView && !CGRectEqualToRect( thumbnailView.frame, thumbnailFrame )) { [ thumbnailView removeFromSuperview ]; thumbnailView = nil; }

/* Create a thumbnail view if necessary */ if( !thumbnailView ) { thumbnailView = [[ UIImageView alloc ] initWithFrame:thumbnailFrame ]; thumbnailView.contentMode = UIViewContentModeScaleAspectFit; thumbnailView.userInteractionEnabled = NO; thumbnailView.tag = thumbnailTag; [ scrollView addSubview:thumbnailView ]; [ thumbnailView release ]; }

/* Is the thumbnail in the visible frame? */ CGRect visibleFrame = CGRectInset( scrollView.bounds, -scrollView.bounds.size.width, 0.0f );

BOOL isVisible = CGRectIntersectsRect( thumbnailFrame, visibleFrame );

if( isVisible ) { if( thumbnailView.image == nil ) { NSURL *url = [ thumbnails objectAtIndex:i ];

thumbnailView.image = [ STSThumbnailView cachedThumbnailAtTime:0.0f atSize:thumbnailView.bounds.size forURL:url withScale:[ UIScreen mainScreen].scale ]; } } else { thumbnailView.image = nil; } } } — Reply to this email directly or view it on GitHub.

— Reply to this email directly or view it on GitHub< https://github.com/Orion98MC/MCCGalleryViewDemo/issues/1#issuecomment-9498797>.

vectoria.co.uk

concentrichron.com

Mindbrix -- Dream it, draw it, build it, love it

69 Derby Street Beeston Nottingham NG9 2LG

+44 7905 311 352 nigel@mindbrix.co.uk www.mindbrix.co.uk Skype: ntbarber twitter.com/mindbrix — Reply to this email directly or view it on GitHub.

— Reply to this email directly or view it on GitHubhttps://github.com/Orion98MC/MCCGalleryViewDemo/issues/1#issuecomment-9501687.

vectoria.co.uk

concentrichron.com

Mindbrix -- Dream it, draw it, build it, love it

69 Derby Street Beeston Nottingham NG9 2LG

+44 7905 311 352 nigel@mindbrix.co.uk www.mindbrix.co.uk Skype: ntbarber twitter.com/mindbrix