I found different posts where people have problems to show CATextLayer
like this or that.
The solution from the threads unfortunately only partially work for me. My problem is that my CATextLayer
sometimes show text and sometimes not.
The structure: I have an AVSynchronizedLayer
, on this I add a CATextLayer
.
For illustration the AVSynchronizedLayer
has a yellow background. The CATextLayer
has a red background with font color cyan
.
let synchronizedLayer = AVSynchronizedLayer(playerItem: playerItem)
synchronizedLayer.frame = CGRect(x: 0, y: 0, width: 500, height: 500)
synchronizedLayer.opacity = 1.0
synchronizedLayer.beginTime = 0.5
synchronizedLayer.backgroundColor = UIColor.yellow.cgColor
The text layer:
let textlayer = CATextLayer()
textlayer.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
textlayer.fontSize = 20
let myAttributes = [
NSAttributedString.Key.font: UIFont(name: "Chalkduster", size: 30.0)! ,
NSAttributedString.Key.foregroundColor: UIColor.cyan,
NSAttributedString.Key.backgroundColor: UIColor.red
]
let myAttributedString = NSAttributedString(string: "My text", attributes: myAttributes )
textlayer.alignmentMode = .center
textlayer.string = myAttributedString
textlayer.isWrapped = true
textlayer.beginTime = 1.0
textlayer.opacity = 1.0
textlayer.truncationMode = .end
What I am doing with the layers
// playerItem is a bundle of 5 short movies (3 seconds long). Between every video is a opacity transition
let player = AVPlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: player)
// [...] config for playerLayer
synchronizedLayer.addSublayer(textlayer)
playerLayer.addSublayer(synchronizedLayer)
return playerLayer // to the view
I use SwiftUI (min version iOS 15). But SwiftUI does not offer the possibility to use an 'AVPlayer' directly. I had to build a kind of bridge.
struct CustomPlayerView: UIViewRepresentable {
let playerLayer: AVPlayerLayer
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<CustomPlayerView>) {
}
func makeUIView(context: Context) -> UIView {
let customPlayerUIView = CustomPlayerUIView(frame: .zero)
customPlayerUIView.initPlayer(playerLayer: playerLayer)
return customPlayerUIView
}
}
The initPlayer
does the following thing
class CustomPlayerUIView: UIView {
private var playerLayer: AVPlayerLayer?
override init(frame: CGRect) {
super.init(frame: frame)
}
func initPlayer(playerLayer: AVPlayerLayer) {
self.playerLayer = playerLayer
layer.addSublayer(playerLayer)
playerLayer.player?.play()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
}
}
What always appears is a yellow box (AVSynchronizedLayer
) and a red box (CATextLayer
). Only the text is always displayed randomly.
I am quite sure it's a lifecycle or rendering problem. But I don't know exactly what the correct lifecycle is. I have almost everywhere already layoutIfNeeded()
or displayIfNeeded()
called. But this had no effect.