Предположительно, вам не понадобятся четыре других вида, вам понадобится восемь.Внутренний «фиксированный» прямоугольник делит вид на девять срезов:
+----+-----+---------+
| | | |
+----+-----+---------|
| |fixed| |
| | | |
+----+-----+---------+
| | | |
| | | |
| | | |
+----+-----+---------+
Обратите внимание, что для каждого из девяти срезов может потребоваться различная комбинация горизонтального и вертикального масштабирования.В конечном итоге это выглядит так:
В любом случае, я подумал, что это не будет слишком сложно сделать, просто создав подкласс UIView
, который переопределяет drawRect:
.
Я назвал свой подкласс OutstretchView
и дал ему три свойства:
@interface OutstretchView : UIView
@property (nonatomic) UIImage *image;
@property (nonatomic) CGRect fixedRect; // in image coordinates
@property (nonatomic) CGPoint fixedCenter; // in view coordinates
@end
Свойство fixedRect
определяет часть изображения, которая никогда не должна растягиваться.Свойство fixedCenter
определяет, где в представлении должна быть нарисована часть изображения.
Чтобы на самом деле нарисовать девять фрагментов изображения, полезно определить метод, который принимает CGRect
в изображениикоординаты и CGRect
в координатах вида, и рисует указанную часть изображения в указанной части вида.Я использую функции клипа и CTM в Quartz 2D для выполнения «тяжелой работы»:
- (void)drawRect:(CGRect)imageRect ofImage:(UIImage *)image inRect:(CGRect)viewRect {
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc); {
CGContextClipToRect(gc, viewRect);
CGContextTranslateCTM(gc, viewRect.origin.x, viewRect.origin.y);
CGContextScaleCTM(gc, viewRect.size.width / imageRect.size.width, viewRect.size.height / imageRect.size.height);
CGContextTranslateCTM(gc, -imageRect.origin.x, -imageRect.origin.y);
[image drawAtPoint:CGPointZero];
} CGContextRestoreGState(gc);
}
Я также буду использовать небольшой вспомогательный метод, который создает CGRect
из массива двух координат X и массивас двумя координатами Y:
static CGRect rect(CGFloat *xs, CGFloat *ys) {
return CGRectMake(xs[0], ys[0], xs[1] - xs[0], ys[1] - ys[0]);
}
С этими помощниками я могу реализовать drawRect:
.Сначала я работаю с простыми случаями, когда изображение не рисуется или не определено fixedRect
:
- (void)drawRect:(CGRect)dirtyRect {
UIImage *image = self.image;
if (!image)
return;
CGRect imageBounds = (CGRect){ CGPointZero, image.size };
CGRect viewBounds = self.bounds;
CGRect imageFixedRect = self.fixedRect;
if (CGRectIsNull(imageFixedRect)) {
[image drawInRect:viewBounds];
return;
}
Затем я вычисляю прямоугольник в координатах вида, где будет нарисована фиксированная часть изображения:
CGPoint imageFixedCenter = self.fixedCenter;
CGRect viewFixedRect = CGRectOffset(imageFixedRect, imageFixedCenter.x - imageFixedRect.size.width / 2, imageFixedCenter.y - imageFixedRect.size.height / 2);
Далее я создаю массив из четырех (да, четырех) координат X, которые определяют края срезов в координатном пространстве вида, и аналогичный массив для координат Y, и еще два массива длякоординаты в координатном пространстве изображения:
CGFloat viewSlicesX[4] = { viewBounds.origin.x, viewFixedRect.origin.x, CGRectGetMaxX(viewFixedRect), CGRectGetMaxX(viewBounds) };
CGFloat viewSlicesY[4] = { viewBounds.origin.y, viewFixedRect.origin.y, CGRectGetMaxY(viewFixedRect), CGRectGetMaxY(viewBounds) };
CGFloat imageSlicesX[4] = { imageBounds.origin.x, imageFixedRect.origin.x, CGRectGetMaxX(imageFixedRect), CGRectGetMaxX(imageBounds) };
CGFloat imageSlicesY[4] = { imageBounds.origin.y, imageFixedRect.origin.y, CGRectGetMaxY(imageFixedRect), CGRectGetMaxY(imageBounds) };
И, наконец, самая простая часть, рисование срезов:
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 3; ++x) {
[self drawRect:rect(imageSlicesX + x, imageSlicesY + y) ofImage:image inRect:rect(viewSlicesX + x, viewSlicesY + y)];
}
}
}
Это немного запаздывает на моем iPad 2.
Эта версия моего тестового проекта на github .