不知我做错了什么,这是我的自定义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))
}

我得到以下错误:
ios - 无法为自定义UIButton添加NSLayoutContraints-LMLPHP
我试图使btnNext变懒,但得到以下错误:
ios - 无法为自定义UIButton添加NSLayoutContraints-LMLPHP
以下是自定义UIView类的代码:
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没有正确生成):

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

使用此语法生成btnNext
var btnNext: IteratorChevronButton = {
    let btn = IteratorChevronButton()
    btn.translatesAutoresizingMaskIntoConstraints = false
    return btn
}()

10-08 05:33