使用自定义TTPhotoView扩展TTPhotoViewController

时间:2010-06-07 17:18:48

标签: iphone three20 ttphotoviewcontroller

我已成功将three20框架集成到我的项目中, 我已经扩展了TTPhotoViewController以进一步添加 功能。

现在我需要为TTPhotoView添加一些子视图 TTPhotoViewController。特别是我想添加该子视图 每个TTPhotoView加载后。这些子视图代表 图像上的敏感区域,因此它们应按比例缩放 图片。 用户可以点击子视图以获取有关图像的额外信息。

我不知道如何实现此行为。我应该扩展 TTPhotoView并确保TTPhotoViewController使用它 扩展版本而不是它的TTPhotoView?

有人能指出我正确的方向吗? 谢谢

3 个答案:

答案 0 :(得分:5)

解决了TTPhotoView(TapDetectingPhotoView)的子类,然后将我的所有子视图添加到该子类。 主要问题是照片切换,因为TTPhotoViewController(特别是其内部TTScrollView)在切换操作期间重用TTPhotoView。 因此,例如,如果将子视图添加到TTPhotoView子类并尝试切换到下一张照片,则子视图可能会在此处,因为TTPhotoView会重复使用。 为了解决这个问题,我决定每次发生照片切换时添加和删除所有子视图(请参阅TTPhotoViewController :: didMoveToPhoto)。 通过这种方式,我确信每个photoview都有其子视图。

这里是我的实施(只有非凡的方法) 希望这些帮助!

<强> PhotoViewController.h:

#import "TapDetectingPhotoView.h"


@interface PhotoGalleryController : TTPhotoViewController <TTScrollViewDelegate, TapDetectingPhotoViewDelegate> {

    NSArray *images;
}
@property (nonatomic, retain) NSArray *images;
@end

<强> PhotoViewController.m:

#import "PhotoGalleryController.h"

@implementation PhotoGalleryController
@synthesize images;

- (void)viewDidLoad { // fill self.images = ... }

- (TTPhotoView*)createPhotoView {
    TapDetectingPhotoView *photoView = [[TapDetectingPhotoView alloc] init];
    photoView.tappableAreaDelegate = self;

    return [photoView autorelease];
}

#pragma mark -
#pragma mark TTPhotoViewController

- (void)didMoveToPhoto:(id<TTPhoto>)photo fromPhoto:(id<TTPhoto>)fromPhoto {
    [super didMoveToPhoto:photo fromPhoto:fromPhoto];

    TapDetectingPhotoView *previousPhotoView = (TapDetectingPhotoView *)[_scrollView pageAtIndex:fromPhoto.index];
    TapDetectingPhotoView *currentPhotoView = (TapDetectingPhotoView *)[_scrollView pageAtIndex:photo.index];

    // destroy the sensible areas from the previous photoview, because the photo could be reused by the TTPhotoViewController!
    if (previousPhotoView)
        previousPhotoView.sensibleAreas = nil;

    // if sensible areas has not been already created, create new
    if (currentPhotoView && currentPhotoView.sensibleAreas == nil) {
        currentPhotoView.sensibleAreas = [[self.images objectAtIndex:photo.index] valueForKey:@"aMap"];
        [self showSensibleAreas:YES animated:YES];
    }
}


#pragma mark -
#pragma mark TappablePhotoViewDelegate

// show a detail view when a sensible area is tapped
- (void)tapDidOccurOnSensibleAreaWithId:(NSUInteger)ids {
    NSLog(@"SENSIBLE AREA TAPPED ids:%d", ids); 
    // ..push new view controller...
}

<强> TapDetectingPhotoView.h:

#import "SensibleAreaView.h"

@protocol TapDetectingPhotoViewDelegate;

@interface TapDetectingPhotoView : TTPhotoView <SensibleAreaViewDelegate> {
    NSArray *sensibleAreas;
    id <TapDetectingPhotoViewDelegate> tappableAreaDelegate;
}

@property (nonatomic, retain) NSArray *sensibleAreas;
@property (nonatomic, assign) id <TapDetectingPhotoViewDelegate> tappableAreaDelegate;
@end


@protocol TapDetectingPhotoViewDelegate <NSObject>
@required
- (void)tapDidOccurOnSensibleAreaWithId:(NSUInteger)ids;
@end

<强> TapDetectingPhotoView.m:

#import "TapDetectingPhotoView.h"


@interface TapDetectingPhotoView (Private)
- (void)createSensibleAreas;
@end


@implementation TapDetectingPhotoView

@synthesize sensibleAreas, tappableAreaDelegate;


- (id)init {
    return [self initWithSensibleAreas:nil];
}

- (id)initWithFrame:(CGRect)frame {
    return [self initWithSensibleAreas:nil];
}

// designated initializer
- (id)initWithSensibleAreas:(NSArray *)areasList {
    if (self = [super initWithFrame:CGRectZero]) {
        self.sensibleAreas = areasList;
        [self createSensibleAreas];
    }

    return self;
}

- (void)setSensibleAreas:(NSArray *)newSensibleAreas {
    if (newSensibleAreas != self.sensibleAreas) {
        // destroy previous sensible area and ensure that only sensible area's subviews are removed
        for (UIView *subview in self.subviews)
            if ([subview isMemberOfClass:[SensibleAreaView class]])
                [subview removeFromSuperview];

        [newSensibleAreas retain];
        [sensibleAreas release];
        sensibleAreas = newSensibleAreas;
        [self createSensibleAreas];
    }
}

- (void)createSensibleAreas {
    SensibleAreaView *area;
    NSNumber *areaID;
    for (NSDictionary *sensibleArea in self.sensibleAreas) {
        CGFloat x1 = [[sensibleArea objectForKey:@"nX1"] floatValue];
        CGFloat y1 = [[sensibleArea objectForKey:@"nY1"] floatValue];

        area = [[SensibleAreaView alloc] initWithFrame:
            CGRectMake(
                x1, y1, 
                [[sensibleArea objectForKey:@"nX2"] floatValue]-x1, [[sensibleArea objectForKey:@"nY2"] floatValue]-y1
            )
    ];

        areaID = [sensibleArea objectForKey:@"sId"];
        area.ids = [areaID unsignedIntegerValue]; // sensible area internal ID
        area.tag = [areaID integerValue];
        area.delegate = self;
        [self addSubview:area];
        [area release];
    }
}

// to make sure that if the zoom factor of the TTScrollView is > than 1.0 the subviews continue to respond to the tap events
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 
    UIView *result = nil;
    for (UIView *child in self.subviews) {
        CGPoint convertedPoint = [self convertPoint:point toView:child];
        if ([child pointInside:convertedPoint withEvent:event]) {
            result = child;
        }
    }

    return result;
}

#pragma mark -
#pragma mark TapDetectingPhotoViewDelegate methods

- (void)tapDidOccur:(SensibleAreaView *)aView {
    NSLog(@"tapDidOccur ids:%d tag:%d", aView.ids, aView.tag);
    [tappableAreaDelegate tapDidOccurOnSensibleAreaWithId:aView.ids];
}

<强> SensibleAreaView.h:

@protocol SensibleAreaViewDelegate;

@interface SensibleAreaView : UIView {
    id <SensibleAreaViewDelegate> delegate;
    NSUInteger ids;
    UIButton *disclosureButton;
}

@property (nonatomic, assign) id <SensibleAreaViewDelegate> delegate;
@property (nonatomic, assign) NSUInteger ids;
@property (nonatomic, retain) UIButton *disclosureButton;

@end


@protocol SensibleAreaViewDelegate <NSObject>
@required
- (void)tapDidOccur:(SensibleAreaView *)aView;
@end

<强> SensibleAreaView.m:

#import "SensibleAreaView.h"

@implementation SensibleAreaView

@synthesize delegate, ids, disclosureButton;


- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.userInteractionEnabled = YES;

        UIColor *color = [[UIColor alloc] initWithWhite:0.4 alpha:1.0]; 
        self.backgroundColor = color;
        [color release];

        UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        [button addTarget:self action:@selector(buttonTouched) forControlEvents:UIControlEventTouchUpInside];
        CGRect buttonFrame = button.frame;
        // set the button position over the right edge of the sensible area
        buttonFrame.origin.x = frame.size.width - buttonFrame.size.width + 5.0f;
        buttonFrame.origin.y = frame.size.height/2 - 10.0f;
        button.frame = buttonFrame;
        button.autoresizingMask = UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
        self.disclosureButton = button;
        [self addSubview:button];

        // notification used to make sure that the button is properly scaled together with the photoview. I do not want the button looks bigger if the photoview is zoomed, I want to preserve its default dimensions
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(zoomFactorChanged:) name:@"zoomFactorChanged" object:nil];
    }

    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];

    if ([[touches anyObject] tapCount] == 1)
        [delegate tapDidOccur:self];
}


- (void)buttonTouched {
[delegate tapDidOccur:self];
}

- (void)zoomFactorChanged:(NSNotification *)message {
    NSDictionary *userInfo = [message userInfo];
    CGFloat factor = [[userInfo valueForKey:@"zoomFactor"] floatValue];
    BOOL withAnimation = [[userInfo valueForKey:@"useAnimation"] boolValue];

    if (withAnimation) {
        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:0.18];
    }

    disclosureButton.transform = CGAffineTransformMake(1/factor, 0.0, 0.0, 1/factor, 0.0, 0.0);

    if (withAnimation)
        [UIView commitAnimations];
}


- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"zoomFactorChanged"   object:nil];
    [disclosureButton release];
    [super dealloc];
}

答案 1 :(得分:1)

一些想法:

子类TTPhotoView,然后覆盖createPhotoView中的TTPhotoViewController

- (TTPhotoView*)createPhotoView {
  return [[[MyPhotoView alloc] init] autorelease];
}

尝试覆盖setImage:子类中的私有方法(是的,不好的做法,但是嘿)TTPhotoView

- (void)setImage:(UIImage*)image {
  [super setImage:image]

  // Add a subview with the frame of self.view, maybe?..
  //
  // Check for self.isLoaded (property of TTImageView
  // which is subclassed by TTPhotoView) to check if
  // the image is loaded
}

通常,请查看TTPhotoViewControllerTTPhotoView的标题实现(针对私有方法)。设置一些断点来弄清楚它是什么:)

答案 2 :(得分:0)

有趣的问题。 Facebook的标签功能与此类似。它们的标签不会与图像成比例缩放。事实上,如果你进行了缩放,它们甚至都不显示标签。我不知道这对你有帮助,但我会看看(如果)three20如何处理标签然后尝试扩展它。