@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的建议:尝试将代码合并到一个类文件中,因为图像属性在UIImageView中可用,并且在界面构建器中插入和播放任何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;
}
答案 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);
...
}
糟糕的是,为复杂的形状建立路径是乏味的。好处是因为你正在处理手指敲击,粗略的轮廓应该足够了。此外,这可以让您的触摸目标偏离图像的不透明部分,以防您需要一些额外的空间来帮助实现可用性。它还允许触摸目标内的透明度,从而允许您在必要时拥有更复杂的图像。
如果您只是在图像编辑器中将它们映射出来,则可以非常轻松地获得所需的边界点。