Swift: добавить изображение в CAShapeLayer

У меня есть CAShapeLayer с цветом заливки, и я хочу добавить значок в центре этой формы:

var shape = CAShapeLayer()
shape.fillColor = UIColor(white: 0.90, alpha: 1).CGColor
var image = UIImage(named: "some_image")
shape.contents = image?.CGImage

Этот код компилируется и показывает серую форму, но без изображения. :(

Я просмотрел эти сообщения, но я не могу использовать CGImage, как они.

добавить UIImage в CALayer и Отображать изображение или UIImage с простым CALayer

Есть другой способ? Или обходной путь для id cast?

Полный код:

ViewController.swift

import UIKit

class ViewController: UIViewController {

    @IBOutlet var menuView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func viewDidAppear(animated: Bool) {

        let menu: CircularMenu = CircularMenu(frame: menuView.bounds, numberOfMenuItems: 4, hasProgressBar: false)
        self.menuView.addSubview(menu)
    }

}

CircularMenu.swift

import UIKit

// *** Helper methods start
func DegreesToRadians (value: Double) -> CGFloat {
    return CGFloat(value * M_PI / 180.0)
}

func RadiansToDegrees (value: Double) -> CGFloat {
    return CGFloat(value * 180.0 / M_PI)
}
// *** Helper methods end

class CircularMenu: UIControl {

    var parent: UIViewController!

    var centerButton: UIButton!
    var menuButtonShapes = [CAShapeLayer]()
    var menuButtons = [UIButton]()

    var numberOfButtons: Double = 4

    var bigSize: CGFloat = 300
    var centerButtonSize: CGFloat = 100
    var centerButtonPadding: CGFloat = 70
    let paddingBetweenButtons = 1

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    convenience init(frame: CGRect, numberOfMenuItems: Int, hasProgressBar: Bool = false) {
        self.init(frame: frame)
        self.bounds = frame

        self.bigSize = frame.width
        self.centerButtonSize = bigSize/2
        self.centerButtonPadding = bigSize/4-5

        if hasProgressBar {
            self.centerButtonSize -= 10
        }

        if numberOfMenuItems != 0 && numberOfMenuItems <= 10 {
            self.numberOfButtons = Double(numberOfMenuItems)
        }

        // Draw center button
        var centerButtonView = UIView(frame: CGRectMake(0, 0, centerButtonSize, centerButtonSize))
        centerButtonView.backgroundColor = UIColor(red: 160.0/255, green: 197.0/255, blue: 25.0/255, alpha: 1)

        centerButtonView.center = self.center;
        centerButtonView.layer.cornerRadius = centerButtonSize/2
        self.addSubview(centerButtonView)
        self.centerButton = UIButton(frame: centerButtonView.bounds)


        let degreesPerButton: Double = 360.0/numberOfButtons

        // Draw button shapes
        for (var i: Double = 1; i <= numberOfButtons; i++)
        {
            // Determine start and end radians of pizza slice
            let startAngle = DegreesToRadians(degreesPerButton*i+degreesPerButton-Double(paddingBetweenButtons))
            let endAngle = DegreesToRadians(degreesPerButton*i+Double(paddingBetweenButtons))

            // Determine center and angle of slice
            var buttonCenter = self.center;
            var offsetDirection = (startAngle - endAngle)/2 + endAngle

            // Shove slice outward to make space for inner padding
            var offsetX = cos(offsetDirection)*tan(DegreesToRadians(Double(paddingBetweenButtons)))*bigSize/2
            var offsetY = sin(offsetDirection)*tan(DegreesToRadians(Double(paddingBetweenButtons)))*bigSize/2

            buttonCenter.x += offsetX
            buttonCenter.y += offsetY

            // Create mask for outer rim of slice to a circular form (from triangle to pizza slice)
            let outerCircleRadius = bigSize/2;
            var bigCircle = UIBezierPath(arcCenter: self.center, radius: bigSize/2, startAngle: startAngle , endAngle: endAngle, clockwise: false);
            bigCircle.addLineToPoint(buttonCenter)
            bigCircle.closePath()

            // Take padding into consideration when creating mask
            let tmpBigSizeX = (bigSize+2*abs(offsetX))
            let tmpBigSizeY = (bigSize+2*abs(offsetY))

            // Create mask for inner rim of slice to a circular form (from pizza slice to donut slice)
            let smallSize = bigSize-centerButtonSize-centerButtonPadding
            let innerCircleRadius = outerCircleRadius-bigSize/2/2;
            var smallCircle = UIBezierPath(arcCenter: self.center, radius: smallSize, startAngle: 0 , endAngle: DegreesToRadians(360), clockwise: false);

            // Apply masks for everything but the donut slice shape
            var buttonShape = CAShapeLayer()
            var maskLayer = CAShapeLayer()
            var maskPath = CGPathCreateMutable();

            CGPathAddRect(maskPath, nil, CGRectMake(outerCircleRadius-tmpBigSizeX/2, outerCircleRadius-tmpBigSizeY/2, tmpBigSizeX, tmpBigSizeY));
            CGPathAddPath(maskPath, nil, smallCircle.CGPath);
            maskLayer.path = maskPath;
            maskLayer.fillRule = kCAFillRuleEvenOdd;

            buttonShape.path = bigCircle.CGPath
            buttonShape.mask = maskLayer
            buttonShape.fillColor = UIColor(white: 0.90, alpha: 1).CGColor

            // Add icons and labels to the button
            var image = UIImage(named: "menu-icon_users_grey")
            let imageSubLayer = CALayer()
            imageSubLayer.contents = image?.CGImage
            buttonShape.addSublayer(imageSubLayer)

            // Save references to both shape and button
            self.menuButtons.append(UIButton(frame: buttonShape.frame))
            self.menuButtonShapes.append(buttonShape)

            // Add shape to view
            self.layer.addSublayer(buttonShape)
        }
    }

}

Интересующие строки находятся внизу пользовательского UIControl под комментарием:

«Добавить значки и надписи на кнопку»


person Anfaje    schedule 26.03.2015    source источник
comment
Почему вы ожидаете, что слой будет рисовать изображение поверх цвета заливки? Попробуйте добавить подслой к этому слою и добавьте к нему изображение.   -  person Jack    schedule 26.03.2015
comment
Неа, пробовал но все равно не рисуется ... :(   -  person Anfaje    schedule 26.03.2015
comment
Можете ли вы показать код, который используете? Нет причин не рисовать. Для CALayers вам необходимо установить границы соответствующим образом btw.   -  person Jack    schedule 26.03.2015
comment
Спасибо @JackWu, я загрузил весь класс, если вы хотите опробовать его в своем собственном проекте. :) Надеюсь, ты сможешь помочь!   -  person Anfaje    schedule 27.03.2015


Ответы (1)


У вас есть два варианта:

Не очень хороший, преобразуйте UIImage в цвет, а затем передайте его в CGColor, но поскольку ваш фрагмент кода такой маленький, я не знаю, что будет работать:

let image = UIImage(named: "")
if let realImage = image {
    let color = UIColor(patternImage: realImage)
    shape.fillColor = color.CGColor
}

И рекомендуемый, создайте новый слой и установите UIImage для его содержимого:

let imageSubLayer = CALayer()
imageSubLayer.contents = image?.CGImage
shape.addSublayer(imageSubLayer)

Я не могу проверить это прямо сейчас, поэтому скажите мне, работает ли он.

person GBF_Gabriel    schedule 26.03.2015
comment
Нет. Рекомендуемое решение не работает, по крайней мере, на TViOS под управлением 10.x. - person user3069232; 20.10.2016
comment
Я не тестирую его на tvOS, но в iOS вам нужно установить рамку для этого imageSublayer, чтобы работать - person Laszlo; 20.04.2017