ifish/Ifish/controllers/IfishYooseeFile/IfishYooseeHelper/KTPhotoBrowser/KTThumbsView.m

194 lines
5.9 KiB
Objective-C

//
// KTThumbsView.m
// Sample
//
// Created by Kirby Turner on 3/23/10.
// Copyright 2010 White Peak Software Inc. All rights reserved.
//
#import "KTThumbsView.h"
#import "KTThumbView.h"
#import "KTThumbsViewController.h"
@implementation KTThumbsView
@synthesize dataSource = dataSource_;
@synthesize controller = controller_;
@synthesize thumbsHaveBorder = thumbsHaveBorder_;
@synthesize thumbsPerRow = thumbsPerRow_;
@synthesize thumbSize = thumbSize_;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Set default values.
thumbsHaveBorder_ = YES;
thumbsPerRow_ = NSIntegerMin; // Forces caluation because on view size.
thumbSize_ = CGSizeMake(75, 75);
// We keep a collection of reusable thumbnail
// views. This improves performance by not
// requiring a create view each and every time.
reusableThumbViews_ = [[NSMutableSet alloc] init];
// No thumbnail views are visible at first; note this by
// making the firsts very high and the lasts very low
firstVisibleIndex_ = NSIntegerMax;
lastVisibleIndex_ = NSIntegerMin;
lastItemsPerRow_ = NSIntegerMin;
}
return self;
}
- (KTThumbView *)dequeueReusableThumbView
{
KTThumbView *thumbView = [reusableThumbViews_ anyObject];
if (thumbView != nil) {
// The only object retaining the view is the
// reusableThumbViews set, so we retain/autorelease
// it before returning it so that it's not immediately
// deallocated when removed form the set.
[reusableThumbViews_ removeObject:thumbView];
}
return thumbView;
}
- (void)queueReusableThumbViews
{
for (UIView *view in [self subviews]) {
if ([view isKindOfClass:[KTThumbView class]]) {
[reusableThumbViews_ addObject:view];
[view removeFromSuperview];
}
}
firstVisibleIndex_ = NSIntegerMax;
lastVisibleIndex_ = NSIntegerMin;
}
- (void)reloadData
{
[self queueReusableThumbViews];
[self setNeedsLayout];
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect visibleBounds = [self bounds];
int visibleWidth = visibleBounds.size.width;
int visibleHeight = visibleBounds.size.height;
// Do a bunch of math to determine which rows and colums
// are visible.
NSInteger itemsPerRow = thumbsPerRow_;
if (itemsPerRow == NSIntegerMin) {
itemsPerRow = floor(visibleWidth / thumbSize_.width);
}
if (itemsPerRow != lastItemsPerRow_) {
// Force re-load of grid cells. Unfortunately this means
// visible cells will reload, which can hurt performance
// when the thumbnail image isn't cached. Need to find a
// better approach.
[self queueReusableThumbViews];
}
lastItemsPerRow_ = itemsPerRow;
// Ensure a minimum of space between images.
int minimumSpace = 5;
if (visibleWidth - itemsPerRow * thumbSize_.width < minimumSpace) {
itemsPerRow--;
}
if (itemsPerRow < 1) itemsPerRow = 1; // Ensure at least one per row.
int spaceWidth = round((visibleWidth - thumbSize_.width * itemsPerRow) / (itemsPerRow + 1));
int spaceHeight = spaceWidth;
// Calculate content size.
NSInteger thumbCount = [dataSource_ thumbsViewNumberOfThumbs:self];
int rowCount = ceil(thumbCount / (float)itemsPerRow);
int rowHeight = thumbSize_.height + spaceHeight;
CGSize contentSize = CGSizeMake(visibleWidth, (rowHeight * rowCount + spaceHeight));
[self setContentSize:contentSize];
NSInteger rowsPerView = visibleHeight / rowHeight;
NSInteger topRow = MAX(0, floorf(visibleBounds.origin.y / rowHeight));
NSInteger bottomRow = topRow + rowsPerView;
CGRect extendedVisibleBounds = CGRectMake(visibleBounds.origin.x, MAX(0, visibleBounds.origin.y), visibleBounds.size.width, visibleBounds.size.height + rowHeight);
// Recycle all thumb views that are no longer visible
for (UIView *view in [self subviews]) {
if ([view isKindOfClass:[KTThumbView class]]) {
// We want to see if the view intersect the scrollView's
// bounds, so we need to convert their frames to our own
// coordinate system.
CGRect viewFrame = [view frame];
// If the view doesn't intersect, it's not visible, so we can recycle it
if (! CGRectIntersectsRect(viewFrame, extendedVisibleBounds)) {
[reusableThumbViews_ addObject:view];
[view removeFromSuperview];
}
}
}
NSInteger startAtIndex = MAX(0, topRow * itemsPerRow);
NSInteger stopAtIndex = MIN(thumbCount, (bottomRow * itemsPerRow) + itemsPerRow);
// Set our initial origin.
int x = spaceWidth;
NSInteger y = spaceHeight + (topRow * rowHeight);
// Iterate through the needed thumbnail views adding
// any views that are missing.
for (NSInteger index = startAtIndex; index < stopAtIndex; index++) {
// If index is between first and last, then not missing.
BOOL isThumbViewMissing = !(index >= firstVisibleIndex_ && index < lastVisibleIndex_);
if (isThumbViewMissing) {
KTThumbView *thumbView = [dataSource_ thumbsView:self thumbForIndex:index];
// Set the frame so the view is inserted into the correct position.
CGRect newFrame = CGRectMake(x, y, thumbSize_.width, thumbSize_.height);
[thumbView setFrame:newFrame];
// Store the current index so the thumb view can
// find it later.
[thumbView setTag:index];
[thumbView setHasBorder:thumbsHaveBorder_];
[self addSubview:thumbView];
}
// Adjust the position.
if ( (index+1) % itemsPerRow == 0) {
// Start new row.
x = spaceWidth;
y += thumbSize_.height + spaceHeight;
} else {
x += thumbSize_.width + spaceWidth;
}
}
// Remember which thumb view indexes are visible.
firstVisibleIndex_ = startAtIndex;
lastVisibleIndex_ = stopAtIndex;
}
@end