Расположите CATextLayers вдоль Arc

Я пытаюсь создать пользовательский элемент управления, который может выглядеть примерно так:

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

Я делал вещи с drawRect() но мне нужно оживить некоторые элементы, поэтому я решил переключиться на композицию CALayers. Я рисовал текст, используя CGContextRef функции, но переключившись на CATextLayer для каждого персонажа я не могу правильно их преобразовать.

Мой общий подход заключался в создании CATextLayer для каждого персонажа. Я думаю, что я могу использовать каждый слой preferredFrameSize() чтобы получить возможный размер персонажа. И тогда я подумал, что я отрегулирую их положение и вращение в моем контроле layoutSubviews() метод.

"Что я пробовал?" Я чувствую себя так, словно только что сижу сижу с завязанными глазами, играю в дротики. Вы называете это, я попробовал это.

Я могу определить угол поворота для каждого персонажа примерно так:

let baseline = (self.ringBox.width * 3 / 8) // radius out to the baseline arc
let circumference = baseline * Tau
var advance = wordWidth.half.negated // wordWidth was summed from the width of each character earlier
let angle = 0.75.rotations
for each in self.autoCharacters {
    let charSize = each.bounds.size
    advance += charSize.width.half
    let charRotation = (advance / circumference - 0.75).rotations + angle
    ...
}

Я заметил что layoutSubviews() кажется, называется дважды. Зачем? Я заметил, что во второй раз preferredFrameSize() кажется тоньше Зачем? Было ли это потому, что я устанавливал affineTransform и это имело кумулятивный эффект. Сначала я пытался установить положение слоя в центре родительского блока, а также границы для предпочитаемого фрейма, надеясь, что он будет располагаться по центру. А затем применить преобразования. Несмотря на многочисленные попытки, я получаю странное размещение, а также странное отсечение.

Так что я просто ищу простой / простой рецепт, который будет позиционировать CATextLayer на заданный радиус / угол от центра зрения.

1 ответ

Решение

В конце я написал отдельное приложение с ползунками для всех:

  • происхождение кадра
  • Размер рамки
  • границы происхождения
  • размер границ
  • позиция
  • anchorPoint
  • Преобразование перед вращением
  • преобразовать вращение
  • преобразование после поворота

А также некоторая отладочная информация, которая показала preferredFrameSize() слоя. Вооружившись этим, способным манипулировать вещами и видеть эффекты, я смог сделать это довольно просто.

После создания одного CATextLayer для каждого символа в данном слове (набирается как [CATextLayer]), Я использовал следующий метод для позиционирования символов слова:

// pass 1 to:
// - baseline each characters rotation
// - size it and center it
// - accumulate the length of the whole word
var wordWidth:CGFloat = 0.0
for letter in word {
    // baseline the transform first, because preferredFrameSize() is affected by any latent rotation in the current transform
    letter.setAffineTransform(CGAffineTransformIdentity)
    // the preferredFrameSize will now be the un rotated size of the single character
    let size = letter.preferredFrameSize()
    // move the letter to the center of the view
    letter.frame = CGRect(origin: center - size.half, size: size)
    // accumulate the length of the word as the length of characters
    wordWidth += size.width
}
let Tau = CGFloat(M_PI * 2)
// the length of the circle we want to be relative to
let circumference = radius * Tau
// back up the "advance" to half of the word width
var advance = wordWidth.half.negated
for letter in word {
    // retrieve the character dimensions
    let charSize = letter.bounds.size
    // advance half of the character, so we're at its center
    advance += charSize.width.half
    // determine the angle to rotate off of the nominal (which is 270 degrees)
    let charRotation = (advance / circumference * Tau - (Tau * 3 / 4)) + angle
    // start with an identity transform now
    var transform = CGAffineTransformIdentity
    // rotate it. this will rotate about the center of the character
    transform = transform.rotated(charRotation)
    // translate outwards to the ring
    transform = transform.translated(CGPoint(x: 0, y: radius.negated))
    // apply the transform
    letter.setAffineTransform(transform)
    // move the advance the other half of this character
    advance += charSize.width.half
}
Другие вопросы по тегам