CAShapeLayer с градиентом

#ios #swift #xcode

#iOS #swift #xcode

Вопрос:

У меня проблема с добавлением градиента в CAShapeLayer. Я создал слой circle и хочу добавить к нему градиент. Итак, я создал CAGradientLayer и установил для него рамки в качестве границ слоя и маску в качестве слоя, но она не отображается. Что я могу сделать?

 private func setupCircle() -> CAShapeLayer {
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: progressView.frame.size.width / 2.0, y: progressView.frame.size.height / 2.0), radius: (progressView.frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)

    let circleLayer = CAShapeLayer()
    circleLayer.path = circlePath.CGPath
    circleLayer.fillColor = nil
    circleLayer.strokeColor = UIColor.yellowColor().CGColor
    circleLayer.backgroundColor = UIColor.yellowColor().CGColor
    circleLayer.lineWidth = configurator.lineWidth
    circleLayer.strokeEnd = 1.0

    return circleLayer
}

private func setupGradient() -> CAGradientLayer {
    let gradient = CAGradientLayer()
    gradient.colors = [UIColor.clearColor().CGColor, UIColor.yellowColor().CGColor, UIColor.greenColor().CGColor]
    gradient.locations = [0.0, 0.25, 1.0]
    gradient.startPoint = CGPointMake(0, 0)
    gradient.endPoint = CGPointMake(1, 1)
    return gradient
}

let circle = setupCircle()
let gradient = setupGradient()

gradient.frame = circle.bounds
gradient.mask = circle

progressView.layer.addSublayer(circle)
progressView.layer.insertSublayer(gradient, atIndex: 0)
  

Редактировать:
Я изменил метод setupCircle на:

 private func setupCircle() -> CAShapeLayer {
    let circleLayer = CAShapeLayer()
    circleLayer.frame = progressView.bounds.insetBy(dx: 5, dy: 5)
    circleLayer.path = UIBezierPath(ovalInRect: CGRectMake(progressView.center.x, progressView.center.y, progressView.bounds.size.width, progressView.bounds.size.height)).CGPath
    circleLayer.fillColor = nil
    circleLayer.strokeColor = UIColor.yellowColor().CGColor
    circleLayer.backgroundColor = UIColor.yellowColor().CGColor
    circleLayer.lineWidth = configurator.lineWidth
    circleLayer.strokeEnd = 1.0

    return circleLayer
}
  

И вот эффект, я сделал это правильно?:

ПРАВКА 2: я изменил circleLayer.path = UIBezierPath(ovalInRect: CGRectMake(progressView.center.x, progressView.center.y, progressView.bounds.size.width, progressView.bounds.size.height)).CGPath на circleLayer.path = UIBezierPath(ovalInRect: circleLayer.bounds).CGPath , и это лучше, но все еще не то, что я хочу:

введите описание изображения здесь

Комментарии:

1. Какова область действия этой строки let circle = setupCircle() и кода ниже? это переменная экземпляра или локальная переменная в viewDidLoad или что-то еще?

2. Это в методе установки, который вызывается в viewDidLoad

3. Проверьте обновленный ответ, он должен устранить ваши проблемы.

Ответ №1:

Это нормально, что вы создаете слои и добавляете их в иерархию слоев в viewDidLoad , но вам не следует использовать там какую-либо фреймовую логику, поскольку макет в этот момент не обновляется. Попробуйте переместить весь код, который выполняет какие-либо вычисления фрейма в viewDidLayoutSubviews .

Это также следует делать при каждом обновлении макета, поэтому перемещайте его с setupCircle на viewDidLayoutSubviews :

 let circlePath = UIBezierPath(...)
circleLayer.path = circlePath.cgPath
  

Редактировать:

Также я не вижу, где вы устанавливаете circleLayer рамку? Недостаточно задать его путь. Если у вас есть CGRect.zero rect для маски, она полностью скроет родительский слой.

Я бы рекомендовал заменить это:

 let circlePath = UIBezierPath(arcCenter: CGPoint(x: progressView.frame.size.width / 2.0, y: progressView.frame.size.height / 2.0), radius: (progressView.frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
  

с помощью этого:

 circleLayer.bounds = progressView.frame.insetBy(dx: 5, dy: 5)
circleLayer.path = UIBezierPath(ovalIn: circleLayer.bounds).cgPath
  

Таким образом, у вас будут заданы и рамка, и контур. Также будьте осторожны с системой координат. Я не уверен насчет того, где находится progressView в вашей иерархии представлений, возможно, вам это понадобится bounds вместо этого.

Правка 2: Также вам следует удалить там несколько строк кода.

  1. Это делает ваш слой circle полностью непрозрачным, делает саму фигуру невидимой.

 circleLayer.backgroundColor = UIColor.yellowColor().CGColor 
  
  1. Вам не следует добавлять circle в качестве подслоя, поскольку вы собираетесь использовать его в качестве маски:

 progressView.layer.addSublayer(circle)
  

Комментарии:

1. Он по-прежнему не отображается

2. Только что обнаружил еще одну ошибку. Убедитесь, что вы установили рамку для mask слоя

3. Спасибо, я не устанавливал frame для layer. Как я могу установить фрейм на основе path?

4. @Woodgun11 Вам лучше сначала рассчитать фрейм, а затем создать path на основе этого фрейма.

5. Попробуйте UIBezierPath(ovalIn: circleLayer.bounds).cgPath вместо этого. Вам это больше не нужно progressView.frame .