Расширение TTPhotoViewController с помощью пользовательского TTPhotoView
Я успешно интегрировал фреймворк Three20 в свой проект и расширил TTPhotoViewController, чтобы добавить некоторые дополнительные функции.
Теперь мне нужно добавить несколько подпредставлений в TTPhotoView, загруженный TTPhotoViewController. В частности, я хотел бы добавить эти подпредставления после каждой загрузки TTPhotoView. Эти подпредставления представляют собой чувствительную область над изображением, поэтому они должны масштабироваться пропорционально изображению. Пользователь может нажать на подпредставление, чтобы получить дополнительную информацию об изображении.
Я не знаю, как реализовать это поведение. Должен ли я расширить TTPhotoView и убедиться, что TTPhotoViewController использует эту расширенную версию вместо своего TTPhotoView?
Может ли кто-нибудь указать мне правильное направление? Спасибо
3 ответа
Решил создание подкласса TTPhotoView (TapDetectingPhotoView) и добавление всех моих подпредставлений в этот подкласс. Основная проблема была представлена переключением фотографий, потому что TTPhotoViewController (в частности его внутренний TTScrollView) повторно использует TTPhotoView во время операции переключения. Например, если вы добавите свое подпредставление в свой подкласс TTPhotoView и попытаетесь переключиться на следующую фотографию, ваше подпредставление, вероятно, будет здесь, поскольку TTPhotoView используется повторно. Чтобы решить эту проблему, я решил добавлять и удалять все мои подпредставления каждый раз, когда происходит переключение фотографий (см. TTPhotoViewController::didMoveToPhoto). Таким образом, я уверен, что у каждого фотообзора есть свои подпредставления.
Здесь моя реализация (только замечательные методы) Надеюсь, что это поможет!
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];
}
Некоторые идеи:
Подкласс 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
}
В общем, посмотрите на заголовки и реализации (для частных методов) TTPhotoViewController
а также TTPhotoView
, Установите некоторые точки останова, чтобы выяснить, что и что делает:)
Интересный вопрос. Facebook имеет похожую функциональность с их тегами. Их теги не масштабируются пропорционально изображению. На самом деле, они даже не показывают теги, если вы увеличили масштаб. Я не знаю, поможет ли это вам, но я бы посмотрел, как (если) three20 обрабатывает теги, а затем, возможно, попытается расширить это.