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

人气:848 发布:2022-10-16 标签: iphone draggable uiimageview

问题描述

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

将此代码从网络上取下来用于可拖动图像,

took this code off the web for the draggable image,

问题:图片是不规则形状,有透明区域,点击透明区域也会拖拽.

Problem: image is irregular shape with transparent areas, clicking on transparent areas drags it as well.

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

Required Solution: How to make the transparent areas non interactive/non draggable?

任何建议,我将尝试掩盖图像并发布结果,但任何解决方法/建议.

Any suggestions, I will be trying to mask the image as an attempt and will post the results, but any workarounds/advise.

MiRAGe 的进一步建议:尝试将代码合并到一个类文件中,因为图像属性在 UIImageView 中可用,并且在界面生成器中使用任何 UIImageView 即插即用会更容易,但仍然存在问题,透明区域是可移动的,hitTest 方法在单击时被多次调用,有什么建议吗?

Further to suggestions by MiRAGe: Trying to incorporate the code in one class file, since image property is available in UIImageView and it would be easier to plug and play with any UIImageView in interface builder, but still having problems, transparent areas are movable, hitTest method gets called several times on a single click, any advise?

#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;
}

推荐答案

您可以轻松检测 alpha 区域并使其不可拖动.这是一些允许您检测 alpha 区域的代码.这对你来说可能是一些开销,但这是我能做的最好的.

You can easily detect alpha area's and make them non draggable. Here is some code that will allow you to detect alpha area's. It might be some overhead for you, but it's the best I could do.

我对 UIImage 进行了子类化,并将这段代码放在了实现文件中.

I have subclassed UIImage and put this code in the implementation file.

#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 函数来允许检测,但您可以轻松地将其更改为适合您的东西,这只是一个示例)我放置此代码来检测点命中是否透明或不.如果它是透明的,我们将触摸传递给下面的视图,否则我们将触摸保留给自己.

Now in a subclass of UIImageView (I use the hitTest function to allow detection, but you could easily change this into something that works for you, this is just an example) I put this code to detect if the point hit was transparent or not. If it is transparent, we pass the touch onto the view below, otherwise we keep the touch to ourselves.

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

692