可拖动的UIImageView部分透明&不规则形状

时间:2011-04-20 15:06:28

标签: iphone uiimageview draggable

@interface UIDraggableImageView : UIImageView {

}

.m文件

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
// Retrieve the touch point
CGPoint point = [[touches anyObject] locationInView:self];
startLocation = point;
[[self superview] bringSubviewToFront:self];
}

 - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
// Move relative to the original touch point
CGPoint point = [[touches anyObject] locationInView:self];
CGRect frame = [self frame];
frame.origin.x += point.x - startLocation.x;
frame.origin.y += point.y - startLocation.y;
[self setFrame:frame];
}

将此代码从网上下载为可拖动图像

问题:图像是带有透明区域的不规则形状,点击透明区域也会拖拽它。

必需的解决方案:如何使透明区域不具有交互性/不可拖动性?

任何建议,我都会尝试将图像屏蔽为尝试并发布结果,但任何变通方法/建议。

继MiRAGe的建议:尝试将代码合并到一个类文件中,因为图像属性在U​​IImageView中可用,并且在界面构建器中插入和播放任何UIImageView会更容易,但仍然有问题,透明区域是可以通过一次点击多次调用可移动的hitTest方法,任何建议?

#import "UIImageViewDraggable.h"

@implementation UIImageViewDraggable

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// Retrieve the touch point
CGPoint point = [[touches anyObject] locationInView:self];
startLocation = point;
[[self superview] bringSubviewToFront:self];
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
// Move relative to the original touch point
CGPoint point = [[touches anyObject] locationInView:self];
CGRect frame = [self frame];
frame.origin.x += point.x - startLocation.x;
frame.origin.y += point.y - startLocation.y;
[self setFrame:frame];
}

- (NSData *)alphaData {
CGContextRef    cgctx = NULL;
void *          bitmapData;
int             bitmapByteCount;

size_t pixelsWide = CGImageGetWidth(self.image.CGImage);
size_t pixelsHigh = CGImageGetHeight(self.image.CGImage);

bitmapByteCount     = (pixelsWide * pixelsHigh);

bitmapData = malloc( bitmapByteCount );
if (bitmapData == NULL) 
    return nil;

cgctx = CGBitmapContextCreate (bitmapData,
                               pixelsWide,
                               pixelsHigh,
                               8,
                               pixelsWide,
                               NULL,
                               kCGImageAlphaOnly);
if (cgctx == NULL) {
    free (bitmapData);
    fprintf (stderr, "Context not created!");

    return nil;
}

CGRect rect = {{0,0},{pixelsWide,pixelsHigh}}; 
CGContextDrawImage(cgctx, rect, self.image.CGImage); 

unsigned char *data = CGBitmapContextGetData(cgctx);

CGContextRelease(cgctx);

if (!data) {
    free(bitmapData);
    return nil;
}

size_t dataSize = pixelsWide * pixelsHigh;

NSData *alphaData = [NSData dataWithBytes:data length:dataSize];

free(bitmapData);
return alphaData;
}    

- (BOOL)isTransparentLocation:(CGPoint)point withData:(NSData *)data {   
if (data == nil)
    NSLog(@"data was nil");

NSUInteger index = point.x + (point.y * [self.image size].width);
unsigned char *rawDataBytes = (unsigned char *)[data bytes];

return (rawDataBytes[index] == 0);
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSLog(@"test");
NSAutoreleasePool *pool = [NSAutoreleasePool new];

// view responding to the hit test. note that self may respond too.
UIView *anyViewResponding = [super hitTest:point withEvent:event];  
if( anyViewResponding == nil || anyViewResponding == self ) {
    // convert the point in the image, to a global point.
    CGPoint framePoint = [self.superview convertPoint:point fromView:self];
    // if the point is in the image frame, and there is an image, see if we need to let the touch through or not
    if(self.image != nil && CGRectContainsPoint([self frame], framePoint)) {
        NSData *imageData = [self alphaData];         

        // check if the point touched is transparent in the image
        if( imageData != nil && [self isTransparentLocation:point withData:imageData]) {               
            // return nil, so the touch will not arrive at this view
            anyViewResponding = nil;
        }
    }
}

[pool drain];
return anyViewResponding;
}

2 个答案:

答案 0 :(得分:1)

您可以轻松检测Alpha区域并使其不可拖动。这里有一些代码可以让你检测alpha区域。这可能是你的一些开销,但这是我能做的最好的。

我已经将UIImage子类化并将此代码放在实现文件中。

#import <CoreGraphics/CoreGraphics.h>

- (NSData *)alphaData
{
    CGContextRef    cgctx = NULL;
    void *          bitmapData;
    int             bitmapByteCount;

    size_t pixelsWide = CGImageGetWidth(self.CGImage);
    size_t pixelsHigh = CGImageGetHeight(self.CGImage);

    bitmapByteCount     = (pixelsWide * pixelsHigh);

    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL) 
        return nil;

    cgctx = CGBitmapContextCreate (bitmapData,
                                   pixelsWide,
                                   pixelsHigh,
                                   8,
                                   pixelsWide,
                                   NULL,
                                   kCGImageAlphaOnly);
    if (cgctx == NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");

        return nil;
    }

    CGRect rect = {{0,0},{pixelsWide,pixelsHigh}}; 
    CGContextDrawImage(cgctx, rect, self.CGImage); 

    unsigned char *data = CGBitmapContextGetData(cgctx);

    CGContextRelease(cgctx);

    if (!data)
    {
        free(bitmapData);
        return nil;
    }

    size_t dataSize = pixelsWide * pixelsHigh;

    NSData *alphaData = [NSData dataWithBytes:data length:dataSize];

    free(bitmapData);
    return alphaData;
}    

- (BOOL)isTransparentLocation:(CGPoint)point withData:(NSData *)data
{   
    if (data == nil)
        NSLog(@"data was nil");

    NSUInteger index = point.x + (point.y * [self size].width);
    unsigned char *rawDataBytes = (unsigned char *)[data bytes];

    return (rawDataBytes[index] == 0);
}

现在在UIImageView的子类中(我使用hitTest函数来允许检测,但您可以轻松地将其更改为适合您的内容,这只是一个示例)我将此代码用于检测点击是否透明或不。如果它是透明的,我们将触摸传递到下面的视图,否则我们会保持触摸自己。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    // view responding to the hit test. note that self may respond too.
    UIView *anyViewResponding = [super hitTest:point withEvent:event];  
    if( anyViewResponding == nil || anyViewResponding == self )
    {
        // convert the point in the image, to a global point.
        CGPoint framePoint = [self.superview convertPoint:point fromView:self];
        // if the point is in the image frame, and there is an image, see if we need to let the touch through or not
        if( self.image != nil && CGRectContainsPoint([self frame], framePoint) )
        {
            NSData *imageData = [self.image alphaData];         

            // check if the point touched is transparent in the image
            if( imageData != nil && [self.image isTransparentLocation:point imageData] )
            {               
                // return nil, so the touch will not arrive at this view
                anyViewResponding = nil;
            }
        }
    }

    [pool drain];
    return anyViewResponding;
}

答案 1 :(得分:0)

您可以执行命中检测以确定表示点击手势的CGPoint是否位于由CGPath定义的形状内。

- (id)initWithFrame:(CGRect)frame {
    ...
    CGPathRef outline = CGPathCreateMutable();
    CGPathMoveToPoint(outline, NULL, 20, 20);
    // Build up path
    ...
}

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
    CGPoint point = [[touches anyObject] locationInView:self];
    if (CGPathContainsPoint(outline, NULL, point, false) {
        ...
        dragIsRespected = YES;
    }
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
    if (dragIsRespected) {
        ...
    }
}

- (void)dealloc {
    CGPathRelease(outline);
    ...
}

糟糕的是,为复杂的形状建立路径是乏味的。好处是因为你正在处理手指敲击,粗略的轮廓应该足够了。此外,这可以让您的触摸目标偏离图像的不透明部分,以防您需要一些额外的空间来帮助实现可用性。它还允许触摸目标内的透明度,从而允许您在必要时拥有更复杂的图像。

如果您只是在图像编辑器中将它们映射出来,则可以非常轻松地获得所需的边界点。