Ios 无法为自定义UIButton添加NSLayoutControints

Ios 无法为自定义UIButton添加NSLayoutControints,ios,swift,uibutton,Ios,Swift,Uibutton,不知我做错了什么,这里是我的自定义UIButton: import UIKit class IteratorChevronButton: UIButton { required init() { super.init(frame: .zero) self.setBackgroundImage(UIImage(named: "icon-chevron-right"), for: .normal) } required init?(co

不知我做错了什么,这里是我的自定义UIButton:

import UIKit

class IteratorChevronButton: UIButton {

    required init() {
        super.init(frame: .zero)

        self.setBackgroundImage(UIImage(named: "icon-chevron-right"), for: .normal)
    }

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

}
在UIView类中使用IteratorChevronButton:

var btnNext: IteratorChevronButton {
    let btn = IteratorChevronButton()
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}

func doInit()  {
    self.addSubview(btnNext)

    self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .width, relatedBy: .equal,
                                          toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
    self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .height, relatedBy: .equal,
                                          toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
    self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0))
    self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: 10))
}
import UIKit
import AVFoundation
import RealmSwift

enum PlayerError {
    case unknownError
}
class Player: UIView {
    let circularSliderVerticalPostionString:String = "75"
    let circularSliderWidthString:String = "180"
    let circularSliderHeightString = "180"
    var circularSliderWidth:CGFloat!
    var circularSliderHeight:CGFloat!
    let uiImageIconClose = UIImage(named: "icon-close")
    var movieDimension: CGSize = CGSize.zero
    var imageGenerator: AVAssetImageGenerator!
    var duration: CMTime = CMTimeMake(0, 30)
    var avPlayerLayer: AVPlayerLayer?
    var avPlayer: AVPlayer!
    var startedDragging: Bool = false
    var ready: Bool = false
    var gForce: Double = 0.0
    var isInDoublePlayer:Bool = false //used as a User Runtime Define Attribute in DoublePlayerViewController.xib

    lazy var canvas: DrawingLayerView = {
       let dv = DrawingLayerView()
        return dv
    }()

    //Set this variable to swithch between normal playback and slow mo
    var playSlowMo: Bool {
        get {
            return playerToolBar.playUsingTimer
        }
        set {
            playerToolBar.playUsingTimer = newValue
        }
    }

    //This when set the playback will resume after user stop dragging... I think its worth showing to
    //some of the customers, if I were a player I would like it to be like this :)
    var continuePlaybackWhenUserStopDragging: Bool {
        get {
            return playerToolBar.autoPlayWhenStopDragging
        }
        set {
            playerToolBar.autoPlayWhenStopDragging = newValue
        }
    }

    var playbackComlete: ((_ error: PlayerError?) -> Void)? = nil

    lazy var controlBarSize: CGSize = {
        return CGSize(width: self.bounds.width*3/4, height: 100)
    }()

    lazy var playerToolBar: PlayerToolBar = {[unowned self] in
        let bar = PlayerToolBar(frame: CGRect.zero)
        bar.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(bar)
        return bar
    }()

    let controlsBar: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var closeButton: UIButton = {
        let btn = ExtendedBoundsButton(type: .custom)
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.setImage(self.uiImageIconClose, for: UIControlState())
        btn.setTitleColor(UIColor.blue, for: UIControlState())
        btn.isHidden = true
        self.addSubview(btn)
        return btn
    }()

    lazy var progressLabel: UILabel = {
        let label = UILabel()
        label.textColor = UIColor.white
        return label
    }()

    var btnNext: IteratorChevronButton {
        let btn = IteratorChevronButton()
        btn.translatesAutoresizingMaskIntoConstraints = false
        return btn
    }

    lazy var chevronImageRight: UIImageView = {
        let image = UIImage(named:"icon-chevron-right")!
        let imageView = UIImageView(image: image)
        imageView.contentMode = .scaleToFill
        imageView.translatesAutoresizingMaskIntoConstraints = false

        return imageView
    }()

    lazy var circularSlider: BWCircularSliderView = {
        let cs = BWCircularSliderView()
        cs.translatesAutoresizingMaskIntoConstraints = false
        cs.frame.size.width = self.circularSliderWidth
        cs.frame.size.height = self.circularSliderHeight
        return cs
    }()

    var exporter: AVAssetExportSession? = nil
    var autoPlay: Bool = false
    var progressTimer: Timer?
    var movieDidPlay: (()->Void?)? = nil



    var onTap: (()-> Void)? 

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

        doInit()
    }

    func doInit()  {
        self.circularSliderWidth = CGFloat(Int(circularSliderWidthString)!)
        self.circularSliderHeight = CGFloat(Int(circularSliderHeightString)!)
        self.addSubview(chevronImageRight)
        self.addSubview(progressLabel)
        self.addSubview(circularSlider)
        self.addSubview(btnNext)

        self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .width, relatedBy: .equal,
                                              toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
        self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .height, relatedBy: .equal,
                                              toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
        self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0)
)
        self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: 10))

        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(circularSliderVerticalPostionString)-[circularSlider(\(circularSliderWidthString))]", options: [], metrics: nil, views: ["circularSlider": circularSlider]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[circularSlider(\(circularSliderHeightString))]|", options: [], metrics: nil, views: ["circularSlider": circularSlider]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[toolbar(100)]-0-|", options: [], metrics: nil, views: ["toolbar": playerToolBar]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[toolbar]|", options: [], metrics: nil, views: ["toolbar": playerToolBar]))

        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-35-[btn(40)]", options: [], metrics: nil, views: ["btn": closeButton]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[btn(40)]-10-|", options: [], metrics: nil, views: ["btn": closeButton]))

        closeButton.addTarget(self, action: #selector(onClose), for: .touchUpInside)

    }

    func onClose() {
        if !ready {
            return
        }

        if let periodicTimeObserver = playerToolBar.periodicTimeObserver {
            self.avPlayer.removeTimeObserver(periodicTimeObserver)
        }
        self.avPlayer.pause()
        progressTimer?.invalidate()
        playbackComlete?(nil)

    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        doInit()
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        playerToolBar.isInDoublePlayer = self.isInDoublePlayer
        self.circularSlider.isHidden = self.isInDoublePlayer

        self.circularSlider.gForce = self.gForce

        if let avPlayerLayer = avPlayerLayer {
            avPlayerLayer.bounds = self.bounds
            avPlayerLayer.position = CGPoint(x: self.bounds.width/2, y: self.bounds.height/2)

            playerToolBar.avPlayer = avPlayer
            playerToolBar.setupMovieScrollBar()
            if autoPlay {
                autoPlay = false
                play()
            }
            movieDidPlay?()
        }
        progressLabel.frame = CGRect(x: frame.size.width/2-100, y: frame.size.height/2-15, width: 200, height: 30)
        addSubview(canvas)
        addSubview(playerToolBar)
        addSubview(closeButton)
        canvas.frame = bounds
    }

    func onExportTimer(_ sender: AnyObject)  {
        guard let exporter = exporter else {
            return
        }
        progressLabel.text = "Processing " + String(Int(exporter.progress*100) ) + "%"
    }

    func mergeFiles(_ items: [String], assetWithOnset: String?,  mergeComplete: @escaping (_ fileName: String?)->Void) -> Void {
        if (assetWithOnset == nil) {
            mergeComplete(items.first!)
            return
        }
        let composition = AVMutableComposition()
        let track:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
        var insertTime = kCMTimeZero

        for item in items {
            let sourceAsset = AVAsset(url: URL(fileURLWithPath: FileUtility.getPathForFileMovieDirectory(item)))
            let tracks = sourceAsset.tracks(withMediaType: AVMediaTypeVideo)
            print("\(item) \(sourceAsset.isPlayable)") // print true
            print(sourceAsset.isExportable) // print true
            print(sourceAsset.isReadable) // print true
            if tracks.count > 0 {
                let assetTrack:AVAssetTrack = tracks[0] as AVAssetTrack
                do {
                    try track.insertTimeRange(CMTimeRangeMake(kCMTimeZero,sourceAsset.duration), of: assetTrack, at: insertTime)
                    insertTime = CMTimeAdd(insertTime, sourceAsset.duration)
                } catch {
                    mergeComplete(nil)
                    return
                }
            }
        }

        let fusedFileName = "fused_" + assetWithOnset!
        let fusedFilePath = FileUtility.getPathForFileMovieDirectory(fusedFileName)
        let fusedFileUrl = URL(fileURLWithPath: fusedFilePath)

        do {
            //in case the file merging fails, the residual file will cause
            //the file export fail everytime as the file exist 
            try FileManager.default.removeItem(atPath: fusedFilePath)
        } catch {

        }

        exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPreset1280x720)
        guard let exporter = exporter  else {
            return
        }
        exporter.outputURL = fusedFileUrl
        exporter.outputFileType = AVFileTypeQuickTimeMovie
        progressTimer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(onExportTimer(_:)), userInfo: nil, repeats: true);

        exporter.exportAsynchronously(completionHandler: {
            switch exporter.status{
            case  AVAssetExportSessionStatus.failed:
                if exporter.error != nil {
                    print("AVAssetExportSession failed \(exporter.error!)")
                }else{
                   print("AVAssetExportSession failed for unknown reason")
                }
                mergeComplete(nil)
            case AVAssetExportSessionStatus.cancelled:
                if exporter.error != nil {
                    print("AVAssetExportSession canceled \(exporter.error!)")
                }else{
                    print("AVAssetExportSession canceled for unknown reason")
                }
                mergeComplete(nil)
            default:

                do {
                    let realm = try Realm()
                    let movieClip = realm.object(ofType: MovieModel.self, forPrimaryKey: assetWithOnset)
                    try realm.write {
                        movieClip?.fusedFile = fusedFileName
                    }
                    //The files are released based on the usage count
                    MovieRepository.sharedInstance.release(file: movieClip?.fileName)
                    MovieRepository.sharedInstance.release(file: movieClip?.nextFile)
                    MovieRepository.sharedInstance.release(file: movieClip?.prevFile)
                } catch {

                }
                mergeComplete(fusedFileName)
                self.progressLabel.text = ""
                self.progressLabel.isHidden = true
                NotificationUtility.notifyReloadGallery()
            }
        })

    }

    func setMovies(_ items: [String], itemWithOnset asset: String?, playbackCompletion completion: @escaping ((_ error: PlayerError?) -> Void)){
        playbackComlete = completion
        closeButton.isHidden = false
        mergeFiles(items, assetWithOnset: asset ) { [weak self] (fileName) in
            DispatchQueue.main.async(execute: { () -> Void in

                if let fileName = fileName, let strongSelf = self {
                    let asset = AVAsset(url: URL(fileURLWithPath: FileUtility.getPathForFileMovieDirectory(fileName)))
                    let avplayerItem = AVPlayerItem(asset: asset)
                    strongSelf.progressTimer?.invalidate()
                    strongSelf.progressLabel.removeFromSuperview()
                    strongSelf.duration = asset.duration
                    strongSelf.avPlayer = AVPlayer(playerItem: avplayerItem)
                    if let playerLayer = strongSelf.avPlayerLayer {
                        playerLayer.removeFromSuperlayer()
                    }
                    strongSelf.avPlayerLayer = AVPlayerLayer(player: strongSelf.avPlayer)
                    strongSelf.avPlayerLayer?.zPosition = -1 //send to back
                    strongSelf.self.layer.addSublayer(strongSelf.avPlayerLayer!)


                    NotificationCenter.default.addObserver(strongSelf, selector: #selector(Player.currentFileDidFinish(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: avplayerItem)
                    print("Duration \(Float(CMTimeGetSeconds(strongSelf.duration)))")
                    print("Size \(strongSelf.movieDimension)")
                    strongSelf.ready = true
                    strongSelf.autoPlay = true;
                    strongSelf.setNeedsLayout()

                }
            })

        }

    }

    func setMovie(movieAsset: MovieModel, completion: @escaping ()->Void) {

        movieDidPlay = completion
        autoPlay = true
        playerToolBar.playeBackTimer?.invalidate()
        playerToolBar.playeBackTimer = nil
        var clipNames: [String]
        var assetWithOnset: String? = nil
        if let fusedFile = movieAsset.fusedFile {
            clipNames = [fusedFile]
        } else {
            assetWithOnset = movieAsset.fileName
            if let nextFile = movieAsset.nextFile {
                clipNames = [movieAsset.prevFile!, movieAsset.fileName!, nextFile]
            } else {
                clipNames = [movieAsset.prevFile!, movieAsset.fileName!]
            }
        }
        self.setMovies(clipNames, itemWithOnset: assetWithOnset, playbackCompletion: { (err) in

        })
        closeButton.isHidden = true
    }
    func resolutionSizeForVideo(_ asset:AVAsset) -> CGSize? {
        guard let track = asset.tracks(withMediaType: AVMediaTypeVideo).first else { return nil }
        let size = track.naturalSize.applying(track.preferredTransform)
        return CGSize(width: fabs(size.width), height: fabs(size.height))
    }
    //MARK: The playback methods
    func pause(){
        if ready {
            playerToolBar.pause()
        }
    }
    func play() {
        if ready {
            playerToolBar.play()
        }
    }

    func currentFileDidFinish(_ notification: Notification) {
    /*    if let periodicTimeObserver = playerToolBar.periodicTimeObserver {
            self.avPlayer.removeTimeObserver(periodicTimeObserver)
        }
        progressTimer?.invalidate()
        playbackComlete?(error: nil)*/

        avPlayer.seek(to: CMTimeMake(0, 30))
        avPlayer.rate = 1.0
    }

    func stop() {
        avPlayer?.pause()
        avPlayer = nil

        avPlayerLayer?.removeFromSuperlayer()
        avPlayerLayer = nil
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    //MARK:
}

class CollectionViewThumbNailCell: UICollectionViewCell {

    lazy var barView: UIView = {
        let lbl = UIView()
        lbl.contentMode = .scaleToFill
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.backgroundColor = UIColor.white
        lbl.layer.cornerRadius = 2
        lbl.clipsToBounds = true
        return lbl
    }()
    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(barView)
    }

    func configureMark(_ big: Bool) {
        if big {
            barView.frame = CGRect(x: bounds.size.width/2 - 2, y: 2, width: 4, height: bounds.size.height)
        } else {
            barView.frame = CGRect(x: bounds.size.width/2 - 2, y: bounds.size.height/2+2, width: 4, height: bounds.size.height/2)
        }
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
我得到以下错误:

我试图使btnNext变为懒惰,但出现以下错误:

以下是我的自定义UIView类的代码:

var btnNext: IteratorChevronButton {
    let btn = IteratorChevronButton()
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}

func doInit()  {
    self.addSubview(btnNext)

    self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .width, relatedBy: .equal,
                                          toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
    self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .height, relatedBy: .equal,
                                          toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
    self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0))
    self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: 10))
}
import UIKit
import AVFoundation
import RealmSwift

enum PlayerError {
    case unknownError
}
class Player: UIView {
    let circularSliderVerticalPostionString:String = "75"
    let circularSliderWidthString:String = "180"
    let circularSliderHeightString = "180"
    var circularSliderWidth:CGFloat!
    var circularSliderHeight:CGFloat!
    let uiImageIconClose = UIImage(named: "icon-close")
    var movieDimension: CGSize = CGSize.zero
    var imageGenerator: AVAssetImageGenerator!
    var duration: CMTime = CMTimeMake(0, 30)
    var avPlayerLayer: AVPlayerLayer?
    var avPlayer: AVPlayer!
    var startedDragging: Bool = false
    var ready: Bool = false
    var gForce: Double = 0.0
    var isInDoublePlayer:Bool = false //used as a User Runtime Define Attribute in DoublePlayerViewController.xib

    lazy var canvas: DrawingLayerView = {
       let dv = DrawingLayerView()
        return dv
    }()

    //Set this variable to swithch between normal playback and slow mo
    var playSlowMo: Bool {
        get {
            return playerToolBar.playUsingTimer
        }
        set {
            playerToolBar.playUsingTimer = newValue
        }
    }

    //This when set the playback will resume after user stop dragging... I think its worth showing to
    //some of the customers, if I were a player I would like it to be like this :)
    var continuePlaybackWhenUserStopDragging: Bool {
        get {
            return playerToolBar.autoPlayWhenStopDragging
        }
        set {
            playerToolBar.autoPlayWhenStopDragging = newValue
        }
    }

    var playbackComlete: ((_ error: PlayerError?) -> Void)? = nil

    lazy var controlBarSize: CGSize = {
        return CGSize(width: self.bounds.width*3/4, height: 100)
    }()

    lazy var playerToolBar: PlayerToolBar = {[unowned self] in
        let bar = PlayerToolBar(frame: CGRect.zero)
        bar.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(bar)
        return bar
    }()

    let controlsBar: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    lazy var closeButton: UIButton = {
        let btn = ExtendedBoundsButton(type: .custom)
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.setImage(self.uiImageIconClose, for: UIControlState())
        btn.setTitleColor(UIColor.blue, for: UIControlState())
        btn.isHidden = true
        self.addSubview(btn)
        return btn
    }()

    lazy var progressLabel: UILabel = {
        let label = UILabel()
        label.textColor = UIColor.white
        return label
    }()

    var btnNext: IteratorChevronButton {
        let btn = IteratorChevronButton()
        btn.translatesAutoresizingMaskIntoConstraints = false
        return btn
    }

    lazy var chevronImageRight: UIImageView = {
        let image = UIImage(named:"icon-chevron-right")!
        let imageView = UIImageView(image: image)
        imageView.contentMode = .scaleToFill
        imageView.translatesAutoresizingMaskIntoConstraints = false

        return imageView
    }()

    lazy var circularSlider: BWCircularSliderView = {
        let cs = BWCircularSliderView()
        cs.translatesAutoresizingMaskIntoConstraints = false
        cs.frame.size.width = self.circularSliderWidth
        cs.frame.size.height = self.circularSliderHeight
        return cs
    }()

    var exporter: AVAssetExportSession? = nil
    var autoPlay: Bool = false
    var progressTimer: Timer?
    var movieDidPlay: (()->Void?)? = nil



    var onTap: (()-> Void)? 

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

        doInit()
    }

    func doInit()  {
        self.circularSliderWidth = CGFloat(Int(circularSliderWidthString)!)
        self.circularSliderHeight = CGFloat(Int(circularSliderHeightString)!)
        self.addSubview(chevronImageRight)
        self.addSubview(progressLabel)
        self.addSubview(circularSlider)
        self.addSubview(btnNext)

        self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .width, relatedBy: .equal,
                                              toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
        self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .height, relatedBy: .equal,
                                              toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
        self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0)
)
        self.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1.0, constant: 10))

        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(circularSliderVerticalPostionString)-[circularSlider(\(circularSliderWidthString))]", options: [], metrics: nil, views: ["circularSlider": circularSlider]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[circularSlider(\(circularSliderHeightString))]|", options: [], metrics: nil, views: ["circularSlider": circularSlider]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[toolbar(100)]-0-|", options: [], metrics: nil, views: ["toolbar": playerToolBar]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[toolbar]|", options: [], metrics: nil, views: ["toolbar": playerToolBar]))

        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-35-[btn(40)]", options: [], metrics: nil, views: ["btn": closeButton]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:[btn(40)]-10-|", options: [], metrics: nil, views: ["btn": closeButton]))

        closeButton.addTarget(self, action: #selector(onClose), for: .touchUpInside)

    }

    func onClose() {
        if !ready {
            return
        }

        if let periodicTimeObserver = playerToolBar.periodicTimeObserver {
            self.avPlayer.removeTimeObserver(periodicTimeObserver)
        }
        self.avPlayer.pause()
        progressTimer?.invalidate()
        playbackComlete?(nil)

    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        doInit()
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        playerToolBar.isInDoublePlayer = self.isInDoublePlayer
        self.circularSlider.isHidden = self.isInDoublePlayer

        self.circularSlider.gForce = self.gForce

        if let avPlayerLayer = avPlayerLayer {
            avPlayerLayer.bounds = self.bounds
            avPlayerLayer.position = CGPoint(x: self.bounds.width/2, y: self.bounds.height/2)

            playerToolBar.avPlayer = avPlayer
            playerToolBar.setupMovieScrollBar()
            if autoPlay {
                autoPlay = false
                play()
            }
            movieDidPlay?()
        }
        progressLabel.frame = CGRect(x: frame.size.width/2-100, y: frame.size.height/2-15, width: 200, height: 30)
        addSubview(canvas)
        addSubview(playerToolBar)
        addSubview(closeButton)
        canvas.frame = bounds
    }

    func onExportTimer(_ sender: AnyObject)  {
        guard let exporter = exporter else {
            return
        }
        progressLabel.text = "Processing " + String(Int(exporter.progress*100) ) + "%"
    }

    func mergeFiles(_ items: [String], assetWithOnset: String?,  mergeComplete: @escaping (_ fileName: String?)->Void) -> Void {
        if (assetWithOnset == nil) {
            mergeComplete(items.first!)
            return
        }
        let composition = AVMutableComposition()
        let track:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
        var insertTime = kCMTimeZero

        for item in items {
            let sourceAsset = AVAsset(url: URL(fileURLWithPath: FileUtility.getPathForFileMovieDirectory(item)))
            let tracks = sourceAsset.tracks(withMediaType: AVMediaTypeVideo)
            print("\(item) \(sourceAsset.isPlayable)") // print true
            print(sourceAsset.isExportable) // print true
            print(sourceAsset.isReadable) // print true
            if tracks.count > 0 {
                let assetTrack:AVAssetTrack = tracks[0] as AVAssetTrack
                do {
                    try track.insertTimeRange(CMTimeRangeMake(kCMTimeZero,sourceAsset.duration), of: assetTrack, at: insertTime)
                    insertTime = CMTimeAdd(insertTime, sourceAsset.duration)
                } catch {
                    mergeComplete(nil)
                    return
                }
            }
        }

        let fusedFileName = "fused_" + assetWithOnset!
        let fusedFilePath = FileUtility.getPathForFileMovieDirectory(fusedFileName)
        let fusedFileUrl = URL(fileURLWithPath: fusedFilePath)

        do {
            //in case the file merging fails, the residual file will cause
            //the file export fail everytime as the file exist 
            try FileManager.default.removeItem(atPath: fusedFilePath)
        } catch {

        }

        exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPreset1280x720)
        guard let exporter = exporter  else {
            return
        }
        exporter.outputURL = fusedFileUrl
        exporter.outputFileType = AVFileTypeQuickTimeMovie
        progressTimer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(onExportTimer(_:)), userInfo: nil, repeats: true);

        exporter.exportAsynchronously(completionHandler: {
            switch exporter.status{
            case  AVAssetExportSessionStatus.failed:
                if exporter.error != nil {
                    print("AVAssetExportSession failed \(exporter.error!)")
                }else{
                   print("AVAssetExportSession failed for unknown reason")
                }
                mergeComplete(nil)
            case AVAssetExportSessionStatus.cancelled:
                if exporter.error != nil {
                    print("AVAssetExportSession canceled \(exporter.error!)")
                }else{
                    print("AVAssetExportSession canceled for unknown reason")
                }
                mergeComplete(nil)
            default:

                do {
                    let realm = try Realm()
                    let movieClip = realm.object(ofType: MovieModel.self, forPrimaryKey: assetWithOnset)
                    try realm.write {
                        movieClip?.fusedFile = fusedFileName
                    }
                    //The files are released based on the usage count
                    MovieRepository.sharedInstance.release(file: movieClip?.fileName)
                    MovieRepository.sharedInstance.release(file: movieClip?.nextFile)
                    MovieRepository.sharedInstance.release(file: movieClip?.prevFile)
                } catch {

                }
                mergeComplete(fusedFileName)
                self.progressLabel.text = ""
                self.progressLabel.isHidden = true
                NotificationUtility.notifyReloadGallery()
            }
        })

    }

    func setMovies(_ items: [String], itemWithOnset asset: String?, playbackCompletion completion: @escaping ((_ error: PlayerError?) -> Void)){
        playbackComlete = completion
        closeButton.isHidden = false
        mergeFiles(items, assetWithOnset: asset ) { [weak self] (fileName) in
            DispatchQueue.main.async(execute: { () -> Void in

                if let fileName = fileName, let strongSelf = self {
                    let asset = AVAsset(url: URL(fileURLWithPath: FileUtility.getPathForFileMovieDirectory(fileName)))
                    let avplayerItem = AVPlayerItem(asset: asset)
                    strongSelf.progressTimer?.invalidate()
                    strongSelf.progressLabel.removeFromSuperview()
                    strongSelf.duration = asset.duration
                    strongSelf.avPlayer = AVPlayer(playerItem: avplayerItem)
                    if let playerLayer = strongSelf.avPlayerLayer {
                        playerLayer.removeFromSuperlayer()
                    }
                    strongSelf.avPlayerLayer = AVPlayerLayer(player: strongSelf.avPlayer)
                    strongSelf.avPlayerLayer?.zPosition = -1 //send to back
                    strongSelf.self.layer.addSublayer(strongSelf.avPlayerLayer!)


                    NotificationCenter.default.addObserver(strongSelf, selector: #selector(Player.currentFileDidFinish(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: avplayerItem)
                    print("Duration \(Float(CMTimeGetSeconds(strongSelf.duration)))")
                    print("Size \(strongSelf.movieDimension)")
                    strongSelf.ready = true
                    strongSelf.autoPlay = true;
                    strongSelf.setNeedsLayout()

                }
            })

        }

    }

    func setMovie(movieAsset: MovieModel, completion: @escaping ()->Void) {

        movieDidPlay = completion
        autoPlay = true
        playerToolBar.playeBackTimer?.invalidate()
        playerToolBar.playeBackTimer = nil
        var clipNames: [String]
        var assetWithOnset: String? = nil
        if let fusedFile = movieAsset.fusedFile {
            clipNames = [fusedFile]
        } else {
            assetWithOnset = movieAsset.fileName
            if let nextFile = movieAsset.nextFile {
                clipNames = [movieAsset.prevFile!, movieAsset.fileName!, nextFile]
            } else {
                clipNames = [movieAsset.prevFile!, movieAsset.fileName!]
            }
        }
        self.setMovies(clipNames, itemWithOnset: assetWithOnset, playbackCompletion: { (err) in

        })
        closeButton.isHidden = true
    }
    func resolutionSizeForVideo(_ asset:AVAsset) -> CGSize? {
        guard let track = asset.tracks(withMediaType: AVMediaTypeVideo).first else { return nil }
        let size = track.naturalSize.applying(track.preferredTransform)
        return CGSize(width: fabs(size.width), height: fabs(size.height))
    }
    //MARK: The playback methods
    func pause(){
        if ready {
            playerToolBar.pause()
        }
    }
    func play() {
        if ready {
            playerToolBar.play()
        }
    }

    func currentFileDidFinish(_ notification: Notification) {
    /*    if let periodicTimeObserver = playerToolBar.periodicTimeObserver {
            self.avPlayer.removeTimeObserver(periodicTimeObserver)
        }
        progressTimer?.invalidate()
        playbackComlete?(error: nil)*/

        avPlayer.seek(to: CMTimeMake(0, 30))
        avPlayer.rate = 1.0
    }

    func stop() {
        avPlayer?.pause()
        avPlayer = nil

        avPlayerLayer?.removeFromSuperlayer()
        avPlayerLayer = nil
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    //MARK:
}

class CollectionViewThumbNailCell: UICollectionViewCell {

    lazy var barView: UIView = {
        let lbl = UIView()
        lbl.contentMode = .scaleToFill
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.backgroundColor = UIColor.white
        lbl.layer.cornerRadius = 2
        lbl.clipsToBounds = true
        return lbl
    }()
    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(barView)
    }

    func configureMark(_ big: Bool) {
        if big {
            barView.frame = CGRect(x: bounds.size.width/2 - 2, y: 2, width: 4, height: bounds.size.height)
        } else {
            barView.frame = CGRect(x: bounds.size.width/2 - 2, y: bounds.size.height/2+2, width: 4, height: bounds.size.height/2)
        }
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

每次访问
btnNext
时,都会实例化一个新按钮。因此,在将一个实例添加为子视图后,可以使用其他实例来创建约束。由于这些其他子视图不是
self
的子视图,因此应用程序崩溃

将其设置为惰性变量,以便仅实例化一次:

lazy var btnNext: IteratorChevronButton = {
    let btn = IteratorChevronButton()
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}()
您还必须向按钮本身添加
宽度
高度
约束,而不是
self

btnNext.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
btnNext.addConstraint(NSLayoutConstraint(item: btnNext, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128))
更新:

正如Ahmad F所述,
lazy
关键字在本例中是完全可选的。如果您一定要使用按钮,您可以在不使用按钮的情况下简单地实例化按钮:

var btnNext: IteratorChevronButton = {
    let btn = IteratorChevronButton()
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}()

我猜错误是由以下原因造成的(您的btnNext未正确生成):

使用此语法可生成btnNext

var btnNext: IteratorChevronButton = {
    let btn = IteratorChevronButton()
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}()

是否有日志或只是崩溃?@AhmadF只是崩溃,没有日志…我认为您应该只使用约束或只设置帧,在您的超类中使用帧,在子类中使用约束,而且,您只设置尾部和y约束,所以它的宽度和高度没有被正确计算。你能再显示一些你自定义的
UIView
类的代码吗?@3stud1ant3好的,我已经删除了任何框架设置,只使用了约束,仍然是相同的错误,请在我的问题中查看我更新的代码谢谢,我更新了我的问题,由于某些原因,我无法使自定义按钮懒惰,请查看我的问题,了解自定义按钮被禁用时的错误lazy@BrianOgden别忘了=和尾随()我想如果他非常确定他会使用
btnNext
,就没有理由将其声明为
lazy
@AhmadF True。他可以用也可以不用。他只需要确保他不会创建多个实例。。。