В разделе чертежа моего приложения пользователь должен иметь возможность выбрать путь, который они создали. UIBezierPath
имеет удобный метод containsPoint:
, который хорошо работает в большинстве случаев, но это очень плохо, когда линия нарисована близка к прямой, без кривых. Прикосновение пальца к прямой линии очень сложно.Обнаружение касания на толстом UIBezierPath
Похоже, что метод проверяет очень узкую линию вдоль пути, и даже значение свойства lineWidth
пути к большему значению не влияет на результаты теста containsPoint:
.
Я рассмотрел аналогичные вопросы, например, этот: Detect touch on UIBezierPath stroke, not fill, но не смог изменить решение, чтобы посмотреть на «область вокруг пути», она только кажется, смотрит на тонкую линию в центре ее.
Редактировать
рабочая, но интенсивное решение, которое я придумал, чтобы преобразовать путь в UIImage
и проверить цвет этого изображения в точке запрошенной. Если альфа-компонент больше нуля, путь пересекает прикосновение.
Это выглядит следующим образом:
// pv is a view that contains our path as a property
// touchPoint is a CGPoint signifying the location of the touch
PathView *ghostPathView = [[PathView alloc] initWithFrame:pv.bounds];
ghostPathView.path = [pv.path copy]; // make a copy of the path
// 40 px is about thick enough to touch reliably
ghostPathView.path.lineWidth = 40;
// the two magic methods getImage and getColorAtPoint:
// have been copied from elsewhere on stackoverflow
// and do exactly what you would expect from their names
UIColor *color = [[ghostPathView getImage] getColorAtPoint:touchPoint];
CGFloat alpha;
[color getWhite:nil alpha:&alpha];
if (alpha > 0){
// it's a match!
}
Вот вспомогательные методы:
// in a category on UIImage
- (UIColor *)getColorAtPoint:(CGPoint)p {
// return the colour of the image at a specific point
// make sure the point lies within the image
if (p.x >= self.size.width || p.y >= self.size.height || p.x < 0 || p.y < 0) return nil;
// First get the image into your data buffer
CGImageRef imageRef = [self CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(context);
// Now your rawData contains the image data in the RGBA8888 pixel format.
int byteIndex = [[UIScreen mainScreen] scale] * ((bytesPerRow * p.y) + p.x * bytesPerPixel);
CGFloat red = (rawData[byteIndex] * 1.0)/255.0;
CGFloat green = (rawData[byteIndex + 1] * 1.0)/255.0;
CGFloat blue = (rawData[byteIndex + 2] * 1.0)/255.0;
CGFloat alpha = (rawData[byteIndex + 3] * 1.0)/255.0;
free(rawData);
return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
}
// in a category on UIView
- (UIImage *)getImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [[UIScreen mainScreen]scale]);
[[self layer] renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
Есть ли более элегантный способ сделать это?
Редактировать
Великий ответ на Satachito, просто хочу, чтобы включить его в Obj-C для полноты картины. Я использую 40-тактный такт, поскольку он ближе к рекомендованному Apple минимальному размеру касания, равному 44х44 пикселям.
CGPathRef pathCopy = CGPathCreateCopyByStrokingPath(originalPath.CGPath, nil, 40, kCGLineCapButt, kCGLineJoinMiter, 1);
UIBezierPath *fatOne = [UIBezierPath bezierPathWithCGPath:pathCopy];
if ([fatOne containsPoint:p]){
// match found
}
Отлично! Это дает мне те же результаты, что и сложный метод, который я придумал, но примерно в 100-200 раз быстрее. – SaltyNuts