使用自定义 TTPhotoView 扩展 TTPhotoViewController

发布于 2024-09-05 04:26:21 字数 330 浏览 13 评论 0 原文

我已经成功地将 Three20 框架集成到我的项目中, 我扩展了 TTPhotoViewController 以添加更多内容 功能。

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

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

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

I have successfully integrated the three20 framework in my project,
and I have extended the TTPhotoViewController to add some further
functionality.

Now I need to add some subviews to the TTPhotoView loaded by the
TTPhotoViewController. In particular I would like to add that subviews
after every TTPhotoView as been loaded. These subviews represents
sensible area over the image so they should scale proportionally with
the image.
The user can tap a subview to get extra info about the image.

I don't know how to implement this behavior. Should I extend the
TTPhotoView and make sure that the TTPhotoViewController use this
extended version instead of its TTPhotoView?

Could someone point me to the right direction?
Thank you

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

偏爱你一生 2024-09-12 04:26:21

解决了对 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];
}

Solved subclassing the TTPhotoView (TapDetectingPhotoView) and then adding all my subviews to that subclass.
The main problem was represented by the photo switching, because the TTPhotoViewController (in particular its inner TTScrollView) reuse the TTPhotoView during switching operation.
So for example if you add your subview to your TTPhotoView subclass and try to switch to the next photo, your subview will probably be here, because the TTPhotoView is reused.
To solve this problem I decided to add and remove all my subviews every time a photo switch occur (see TTPhotoViewController::didMoveToPhoto).
In this way I'm sure that every photoview has its subviews.

Here my implementation (only remarkable methods)
Hope these help!

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];
}
灵芸 2024-09-12 04:26:21

一些想法:

子类 TTPhotoView,然后在 TTPhotoViewController 中重写 createPhotoView

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

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

- (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。设置一些断点来弄清楚什么是做什么的:)

Some ideas:

Subclass TTPhotoView, then override createPhotoView in your TTPhotoViewController:

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

Try overriding a private method (yes, bad practice, but hey) setImage: in TTPhotoView subclass:

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

In general, look at the headers and implementations (for the private methods) of TTPhotoViewController and TTPhotoView. Set some breakpoints to figure out what does what :)

梦醒时光 2024-09-12 04:26:21

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

Interesting question. Facebook has a similar functionality with their tags. Their tags do not scale proportionally with the image. In fact, they do not even show the tags if you have zoomed. I don't know if this will help you, but I would look at how (if) three20 handles tags and then maybe try to extend that.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文