2010-06-07 13 views
5

He integrado con éxito el framework three20 en mi proyecto, y he extendido el TTPhotoViewController para agregar alguna funcionalidad adicional al .Extienda TTPhotoViewController con TTPhotoView personalizado

Ahora necesito agregar algunas subvistas al TTPhotoView cargado por el TTPhotoViewController. En particular, me gustaría agregar las subvistas después de cada TTPhotoView que se haya cargado. Estas subvistas representan área sensible sobre la imagen, por lo que deben escalar proporcionalmente con la imagen. El usuario puede tocar una subvista para obtener información adicional sobre la imagen.

No sé cómo implementar este comportamiento. ¿Debo extender el TTPhotoView y me aseguro de que el TTPhotoViewController use esta versión extendida en lugar de su TTPhotoView?

¿Podría alguien dirigirme en la dirección correcta? Gracias

Respuesta

5

Resuelto subclasando el TTPhotoView (TapDetectingPhotoView) y luego agregando todas mis subvistas a esa subclase. El problema principal estuvo representado por la conmutación de fotos, porque el TTPhotoViewController (en particular, su TTScrollView interno) reutiliza el TTPhotoView durante la operación de conmutación. Entonces, por ejemplo, si agrega su subvista a su subclase TTPhotoView y trata de cambiar a la siguiente foto, su subvista probablemente estará aquí, porque se reutiliza TTPhotoView. Para resolver este problema, decidí agregar y quitar todas mis subvistas cada vez que ocurría un cambio de foto (vea TTPhotoViewController :: didMoveToPhoto). De esta manera, estoy seguro de que cada foto vista tiene sus subvistas.

Aquí mi implementación (solo métodos notables) Espero que esta ayuda!

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]; 
} 
0

Interesante pregunta. Facebook tiene una funcionalidad similar con sus etiquetas. Sus etiquetas no se escalan proporcionalmente con la imagen. De hecho, ni siquiera muestran las etiquetas si has ampliado. No sé si esto te ayudará, pero me gustaría ver cómo (si) tres20 maneja las etiquetas y luego tal vez intentar extender eso.

1

Algunas ideas:

Subclase TTPhotoView, entonces anulan createPhotoView en su TTPhotoViewController:

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

Intente sustituir un método privado (sí, mala práctica, pero bueno) setImage: en TTPhotoView subclase:

- (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 
} 

En general, mira los encabezados y implementaciones (para los métodos privados) de TTPhotoViewController y TTPhotoView. Establezca algunos puntos de interrupción para averiguar qué hace qué :)

Cuestiones relacionadas