Задача, которую вы ищете, может быть сложной в некоторых случаях. Но может быть, что-то вроде этого должно быть достаточно:
class RoofView: UIView {
var paths: [(path: UIBezierPath, rotationInRadians: CGFloat)]? { didSet { self.refresh() } }
var image: UIImage? { didSet { self.refresh() } }
var preserveImageAspect: Bool = true { didSet { self.refresh() } }
func refresh() {
self.setNeedsDisplay() // This will trigger redraw
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let paths = paths else { return }
guard let image = image else { return }
guard let context = UIGraphicsGetCurrentContext() else { return }
paths.forEach { item in
context.saveGState() // Save state. This includes transformations and clips
item.path.addClip()
let boundingRect: CGRect = {
let rotatedPath = (item.path.copy() as! UIBezierPath) // Create a duplicate path
rotatedPath.apply(CGAffineTransform(rotationAngle: -item.rotationInRadians)) // Rotate it backwards
if preserveImageAspect {
let minimumBounds = rotatedPath.bounds
let center = CGPoint(x: minimumBounds.midX, y: minimumBounds.midY)
let imageRatio: CGFloat = image.size.width / image.size.height
if minimumBounds.width / minimumBounds.height < imageRatio {
// Preserve height, scale width
let size = CGSize(width: minimumBounds.height*imageRatio, height: minimumBounds.height)
return CGRect(x: center.x - size.width*0.5, y: center.y - size.height*0.5, width: size.width, height: size.height)
} else {
// Preserve width, scale height
let size = CGSize(width: minimumBounds.width, height: minimumBounds.width/imageRatio)
return CGRect(x: center.x - size.width*0.5, y: center.y - size.height*0.5, width: size.width, height: size.height)
}
} else {
return rotatedPath.bounds
}
}()
context.rotate(by: item.rotationInRadians)
image.draw(in: boundingRect)
context.restoreGState() // Put back state. This includes transformations and clips
}
}
}
То, что этот код делает, берет изображение и др aws его на всех путях с заданными поворотами.
Для этого он обрезает контекст с помощью пути, используя addClip
, что означает, что каждая последующая команда рисования будет нарисована только внутри обрезанной области.
Затем рисуется изображение, для которого необходимо определить позицию. Мы стараемся вписать минимальный прямоугольник, где должно быть нарисовано изображение. Важно повернуть путь, чтобы определить правильные границы, или они могут быть слишком маленькими при повороте (например, на 45 градусов). При желании соотношение сторон сохраняется как режим «заполнения», который требует немного математики ...
Затем контекст поворачивается так, что изображение кажется повернутым. Затем изображение рисуется по заданному пути.
Осталось только очистить, что означает восстановление состояния. Это очистит как обтравочную маску, так и вращение контекста.
Если вы заинтересованы в математике для сохранения заливки аспекта: Мы пытаемся «заполнить» размер в прямоугольнике. Размер берется из изображения, а прямоугольник - это то место, где мы хотим его нарисовать. Нам нужно сравнить как rat ios (width/height
), так и в зависимости от того, что больше, мы либо увеличиваем ширину или высоту, чтобы нарисовать "вне границ". В каждом из случаев одна из координат сохраняется, а другая определяется с соотношением сторон изображения. Это общее решение для определения прямоугольника заполнения. Как забавный факт, если вам когда-либо понадобится «соответствовать», все, что вам нужно сделать, это изменить неравенство на if minimumBounds.width / minimumBounds.height > imageRatio {
.