Расположите CATextLayers вдоль Arc
Я пытаюсь создать пользовательский элемент управления, который может выглядеть примерно так:
Я делал вещи с drawRect()
но мне нужно оживить некоторые элементы, поэтому я решил переключиться на композицию CALayer
s. Я рисовал текст, используя 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
}