Как создать IBDesignable пользовательский UIView из UIBezierPath? - PullRequest
0 голосов
/ 14 сентября 2018

Я хочу сформировать UIView и иметь возможность видеть его форму в Интерфейсном Разработчике, когда я устанавливаю следующий класс на UIView, который не может быть создан, я не уверен, где ошибка.

@IBDesignable
class CircleExampleView: UIView {

    override func layoutSubviews() {
        setupMask()
    }

    func setupMask() {

        let path = makePath()

        // mask the whole view to that shape
        let mask = CAShapeLayer()
        mask.path = path.cgPath
        self.layer.mask = mask
    }

    private func makePath() -> UIBezierPath {

        //// Oval Drawing
        let ovalPath = UIBezierPath(ovalIn: CGRect(x: 11, y: 12, width: 30, height: 30))
        UIColor.gray.setFill()
        ovalPath.fill()

        return ovalPath
    }
}

Ответы [ 2 ]

0 голосов
/ 14 сентября 2018

Пара наблюдений:

  1. Вы звоните setFill, а затем fill, но вы делаете это только при рисовании в графическом контексте (например, в draw(_:)).При использовании CAShapeLayer вместо этого следует установить fillColor из CAShapeLayer.

  2. Вы используете CAShapeLayer для установки маскиваш взгляд.Если у вашего UIView нет различимого backgroundColor, вы ничего не увидите.

    Если вы установите цвет фона вида, скажем, синий, как показано ниже, ваша маска будетпокажите этот синий фон везде, где маска позволяет (в овале вашего пути).

  3. Вы реализовали layoutSubviews.Обычно вы делаете это только в том случае, если вы делаете здесь что-то, что зависит от bounds представления.Например, вот представление, в котором овальный путь основан на bounds представления:

    @IBDesignable
    class CircleView: UIView {
        override func layoutSubviews() {
            super.layoutSubviews()
            setupMask()
        }
    
        private func setupMask() {
            let mask = CAShapeLayer()
            mask.path = path.cgPath
            mask.fillColor = UIColor.gray.cgColor
            layer.mask = mask
        }
    
        private var path: UIBezierPath {
            return UIBezierPath(ovalIn: bounds)
        }
    }
    
  4. Как сказал E. Coms, если вы переопределите layoutSubviews, выдействительно следует вызвать реализацию super.Это не критично, так как реализация по умолчанию ничего не делает, но это лучшая практика.Например, если вы позже изменили этот класс на подкласс какого-то другого подкласса UIView, вам не нужно будет пересматривать все эти переопределения.

  5. Если у вас есть проектируемое представление,желательно поместить это в отдельную цель.Таким образом, отображение представления в раскадровке не зависит от какой-либо работы, которая может выполняться в основном проекте.До тех пор, пока цель designables (часто имя вашей главной цели с суффиксом Kit) может создаваться, designable view может быть визуализирован.

Например, здесь приведено отображениеваш проектируемый вид, в отдельной целевой структуре и используемый в раскадровке, где рассматриваемый вид имеет синий цвет backgroundColor:

enter image description here


Для чего это стоит, я думаю, что это очень сбивает с толку необходимость маскировать, чтобы показать цвет фона внутри овала.Разработчик приложения должен установить «фоновый» цвет, чтобы установить то, что находится внутри овала, но не фон.

Я мог бы вместо этого удалить логику «маски» и вместо этого дать проектируемому представлению проверяемое свойство, fillColor, и просто добавьте CAShapeLayer в качестве подслоя, используя этот fillColor:

@IBDesignable
class CircleView: UIView {

    private var shapeLayer = CAShapeLayer()

    @IBInspectable var fillColor: UIColor = .blue {
        didSet {
            shapeLayer.fillColor = fillColor.cgColor
        }
    }

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)

        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        configure()
    }

    private func configure() {
        shapeLayer.fillColor = fillColor.cgColor
        layer.addSublayer(shapeLayer)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        shapeLayer.path = UIBezierPath(ovalIn: bounds).cgPath
    }
}

Это делает то же самое, но я думаю, что различие между цветами заливки и цветами фона более интуитивно понятно.Но у вас могут быть другие причины для использования маскирующего подхода, но просто убедитесь, что если вы это сделаете, вам есть что показать после маскировки (например, цвет фона или что-то еще, что вы визуализируете).

0 голосов
/ 14 сентября 2018

Пожалуйста, добавьте "super.layoutSubviews ()"

override func layoutSubviews() {
    setupMask()
    super.layoutSubviews()
}

Таким образом, ваше представление дизайна будет "актуальным".

...