Пользовательский вид нарушается при изменении размера окна (Cocoa)

#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);

Вы также можете загрузить образец проекта по ссылке ниже;

http://www.filedropper.com/osxtest

Ответ №2:

Из документации drawRect(_ dirtyRect: NSRect) :

dirtyRect: прямоугольник, определяющий часть представления, которая требует перерисовки. Этот прямоугольник обычно представляет часть представления, которая требует обновления. Когда включена адаптивная прокрутка, этот прямоугольник также может представлять невидимую часть представления, которую AppKit хочет кэшировать.

Не используйте dirtyRect для своих вычислений, используйте self.bounds .