Масштаб CGPath, чтобы соответствовать UIVIew
Мне нужно знать, как я могу уменьшить или увеличить CGPath, чтобы он соответствовал UIView?
Установите цвет фона на желтый, чтобы четко видеть вид
self.frame = CGRectMake(0, 0, 181, 154);
self.backgroundColor = [UIColor yellowColor];
Draw CAShapeLayer
CAShapeLayer *shape = [CAShapeLayer layer];
shape.path = aPath;
shape.strokeColor = [[UIColor blueColor] CGColor];
shape.lineWidth = 5;
shape.fillColor = [[UIColor clearColor] CGColor];
[self.layer addSublayer:shape];
Тогда я получаю это:
Что я хочу это:
Спасибо разработчикам:)
3 ответа
Я предполагаю, что вы хотите полностью заполнить (не искажая) форму, чтобы она соответствовала вашему виду. Я также предполагаю, что у вас уже есть слой формы с путем, так как вопрос помечен как CAShapeLayer.
В этом случае вы получите ограничивающую рамку пути слоев фигур и рассчитаете соответствующий масштабный коэффициент, который будет применяться к пути.
В зависимости от соотношения сторон формы и вида, который он должен соответствовать, будут либо ширина, либо высота, которые определяют масштабный коэффициент.
Получив масштабный коэффициент, вы создадите преобразование для применения к пути. Поскольку путь может не начинаться с (0,0), вы также переводите (перемещаете) путь в преобразовании в начало ограничивающих прямоугольников.
Если вы хотите, чтобы новый путь находился в центре представления, вам необходимо рассчитать, насколько он будет перемещен. Вы можете закомментировать этот код, если он вам не нужен, но я включил его и для других людей, читающих этот ответ.
Наконец, у вас есть новый путь, поэтому все, что вам нужно сделать, это создать новый слой формы, назначить путь, стилизовать его соответствующим образом и добавить в представление.
// I'm assuming that the view and original shape layer is already created
CGRect boundingBox = CGPathGetBoundingBox(shapeLayer.path);
CGFloat boundingBoxAspectRatio = CGRectGetWidth(boundingBox)/CGRectGetHeight(boundingBox);
CGFloat viewAspectRatio = CGRectGetWidth(viewToFitIn.frame)/CGRectGetHeight(viewToFitIn.frame);
CGFloat scaleFactor = 1.0;
if (boundingBoxAspectRatio > viewAspectRatio) {
// Width is limiting factor
scaleFactor = CGRectGetWidth(viewToFitIn.frame)/CGRectGetWidth(boundingBox);
} else {
// Height is limiting factor
scaleFactor = CGRectGetHeight(viewToFitIn.frame)/CGRectGetHeight(boundingBox);
}
// Scaling the path ...
CGAffineTransform scaleTransform = CGAffineTransformIdentity;
// Scale down the path first
scaleTransform = CGAffineTransformScale(scaleTransform, scaleFactor, scaleFactor);
// Then translate the path to the upper left corner
scaleTransform = CGAffineTransformTranslate(scaleTransform, -CGRectGetMinX(boundingBox), -CGRectGetMinY(boundingBox));
// If you want to be fancy you could also center the path in the view
// i.e. if you don't want it to stick to the top.
// It is done by calculating the heigth and width difference and translating
// half the scaled value of that in both x and y (the scaled side will be 0)
CGSize scaledSize = CGSizeApplyAffineTransform(boundingBox.size, CGAffineTransformMakeScale(scaleFactor, scaleFactor));
CGSize centerOffset = CGSizeMake((CGRectGetWidth(viewToFitIn.frame)-scaledSize.width)/(scaleFactor*2.0),
(CGRectGetHeight(viewToFitIn.frame)-scaledSize.height)/(scaleFactor*2.0));
scaleTransform = CGAffineTransformTranslate(scaleTransform, centerOffset.width, centerOffset.height);
// End of "center in view" transformation code
CGPathRef scaledPath = CGPathCreateCopyByTransformingPath(shapeLayer.path,
&scaleTransform);
// Create a new shape layer and assign the new path
CAShapeLayer *scaledShapeLayer = [CAShapeLayer layer];
scaledShapeLayer.path = scaledPath;
scaledShapeLayer.fillColor = [UIColor blueColor].CGColor;
[viewToFitIn.layer addSublayer:scaledShapeLayer];
CGPathRelease(scaledPath); // release the copied path
В моем примере кода (и формы) это выглядело так
Swift 4.0 версия ответа Дэвида Rönnqvist
func resizepath(Fitin frame : CGRect , path : CGPath) -> CGPath{
let boundingBox = path.boundingBox
let boundingBoxAspectRatio = boundingBox.width / boundingBox.height
let viewAspectRatio = frame.width / frame.height
var scaleFactor : CGFloat = 1.0
if (boundingBoxAspectRatio > viewAspectRatio) {
// Width is limiting factor
scaleFactor = frame.width / boundingBox.width
} else {
// Height is limiting factor
scaleFactor = frame.height / boundingBox.height
}
var scaleTransform = CGAffineTransform.identity
scaleTransform = scaleTransform.scaledBy(x: scaleFactor, y: scaleFactor)
scaleTransform.translatedBy(x: -boundingBox.minX, y: -boundingBox.minY)
let scaledSize = boundingBox.size.applying(CGAffineTransform (scaleX: scaleFactor, y: scaleFactor))
let centerOffset = CGSize(width: (frame.width - scaledSize.width ) / scaleFactor * 2.0, height: (frame.height - scaledSize.height) / scaleFactor * 2.0 )
scaleTransform = scaleTransform.translatedBy(x: centerOffset.width, y: centerOffset.height)
//CGPathCreateCopyByTransformingPath(path, &scaleTransform)
let scaledPath = path.copy(using: &scaleTransform)
return scaledPath!
}
Swift 5 version of David Rönnqvist, based on Tarek's answer but refactored and with the
scaleFactor * 2
bug fixed:
extension CGPath {
func resized(to rect: CGRect) -> CGPath {
let boundingBox = self.boundingBox
let boundingBoxAspectRatio = boundingBox.width / boundingBox.height
let viewAspectRatio = rect.width / rect.height
let scaleFactor = boundingBoxAspectRatio > viewAspectRatio ?
rect.width / boundingBox.width :
rect.height / boundingBox.height
let scaledSize = boundingBox.size.applying(CGAffineTransform(scaleX: scaleFactor, y: scaleFactor))
let centerOffset = CGSize(
width: (rect.width - scaledSize.width) / (scaleFactor * 2),
height: (rect.height - scaledSize.height) / (scaleFactor * 2)
)
var transform = CGAffineTransform.identity
.scaledBy(x: scaleFactor, y: scaleFactor)
.translatedBy(x: -boundingBox.minX + centerOffset.width, y: -boundingBox.minY + centerOffset.height)
return copy(using: &transform)!
}
}