【问题标题】:Extend TTPhotoViewController with custom TTPhotoView使用自定义 TTPhotoView 扩展 TTPhotoViewController
【发布时间】:2010-06-07 17:18:47
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: iphone three20 ttphotoviewcontroller


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      一些想法:

      子类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
      }
      

      一般来说,查看TTPhotoViewControllerTTPhotoView 的标头 实现(用于私有方法)。设置一些断点来弄清楚什么是什么:)

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-12-29
          • 2015-02-01
          • 2019-11-23
          • 2012-09-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多