如何在 Swift 中使用 AVPlayerViewController (AVKit)播放视频

如何播放视频与 AV 工具包播放器视图控制器在迅捷?

override func viewDidLoad() {
super.viewDidLoad()
let videoURLWithPath = "http://****/5.m3u8"
let videoURL = NSURL(string: videoURLWithPath)
playerViewController = AVPlayerViewController()


dispatch_async(dispatch_get_main_queue()) {
self.playerViewController?.player = AVPlayer.playerWithURL(videoURL) as AVPlayer
}
}
258594 次浏览

试试这个

var player:AVPlayer!
var avPlayerLayer:AVPlayerLayer = AVPlayerLayer(player: player)
avPlayerLayer.frame = CGRectMake(your frame)
self.view.layer .addSublayer(avPlayerLayer)
var steamingURL:NSURL = NSURL(string:playerURL)
player = AVPlayer(URL: steamingURL)
player.play()

斯威夫特3.x-5. x

必要: 进口 AVKit进口 AVFoundation

即使使用 AVPlayer,也需要 AVFoundation 框架

如果你想使用 AVPlayerViewController:

let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(url: videoURL!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}

或者仅仅是 AVPlayer:

let videoURL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
let player = AVPlayer(url: videoURL!)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.view.bounds
self.view.layer.addSublayer(playerLayer)
player.play()

最好将这段代码放到方法 覆盖 func viewDidAppear (_ Animated: Bool)或者之后的某个地方。


目标 C

AVPlayerViewController :

NSURL *videoURL = [NSURL URLWithString:@"https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"];
AVPlayer *player = [AVPlayer playerWithURL:videoURL];
AVPlayerViewController *playerViewController = [AVPlayerViewController new];
playerViewController.player = player;
[self presentViewController:playerViewController animated:YES completion:^{
[playerViewController.player play];
}];

或者仅仅是 AVPlayer:

NSURL *videoURL = [NSURL URLWithString:@"https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"];
AVPlayer *player = [AVPlayer playerWithURL:videoURL];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
playerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:playerLayer];
[player play];

试试这个,绝对适用于 Swift 2.0

 let player = AVPlayer(URL: url)
let playerController = AVPlayerViewController()


playerController.player = player
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame


player.play()

使用 MPMoviePlayerController:

 import UIKit
import MediaPlayer


class ViewController: UIViewController {


var streamPlayer : MPMoviePlayerController =  MPMoviePlayerController(contentURL: NSURL(string:"video url here"))
override func viewDidLoad() {
super.viewDidLoad()
streamPlayer.view.frame = self.view.bounds
self.view.addSubview(streamPlayer.view)


streamPlayer.fullscreen = true
// Play the movie!
streamPlayer.play()
}
}

使用 AVPlayer:

import AVFoundation


var playerItem:AVPlayerItem?
var player:AVPlayer?


override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string: "url of the audio or video")
playerItem = AVPlayerItem(URL: url!)
player=AVPlayer(playerItem: playerItem!)
let playerLayer=AVPlayerLayer(player: player!)
playerLayer.frame=CGRectMake(0, 0, 300, 50)
self.view.layer.addSublayer(playerLayer)
}

我有一个播放按钮来处理按钮点击。

playButton.addTarget(self, action: "playButtonTapped:", forControlEvents: .TouchUpInside)


func playButtonTapped(sender: AnyObject) {
if player?.rate == 0
{
player!.play()
playButton.setImage(UIImage(named: "player_control_pause_50px.png"), forState: UIControlState.Normal)
} else {
player!.pause()
playButton.setImage(UIImage(named: "player_control_play_50px.png"), forState: UIControlState.Normal)
}
}

我添加了一个监听 AVPlayerItemDidPlayToEndTimeNotification 的观察器。

override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "finishedPlaying:", name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)
}


override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self)
}

视频/音频播放结束后,重置按钮图像和通知

  func finishedPlaying(myNotification:NSNotification) {
playButton.setImage(UIImage(named: "player_control_play_50px.png"), forState: UIControlState.Normal)


let stopedPlayerItem: AVPlayerItem = myNotification.object as! AVPlayerItem
stopedPlayerItem.seekToTime(kCMTimeZero)
}

目标 c

这只能在 Xcode 7中使用

转到 .h文件并导入 AVKit/AVKit.h和 然后转到 .m文件并添加以下代码:

NSURL *url=[[NSBundle mainBundle]URLForResource:@"arreg" withExtension:@"mp4"];
AVPlayer *video=[AVPlayer playerWithURL:url];
AVPlayerViewController *controller=[[AVPlayerViewController alloc]init];
controller.player=video;
[self.view addSubview:controller.view];
controller.view.frame=self.view.frame;
[self addChildViewController:controller];
[video play];

IOS10/Swift3/Xcode 8中的 bug (? !) ?

if let url = URL(string: "http://devstreaming.apple.com/videos/wwdc/2016/102w0bsn0ge83qfv7za/102/hls_vod_mvp.m3u8"){
let playerItem = AVPlayerItem(url: url)
let player = AVPlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame=CGRect(x: 10, y: 10, width: 300, height: 300)
self.view.layer.addSublayer(playerLayer)
}

不工作(空直...)

这个方法可行:

if let url = URL(string: "http://devstreaming.apple.com/videos/wwdc/2016/102w0bsn0ge83qfv7za/102/hls_vod_mvp.m3u8"){


let player = AVPlayer(url: url)
let controller=AVPlayerViewController()
controller.player=player
controller.view.frame = self.view.frame
self.view.addSubview(controller.view)
self.addChildViewController(controller)
player.play()
}

同样的网址。

斯威夫特3:

import UIKit
import AVKit
import AVFoundation


class ViewController: UIViewController {


@IBOutlet weak var viewPlay: UIView!
var player : AVPlayer?


override func viewDidLoad() {
super.viewDidLoad()


let url : URL = URL(string: "http://static.videokart.ir/clip/100/480.mp4")!
player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = self.viewPlay.bounds
self.viewPlay.layer.addSublayer(playerLayer)


}


@IBAction func play(_ sender: Any) {
player?.play()
}


@IBAction func stop(_ sender: Any) {
player?.pause()
}


}

Swift 3.0完整源代码:

import UIKit
import AVKit
import AVFoundation


class ViewController: UIViewController,AVPlayerViewControllerDelegate
{
var playerController = AVPlayerViewController()




@IBAction func Play(_ sender: Any)
{
let path = Bundle.main.path(forResource: "video", ofType: "mp4")


let url = NSURL(fileURLWithPath: path!)


let player = AVPlayer(url:url as URL)


playerController = AVPlayerViewController()




NotificationCenter.default.addObserver(self, selector: #selector(ViewController.didfinishplaying(note:)),name:NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)


playerController.player = player


playerController.allowsPictureInPicturePlayback = true


playerController.delegate = self


playerController.player?.play()


self.present(playerController,animated:true,completion:nil)
}


func didfinishplaying(note : NSNotification)
{
playerController.dismiss(animated: true,completion: nil)
let alertview = UIAlertController(title:"finished",message:"video finished",preferredStyle: .alert)
alertview.addAction(UIAlertAction(title:"Ok",style: .default, handler: nil))
self.present(alertview,animated:true,completion: nil)
}




func playerViewController(_ playerViewController: AVPlayerViewController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
let currentviewController =  navigationController?.visibleViewController


if currentviewController != playerViewController
{
currentviewController?.present(playerViewController,animated: true,completion:nil)
}




}
}

在《斯威夫特5》中,我就是这么做的

刚刚从 录像样本添加到该项目的样本视频

添加了用于播放来自网站和本地的视频的操作按钮,以下是快速代码示例

import UIKit
import AVKit


class ViewController: UIViewController {


override func viewDidLoad() {
super.viewDidLoad()
//TODO : Make Sure Add and copy "SampleVideo.mp4" file in project before play
}


@IBAction func playWebVideo(_ sender: Any) {


guard let url = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4") else {
return
}
// Create an AVPlayer, passing it the HTTP Live Streaming URL.
let player = AVPlayer(url: url)
let controller = AVPlayerViewController()
controller.player = player
present(controller, animated: true) {
player.play()
}
}


@IBAction func playLocalVideo(_ sender: Any) {


guard let path = Bundle.main.path(forResource: "SampleVideo", ofType: "mp4") else {
return
}
let videoURL = NSURL(fileURLWithPath: path)


// Create an AVPlayer, passing it the local video url path
let player = AVPlayer(url: videoURL as URL)
let controller = AVPlayerViewController()
controller.player = player
present(controller, animated: true) {
player.play()
}
}


}

Swift 5

  @IBAction func buttonPressed(_ sender: Any) {
let videoURL = course.introductionVideoURL
let player = AVPlayer(url: videoURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player


present(playerViewController, animated: true, completion: {


playerViewController.player!.play()
})

//这里的课程包括一个模型文件,在其中我给出了 URL,所以我使用课程函数从模型中调用函数。

//还要介绍 VideoUrl 是我在 model 中声明的一个 URL。

 var introductionVideoURL: URL

此外,您也可以使用下面的代码,而不是从模型中调用函数

替换这个代码

  let videoURL = course.introductionVideoURL

  guard let videoURL = URL(string: "https://something.mp4) else {
return
let videoUrl = //URL: Your Video URL


//Create player first using your URL
let yourplayer = AVPlayer(url: videoUrl)


//Create player controller and set it’s player
let playerController = AVPlayerViewController()
playerController.player = yourplayer




//Final step To present controller  with player in your view controller
present(playerController, animated: true, completion: {
playerController.player!.play()
})

Swift 5.0

从@ingconti 答案改进。这对我很有用。

 if let url = URL(string: "urUrlString"){
let player = AVPlayer(url: url)
let avController = AVPlayerViewController()
avController.player = player
// your desired frame
avController.view.frame = self.view.frame
self.view.addSubview(avController.view)
self.addChild(avController)
player.play()
}


首先,您必须在视图控制器中全局定义2个变量。

var player: AVPlayer!
var playerViewController: AVPlayerViewController!

在这里,我添加到一个理想的视图播放器。

@IBOutlet weak var playerView: UIView!

然后向 ViewDidLoad方法添加以下代码。

let videoURL = URL(string: "videoUrl")
self.player = AVPlayer(url: videoURL!)
self.playerViewController = AVPlayerViewController()
playerViewController.player = self.player
playerViewController.view.frame = self.playerView.frame
playerViewController.player?.pause()
self.playerView.addSubview(playerViewController.view)

如果你没有定义全局 玩家PlayerViewController,你就不能嵌入播放器。

使用 github 链接的 ASVideoPlayer 库定制 VideoPlayer: https://github.com/Asbat/ASVideoPlayer

// --------------------------------------------------------
// MARK:- variables
// --------------------------------------------------------


var videoPlayer = ASVideoPlayerController()
var videoData : [VideoModel] = []
var allVideoData : [AllVideoModel] = []
var cellHeights = [IndexPath: CGFloat]()
let loadingCellTableViewCellCellIdentifier = "LoadingCellTableViewCell"
var pauseIndexPath : Int = 0
var pageNumber = 1
var index = 0
var id = ""
var titleVideo = ""
var isUpdate = false
var myVideo : [MyVideo] = []
var imgs = [UIImage]()
var activityViewController : UIActivityViewController!


private var activityIndicator = NVActivityIndicatorView(frame: CGRect(x: 5, y: 5, width: 5, height: 5), type: .circleStrokeSpin, color: .systemBlue, padding: 5)
private let refreshControl = UIRefreshControl()




// --------------------------------------------------------
// MARK:- Outlets
// --------------------------------------------------------
@IBOutlet private var tableVideo: UITableView!
@IBOutlet private var _btnBack: UIButton!
@IBOutlet var _btnide: UIButton!


// ---------------------------------------------------------
// MARK:- Lifecycle
// ---------------------------------------------------------


override func viewDidLoad() {
super.viewDidLoad()
    

self._btnide.isHidden = true
tableVideo.rowHeight = UITableView.automaticDimension
tableVideo.separatorStyle = .none
tableVideo.delegate = self
tableVideo.dataSource = self
tableVideo.register(UINib(nibName: "VideoPlayerTableCell", bundle: nil), forCellReuseIdentifier: "VideoPlayerTableCell")
let cellNib = UINib(nibName:loadingCellTableViewCellCellIdentifier, bundle: nil)
tableVideo.register(cellNib, forCellReuseIdentifier: loadingCellTableViewCellCellIdentifier)
navigationController?.navigationBar.isHidden = true
}


override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
ASVideoPlayerController.sharedVideoPlayer.pausePlayeVideosFor(tableView: tableVideo)
tableVideo.scrollToRow(at: IndexPath(row: index, section: 0), at: .none, animated: true)
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
tableVideo.scrollToRow(at: IndexPath(row: pauseIndexPath, section: 0), at: .none, animated: true)
puasVideoWhenPushVC(index: pauseIndexPath)
NotificationCenter.default.removeObserver(self)
if tableVideo.isHidden == true {
}
}


@IBAction func _onTapBackBtnAction(_ sender: UIButton) {
tableVideo.scrollToRow(at: IndexPath(row: pauseIndexPath, section: 0), at: .none, animated: true)
self.puasVideoWhenPushVC(index: pauseIndexPath)
navigationController?.popViewController(animated: true)
navigationController?.navigationBar.isHidden = false
}


// ---------------------------------------------------------------------
// MARK:- TableView Delegate & DataSource
// ---------------------------------------------------------------------


extension VideoPlayerVC :
UITableViewDelegate,UITableViewDataSource,UIScrollViewDelegate {


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isUpdate{
return videoData.count
}else{
return allVideoData.count
}
}


func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if tableView == tableVideo {
return view.bounds.height
}else {
return UITableView.automaticDimension
}
}




func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if tableView == tableVideo {
if let videoCell = cell as? ASAutoPlayVideoLayerContainer, let _ = videoCell.videoURL {
ASVideoPlayerController.sharedVideoPlayer.removeLayerFor(cell: videoCell)
}
}
}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableVideo.dequeueReusableCell(withIdentifier: "VideoPlayerTableCell", for: indexPath) as! VideoPlayerTableCell
if isUpdate{
self.id = videoData[indexPath.row].id ?? ""
cell.configureCell(videoUrl: videoData[indexPath.row].videoLink )
}else{
self.id = allVideoData[indexPath.row].id ?? ""
cell.configureCell(videoUrl: allVideoData[indexPath.row].videoLink)
}
cell.btnPlayPause.isSelected = false
cell.btnPlayPause.tag = indexPath.row
cell.btnPlayPause.addTarget(self, action: #selector(didTapPlayPauseButton(_:)), for: .touchUpInside)
cell.btnPlayPause.setImage(UIImage(named: ""), for: .normal)
cell.btnPlayPause.setImage(UIImage(named: "btn_play_video"), for: .selected)
    

cell.btnUseNow.tag = indexPath.row
cell.btnUseNow.addTarget(self, action: #selector(btnUseNowTapped(sender:)), for: .touchUpInside)
    

cell.btnShare.tag = indexPath.row
cell.btnShare.addTarget(self, action: #selector(btnShareTapped(sender:)), for: .touchUpInside)
    

cell.btnSave.tag = indexPath.row
cell.btnSave.addTarget(self, action: #selector(btnSaveTapped(sender:)), for: .touchUpInside)
    

pauseIndexPath = indexPath.row
return cell
}


@objc func btnUseNowTapped(sender: UIButton){
    

self._btnide.isHidden = false
self.pausePlayeVideos()
let editVC = EditVideoVC()
var fileName : String = kEmptyString
    

if self.isUpdate{
editVC.videoString = self.videoData[sender.tag].videoLink ?? kEmptyString
editVC.id = self.videoData[sender.tag].id ?? kEmptyString
editVC.titleVideo = self.videoData[sender.tag].title ?? kEmptyString
fileName = self.videoData[sender.tag].videoZip ?? kEmptyString
        

guard !FileManager.isExist(id: self.videoData[sender.tag].id ?? kEmptyString) else{
print("File Downloaded")
self.puasVideoWhenPushVC(index: sender.tag)
self.navigationController?.pushViewController(editVC, animated: true)
return }
        

FileManager.download(id: self.videoData[sender.tag].id ?? kEmptyString, url: fileName) { (url) in
guard url != nil else {
print("not download")
return
}
self.puasVideoWhenPushVC(index: sender.tag)
self.navigationController?.pushViewController(editVC, animated: true)
}
}
else{
editVC.videoString = self.allVideoData[sender.tag].videoLink ?? kEmptyString
editVC.id = self.allVideoData[sender.tag].id ?? kEmptyString
editVC.titleVideo = self.allVideoData[sender.tag].title ?? kEmptyString
fileName = self.allVideoData[sender.tag].videoZip ?? kEmptyString
        

guard !FileManager.isExist(id: self.allVideoData[sender.tag].id ?? kEmptyString) else{
print("File Downloaded")
self.puasVideoWhenPushVC(index: sender.tag)
self.navigationController?.pushViewController(editVC, animated: true)
return }
        

FileManager.download(id: self.allVideoData[sender.tag].id ?? kEmptyString, url: fileName) { (url) in
guard url != nil else {
print("not download")
return
}
self.puasVideoWhenPushVC(index: sender.tag)
self.navigationController?.pushViewController(editVC, animated: true)
}
}
}


@objc func btnShareTapped(sender: UIButton){
    

if self.isUpdate{
let video = ["\(String(describing: self.videoData[sender.tag].videoLink))"]
self.activityViewController = UIActivityViewController(activityItems: video, applicationActivities: nil)
self.activityViewController.setValue("Video Share", forKey: "subject")
        

self.activityViewController.popoverPresentationController?.sourceView = self.view
self.activityViewController.excludedActivityTypes = [ UIActivity.ActivityType.airDrop, UIActivity.ActivityType.postToTwitter, UIActivity.ActivityType.addToReadingList, UIActivity.ActivityType.assignToContact,UIActivity.ActivityType.copyToPasteboard,UIActivity.ActivityType.mail,UIActivity.ActivityType.markupAsPDF,UIActivity.ActivityType.message,UIActivity.ActivityType.postToFacebook,UIActivity.ActivityType.postToFlickr,UIActivity.ActivityType.postToTencentWeibo,UIActivity.ActivityType.postToVimeo,UIActivity.ActivityType.postToWeibo,UIActivity.ActivityType.saveToCameraRoll]
self.present(self.activityViewController, animated: true, completion: nil)
}
else{
let categoryVideo = ["\(String(describing: self.allVideoData[sender.tag].videoLink))"]
self.activityViewController = UIActivityViewController(activityItems: categoryVideo, applicationActivities: nil)
self.activityViewController.setValue("Video Share", forKey: "subject")
        

self.activityViewController.popoverPresentationController?.sourceView = self.view
self.activityViewController.excludedActivityTypes = [ UIActivity.ActivityType.airDrop, UIActivity.ActivityType.postToTwitter]
self.present(self.activityViewController, animated: true, completion: nil)
}
}


@objc func btnSaveTapped(sender: UIButton){
   

if self.isUpdate{
self.downloadVideos(video: self.videoData[sender.tag].videoLink ?? kEmptyString)
}else{
self.downloadVideos(video: self.allVideoData[sender.tag].videoLink ?? kEmptyString)
}
}




private func downloadVideos(video : String){
Alamofire.request(video).downloadProgress(closure : { (progress) in
}).responseData{ (response) in
///# Create folder in documetn directory #///


if let data = response.result.value{
let path = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("Videos")
if !FileManager.default.fileExists(atPath: path) {
try! FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
}
let fileURL = URL(fileURLWithPath:path).appendingPathComponent("\(self.id)/\(self.titleVideo)/output.mp4")
print(fileURL)
do{
try data.write(to: fileURL, options: .atomic)
}catch{
print("could not download")
}
print(fileURL)
}
}
}




// ----------------------------------------------------------------------
// MARK:- Scrollview Method
// ----------------------------------------------------------------------


func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if scrollView == tableVideo {
pauseIndexPath = Int(scrollView.contentOffset.y / scrollView.frame.size.height)
pausePlayeVideos()
}
}


func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if scrollView == tableVideo {
if !decelerate {
pausePlayeVideos()
}
}
}


// ----------------------------------------------------------------------
// MARK:- Function Pause & Play Button
// ----------------------------------------------------------------------


func puasVideoWhenPushVC (index : NSInteger) {
if isUpdate{
guard let cell = tableVideo.cellForRow(at: IndexPath(row: index, section: 0)) as? VideoPlayerTableCell else { return }
ASVideoPlayerController.sharedVideoPlayer.pauseVideo(forLayer: cell.videoLayer, url: videoData[index].videoLink ?? "")
}
else{
guard let cell = tableVideo.cellForRow(at: IndexPath(row: index, section: 0)) as? VideoPlayerTableCell else { return }
ASVideoPlayerController.sharedVideoPlayer.pauseVideo(forLayer: cell.videoLayer, url: allVideoData[index].videoLink ?? "")
}
}


@objc func pausePlayeVideos(){
ASVideoPlayerController.sharedVideoPlayer.pausePlayeVideosFor(tableView: tableVideo)
}


@objc func appEnteredFromBackground() {
ASVideoPlayerController.sharedVideoPlayer.pausePlayeVideosFor(tableView: tableVideo, appEnteredFromBackground: true)
}


@objc func didTapPlayPauseButton(_ sender: UIButton) {
    

guard let cell = tableVideo.cellForRow(at: IndexPath(row: sender.tag, section: 0)) as? VideoPlayerTableCell else { return }
if sender.isSelected {
if isUpdate{
ASVideoPlayerController.sharedVideoPlayer.playVideo(withLayer: cell.videoLayer, url: videoData[sender.tag].videoLink ?? "")
}else{
ASVideoPlayerController.sharedVideoPlayer.playVideo(withLayer: cell.videoLayer, url: allVideoData[sender.tag].videoLink ?? "")
}
} else {
if isUpdate{
ASVideoPlayerController.sharedVideoPlayer.pauseVideo(forLayer: cell.videoLayer, url: videoData[sender.tag].videoLink ?? "")
}else{
ASVideoPlayerController.sharedVideoPlayer.pauseVideo(forLayer: cell.videoLayer, url: allVideoData[sender.tag].videoLink ?? "")
}
}
sender.isSelected = !sender.isSelected
}