#swift #macos #cocoa
#swift #macos #какао
Вопрос:
Я испытываю эту странную ошибку с моим пользовательским представлением. Предполагается, что пользовательский вид должен отображать показатели распределения рейтинга. Он добавляется в представление ячейки в виде контура.
Когда я изменяю размер окна, пользовательский вид каким-то образом сжимается и выглядит сломанным. Я вставил drawRect
пользовательский вид ниже.
override func drawRect(r: NSRect) {
super.drawRect(r)
var goodRect: NSRect?
var okRect: NSRect?
var badRect: NSRect?
let barHeight = CGFloat(10.0)
if self.goodPercent != 0.0 {
goodRect = NSRect(x: 0, y: 0, width: r.width * CGFloat(goodPercent), height: barHeight)
let goodPath = NSBezierPath(roundedRect: goodRect!, xRadius: 6, yRadius: 6)
RatingDistributionView.goodColor.setFill()
goodPath.fill()
}
if self.okPercent != 0.0 {
let okX = CGFloat(goodRect?.width ?? 0.0)
okRect = NSRect(x: okX, y: 0, width: r.width * CGFloat(okPercent), height: barHeight)
let okPath = NSBezierPath(roundedRect: okRect!, xRadius: 6, yRadius: 6)
RatingDistributionView.okColor.setFill()
okPath.fill()
}
if self.badPercent != 0.0 {
var badX: CGFloat
//Cases:
//Good persent and OK present - badX = okRect.x okRect.width
//Good persent and OK missing - badX = goodRect.x goodRect.width
//Good missing and OK present - badX = okRect.x okRect.width
//Both missing -
if okRect != nil {
badX = okRect!.origin.x okRect!.width
}else if goodRect != nil {
badX = goodRect!.origin.x goodRect!.width
} else {
badX = 0.0
}
badRect = NSRect(x: badX, y: 0, width: r.width * CGFloat(badPercent), height: barHeight)
let badPath = NSBezierPath(roundedRect: badRect!, xRadius: 6, yRadius: 6)
RatingDistributionView.badColor.setFill()
badPath.fill()
}
//Draw dividers
let divWidth = CGFloat(6.75)
if self.goodPercent != 0.0 amp;amp; (self.okPercent != 0.0 || self.badPercent != 0.0) {
let divX = goodRect!.origin.x goodRect!.width
let divRect = NSRect(x: divX - (divWidth / 2.0), y: 0.0, width: divWidth, height: barHeight)
let divPath = NSBezierPath(roundedRect: divRect, xRadius: 0, yRadius: 0)
NSColor.whiteColor().setFill()
divPath.fill()
}
if self.okPercent != 0.0 amp;amp; self.badPercent != 0.0 {
let divX = okRect!.origin.x okRect!.width
let divRect = NSRect(x: divX - (divWidth / 2.0), y: 0.0, width: divWidth, height: barHeight)
let divPath = NSBezierPath(roundedRect: divRect, xRadius: 0, yRadius: 0)
NSColor.whiteColor().setFill()
divPath.fill()
}
}
Комментарии:
1. важно ли для нас BezierPath? разве вы не можете использовать представления
2. Я бы хотел сохранить закругленный угол..
Ответ №1:
Альтернативным решением вашей проблемы является использование NSView. Вы можете иметь представление контейнера с закругленным углом, а затем рисовать вложенные представления (красный, оранжевый, зеленый) в этом контейнере. вот так;
Я написал для него класс, который вы можете настроить в соответствии с вашими требованиями;
public class CProgressView:NSView {
private lazy var goodView:NSView = {
let viw:NSView = NSView(frame: NSRect.zero);
viw.layer = CALayer();
viw.layer?.backgroundColor = NSColor.greenColor().CGColor;
self.addSubview(viw)
return viw;
} ();
private lazy var okView:NSView = {
let viw:NSView = NSView(frame: NSRect.zero);
viw.layer = CALayer();
viw.layer?.backgroundColor = NSColor.orangeColor().CGColor;
self.addSubview(viw)
return viw;
} ();
private lazy var badView:NSView = {
let viw:NSView = NSView(frame: NSRect.zero);
viw.layer = CALayer();
viw.layer?.backgroundColor = NSColor.redColor().CGColor;
self.addSubview(viw)
return viw;
} ();
private var _goodProgress:CGFloat = 33;
private var _okProgress:CGFloat = 33;
private var _badProgress:CGFloat = 34;
private var goodViewFrame:NSRect {
get {
let rect:NSRect = NSRect(x: 0, y: 0, width: (self.frame.size.width * (_goodProgress / 100.0)), height: self.frame.size.height);
return rect;
}
}
private var okViewFrame:NSRect {
get {
let rect:NSRect = NSRect(x: self.goodViewFrame.size.width, y: 0, width: (self.frame.size.width * (_okProgress / 100.0)), height: self.frame.size.height);
return rect;
}
}
private var badViewFrame:NSRect {
get {
let width:CGFloat = (self.frame.size.width * (_badProgress / 100.0));
let rect:NSRect = NSRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height);
return rect;
}
}
override public init(frame frameRect: NSRect) {
super.init(frame: frameRect);
//--
self.commonInit();
}
required public init?(coder: NSCoder) {
super.init(coder: coder);
}
override public func awakeFromNib() {
super.awakeFromNib();
//--
self.commonInit();
}
private func commonInit() {
self.layer = CALayer();
self.layer!.cornerRadius = 15;
self.layer!.masksToBounds = true
//-
self.updateFrames();
}
public func updateProgress(goodProgressV:Int, okProgressV:Int, badProgressV:Int) {
guard ((goodProgressV okProgressV badProgressV) == 100) else {
NSLog("Total should be 100%");
return;
}
_goodProgress = CGFloat(goodProgressV);
_okProgress = CGFloat(okProgressV);
_badProgress = CGFloat(badProgressV);
//--
self.updateFrames();
}
private func updateFrames() {
self.layer?.backgroundColor = NSColor.grayColor().CGColor;
self.goodView.frame = self.goodViewFrame;
self.okView.frame = self.okViewFrame;
self.badView.frame = self.badViewFrame;
}
public override func resizeSubviewsWithOldSize(oldSize: NSSize) {
super.resizeSubviewsWithOldSize(oldSize);
//--
self.updateFrames();
}
}
Примечание: Вызов метода UpdateProgress() для изменения хода выполнения по умолчанию 33, 33 amp; 34 (33 33 34 = 100);
Вы также можете загрузить образец проекта по ссылке ниже;
Ответ №2:
Из документации drawRect(_ dirtyRect: NSRect)
:
dirtyRect: прямоугольник, определяющий часть представления, которая требует перерисовки. Этот прямоугольник обычно представляет часть представления, которая требует обновления. Когда включена адаптивная прокрутка, этот прямоугольник также может представлять невидимую часть представления, которую AppKit хочет кэшировать.
Не используйте dirtyRect
для своих вычислений, используйте self.bounds
.