You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在UIView中水平居中CALayer?视频播放器偏移问题求助

视频播放器在Cell中居右而非居中,关闭translatesAutoresizingMaskIntoConstraints后消失的问题

已实现视频播放器功能,但视频显示在Cell右侧而非居中位置,尝试多种方法仍无法解决。关闭playerView.translatesAutoresizingMaskIntoConstraints = false后播放器直接消失。

img_thumb是视频加载前的缩略图容器,当前作为视频黑色背景,因contentView底部有额外元素,故借助其约束进行布局。

布局相关代码

playerView.frame = videoCell.img_thumb.frame
//                            playerView.layer.bounds = videoCell.img_thumb.bounds
//                            playerView.layer.frame = videoCell.img_thumb.frame
playerView.layer.position = CGPoint(x: videoCell.img_thumb.bounds.midX, y: videoCell.img_thumb.bounds.midY)
videoCell.contentView.addSubview(playerView)
playerView.translatesAutoresizingMaskIntoConstraints = false
playerView.bounds = videoCell.img_thumb.bounds
playerView.center = videoCell.img_thumb.center
playerView.centerXAnchor.constraint(equalTo: videoCell.img_thumb.centerXAnchor).isActive = true
playerView.centerYAnchor.constraint(equalTo: videoCell.img_thumb.centerYAnchor).isActive = true
playerView.heightAnchor.constraint(lessThanOrEqualTo: videoCell.img_thumb.heightAnchor).isActive = true
playerView.widthAnchor.constraint(lessThanOrEqualTo: videoCell.img_thumb.widthAnchor).isActive = true

附Storyboard布局、约束设置及实际显示效果截图

PlayerView 完整代码

import Foundation
import UIKit
import AVKit

class PlayerView: UIView {
    private var url: URL?
    private var urlAsset: AVURLAsset?
    private var playerItem: AVPlayerItem?
    var loaded: Bool = false
    var activityIndicator: UIActivityIndicatorView?
    
    private var assetPlayer:AVPlayer? {
        didSet {
            DispatchQueue.main.async {
                if let layer = self.layer as? AVPlayerLayer {
                    layer.player = self.assetPlayer
                }
            }
        }
    }
    
    override class var layerClass: AnyClass {
        return AVPlayerLayer.self
    }
    
    init() {
        super.init(frame: .zero)
        initialSetup()
    }
    
    required init?(coder: NSCoder) {
        super.init(frame: .zero)
        initialSetup()
    }
    
    private func initialSetup() {
        if let layer = self.layer as? AVPlayerLayer {
            // Do any configuration
            layer.videoGravity = AVLayerVideoGravity.resizeAspect
        }
    }
    
    func prepareToPlay(withUrl url:URL, shouldPlayImmediately: Bool = false) {
        guard !(self.url == url && assetPlayer != nil && assetPlayer?.error == nil) else {
            if shouldPlayImmediately {
                play()
            }
            return
        }
        
        cleanUp()
        
        self.url = url
        
        let options = [AVURLAssetPreferPreciseDurationAndTimingKey : true]
        let urlAsset = AVURLAsset(url: url, options: options)
        self.urlAsset = urlAsset
        
        let keys = ["tracks"]
        urlAsset.loadValuesAsynchronously(forKeys: keys, completionHandler: { [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.startLoading(urlAsset, shouldPlayImmediately)
        })
        NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
    }
    
    private func startLoading(_ asset: AVURLAsset, _ shouldPlayImmediately: Bool = false) {
        var error:NSError?
        let status: AVKeyValueStatus = asset.statusOfValue(forKey: "tracks", error: &error)
        if status == AVKeyValueStatus.loaded {
            let item = AVPlayerItem(asset: asset)
            self.playerItem = item
            
            let player = AVPlayer(playerItem: item)
            self.assetPlayer = player
            self.loaded = true
            
            if shouldPlayImmediately {
                DispatchQueue.main.async {
                    player.play()
                }
            }
        }
    }
    
    func toggle() {
        if self.assetPlayer?.isPlaying == false {
            play()
        } else {
            pause()
        }
    }
    
    func play() {
        guard self.assetPlayer?.isPlaying == false else { return }
//        if self.loaded && self.activityIndicator != nil {
//            self.activityIndicator?.stopAnimating()
//        }
        DispatchQueue.main.async {
            self.assetPlayer?.play()
        }
    }
    
    func pause() {
        guard self.assetPlayer?.isPlaying == true else { return }
        DispatchQueue.main.async {
            self.assetPlayer?.pause()
        }
    }
    
    func cleanUp() {
        pause()
        urlAsset?.cancelLoading()
        urlAsset = nil
        assetPlayer = nil
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
    }
    
    deinit {
        cleanUp()
    }
    
    @objc private func playerItemDidReachEnd(_ notification: Notification) {
        guard notification.object as? AVPlayerItem == self.playerItem else { return }
        DispatchQueue.main.async {
            guard let videoPlayer = self.assetPlayer else { return }
            videoPlayer.seek(to: .zero)
            videoPlayer.play()
        }
    }
}

内容的提问来源于stack exchange,提问作者Zach Handley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 00:47:33