Related
I'm relatively new to Swift. I have a main view controller, ViewControllerMain, and a popover view controller, PopUpVC, which only has a label. I have a function, infoClicked that displays the popover with another function (showPopover) when the info button is clicked. When I click the button, I want to change the label text of the popover. However, with the current code, the label always displays "Default".
Here is my code:
class ViewControllerMain: UIViewController, UIPopoverPresentationControllerDelegate, GetTimesUsed {
let tipController: PopUpVC = PopUpVC().self
#IBAction func infoClicked(_ sender: UIButton) {
tipController.tipText = "Success"
showPopover()
}
func showPopover() {
let myViewController = storyboard?.instantiateViewController(withIdentifier: "popupController")
myViewController?.preferredContentSize = CGSize(width: 350, height: 200)
myViewController?.modalPresentationStyle = .popover
let popOver = myViewController?.popoverPresentationController
popOver?.delegate = self
UIView.animate(withDuration: 1, animations: {
self.GifView.alpha = 0.7
})
DispatchQueue.main.asyncAfter(deadline: .now() + 0.45) {
UIView.animate(withDuration: 0.5, animations: {
self.present(myViewController!, animated: true, completion: nil)
})
}
popOver?.permittedArrowDirections = .down
popOver?.sourceView = self.view
var passthroughViews: [AnyObject]?
passthroughViews = [infoButton]
myViewController?.popoverPresentationController?.passthroughViews = (NSMutableArray(array: passthroughViews!) as! [UIView])
popOver?.sourceRect = infoButton.frame
}
}
class PopUpVC: UIViewController {
#IBOutlet weak var tip: UILabel!
var tipText: String = "Default"
override func viewDidLoad() {
super.viewDidLoad()
tip.text = tipText
// Do any additional setup after loading the view.
}
}
Thank you for your help.
As mentioned in comments, you seem to be instantiating the popup controller 2 times, so try it like this in your showPopOver code:
let myViewController = storyboard?.instantiateViewController(withIdentifier: "popupController") as! PopUpVC
myViewController.preferredContentSize = CGSize(width: 350, height: 200)
myViewController.modalPresentationStyle = .popover
myViewController.tipText = '' //set to what ever
I have an app where I created a custom tabbar item in a UITabbarController, that someone can press to take a picture, and it looks like it does below.
That is exactly what I want, the problem is that when I test it on an iPhone X, the tabbar item for the camera looks lower then I would like, for example:
I have tried a couple of things to fix this, such as fixing the height of the tabbar in the viewDidLayoutSubviews(), but it messed with the tabbar on the iPhone 8. I also made sure that the "Use Safe Area Layout Guides" is selected, but it still doesn't work.
I also tried to change the frame of the tabbar item, but that doesn't work either.
This is the code that I used for the custom tabbar Controller:
import UIKit
class OtherTabController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupBtn()
// Do any additional setup after loading the view.
}
func setupBtn() {
let centerBtn = UIButton(frame: CGRect(x: 0, y: 10, width: 45, height: 45))
var centerBtnFrame = centerBtn.frame
centerBtnFrame.origin.y = (view.bounds.height - centerBtnFrame.height) - 2
centerBtnFrame.origin.x = view.bounds.width/2 - centerBtnFrame.size.width/2
centerBtn.frame = centerBtnFrame
centerBtn.layer.cornerRadius = 35
view.addSubview(centerBtn)
let centerImg = UIImage(named: "Other")
centerBtn.setBackgroundImage(centerImg, for: .normal)
centerBtn.addTarget(self, action: #selector(centerBtnAction(sender:)), for: .touchUpInside)
view.layoutIfNeeded()
}
#objc private func centerBtnAction(sender: UIButton) {
print("Camera")
cameraAction()
}
func cameraAction() {
let alertController = UIAlertController.init(title: nil, message: nil, preferredStyle: .actionSheet)
let takePhotoAction = UIAlertAction(title: "Take a Photo", style: .default, handler: nil)
alertController.addAction(takePhotoAction)
let selectFromAlbumAction = UIAlertAction(title: "Select from Album", style: .default, handler: nil)
alertController.addAction(selectFromAlbumAction)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
//OtherTabController?.present(alertController, animated: true, completion: nil)
}
}
If there is anything else I could help with, please ask. Thank you
EDIT:
I tried to make the y view the same as the super view but all it did was move the button to the top of the screen.
var centerBtnFrame = centerBtn.frame
centerBtnFrame.origin.y = view.bounds.minY //Make centerBtn' top equal to that of view's
centerBtnFrame.origin.x = view.bounds.width/2 - centerBtnFrame.size.width/2
centerBtn.frame = centerBtnFrame
If you need any more info, please ask. Thank you
Edit:
With the help of #Revanth Kausikan, I decided to create a custom tabbar with a view and a few buttons. It works very well in my opinion. It looks a little rough around the edges, but this is just a test for now.
Here is the code for the view:
import UIKit
class ItemScene: UIViewController {
#IBOutlet var customTab: UIView!
#IBOutlet weak var cameraBtn: UIButton!
#IBOutlet weak var twoBtn: UIButton!
#IBOutlet weak var oneBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func cameraBtnPressed(_ sender: Any) {
print("Camera")
}
#IBAction func twoBtnPressed(_ sender: Any) {
self.performSegue(withIdentifier: "segue", sender: nil)
print("Two")
}
#IBAction func oneBtnPressed(_ sender: Any) {
print("One")
}
}
this is the code for the second ViewController:
import UIKit
class TestingViewController: UIViewController {
#IBOutlet weak var cameraBtn: UIButton!
#IBOutlet weak var oneBtn: UIButton!
#IBOutlet weak var twoBtn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func cameraBtnPressed(_ sender: Any) {
print("Camera")
}
#IBAction func twoBtnPressed(_ sender: Any) {
print("Two")
}
#IBAction func oneBtnPressed(_ sender: Any) {
performSegueToReturnBack()
print("One")
}
}
extension UIViewController {
func performSegueToReturnBack() {
if let nav = self.navigationController {
nav.popViewController(animated: true)
} else {
self.dismiss(animated: true, completion: nil)
}
}
}
If anyone has anything else to add it would be greatly appreciated. Thank you
The Y point of button frame must be the same as that of it's super view (i.e. tab bar here).
If you've not created an outlet for the tab bar, first do so.
Ctrl (or right) click and drag from your tab bar in the storyboard to its associated view controller, to create an outlet. (assume that I'm naming it as tabBar)
now try this:
var centerBtnFrame = centerBtn.frame
let tabBarFrame = self.convert(tabBar.frame, to: view)
centerBtnFrame.origin.y = tabBarFrame.minY //Make centerBtn' top equal to that of tabBar's
centerBtnFrame.origin.x = view.bounds.width/2 - centerBtnFrame.size.width/2
centerBtn.frame = centerBtnFrame
Alternatively, you can try this:
var centerBtnFrame = centerBtn.frame
centerBtnFrame.origin.y = view.height - tabBar.height //This is the same logic as you've used for fixing x value of centerBtnFrame.
centerBtnFrame.origin.x = view.bounds.width/2 - centerBtnFrame.size.width/2
centerBtn.frame = centerBtnFrame
Yet another approach is to go back to the constraints, and make sure the elements and the base view are within the safe area.
If my guess is right (since you've not provided details about the constraints in the question), you've set the base view (the view that is in the bottom/base, holding all other elements on it) to fit itself with the entire view are of the view controller. Consider changing it to fit itself within the safe area.
Let me know the results.
EDIT
My opinion on your new attempt: It's really good, and I see that you are now able to customize it a lot more.
To improve it, you can try these:
Here, there are three views that are clickable in nature. we can use this to increase the interactive area as per our wish, to provide a natural experience for the user.
Here are it's constraints:
Other than these suggestions, I don't think I need to provide any. Your method is self sufficient for your implementation idea.
:)
Try this code working 100% ...
Declare the button
var menuButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
setupMiddleButton()
}
//create the method
func setupMiddleButton(flag:String) {
menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: 60, height: 60))
var menuButtonFrame = menuButton.frame
menuButtonFrame.origin.y = tabBar.bounds.height - menuButtonFrame.height
menuButtonFrame.origin.x = tabBar.bounds.width/2 - menuButtonFrame.size.width/2
menuButton.frame = menuButtonFrame
menuButton.backgroundColor = UIColor.red
menuButton.layer.borderColor = UIColor.black.cgColor
menuButton.layer.borderWidth = 1
tabBar.addSubview(menuButton)
menuButton.layer.cornerRadius = menuButtonFrame.height/2
menuButton.setImage(UIImage(named: "Homenormal"), for: .normal)
menuButton.addTarget(self, action: #selector(menuButtonAction(sender:)), for: .touchUpInside)
}
//button methods inside add background colour
#objc private func menuButtonAction(sender: UIButton) {
menuButton.backgroundColor = UIColor.red
selectedIndex = 2
print("menuButtonActio=\(sender.tag)")
}
//next Tabbar didselect
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let Selected_index = tabBar.items?.lastIndex(of: item)
print("selectedIndex123=\(Selected_index)")
if Selected_index == 0 {
menuButton.backgroundColor = UIColor.darkGray
menuButton.layer.borderColor = UIColor.black.cgColor
}
else if Selected_index == 1 {
menuButton.backgroundColor = UIColor.darkGray
menuButton.layer.borderColor = UIColor.black.cgColor
}
else if Selected_index == 2 {
menuButton.backgroundColor = UIColor.red
menuButton.layer.borderColor = UIColor.black.cgColor
}
else if Selected_index == 3 {
menuButton.backgroundColor = UIColor.darkGray
menuButton.layer.borderColor = UIColor.black.cgColor
}
else if Selected_index == 4 {
menuButton.backgroundColor = UIColor.darkGray
menuButton.layer.borderColor = UIColor.black.cgColor
}
}
I have a video in the background of every page of my scrollView with paging enabled. I want the video to play/pause when the area where one can see the video is tapped (see picture on the left below, between UIView containing label with name and image and bottom tab bar)(there is another subview over the video subview in each page: top info). The code used to achieve that is the following for loop (for loop because every page shall have these properties):
//add subviews (pages)
for (pageIndex, page) in pagesViews.enumerated(){
//...create pages with views
//add Subview with videoplayer frame to individual "pages"
let videoPlayerFrame = CGRect(x: 0, y: 0, width: pageSize.width, height: pageSize.height)
let videoPlayerView = PlayerView(frame: videoPlayerFrame)
page.addSubview(videoPlayerView)
//add Subview with top info frame to individual "pages"
let postInfoFrame = CGRect(x: 0, y: 0, width: pageSize.width, height: 80)
let infoView = PostView(frame: postInfoFrame)
page.addSubview(infoView)
//WORKS FINE UNTIL HERE
//NEW SUBVIEW
//add Subview with bottom info frame to individual "pages"
let postPropertiesFrame = CGRect(x: 0, y: (pageSize.height - 200) / 2, width: pageSize.width, height: 200)
let propertiesView = PostPropsView(frame: postPropertiesFrame)
page.addSubview(propertiesView)
}
That's what it looks like if the new subview hasn't been added:
PlayerView class serving as basis of first subview, the video plays/pauses when I tap the video area (read comments to get overview first (e.g. spinner shouldn't be important)):
lass PlayerView: UIView {
//create spinner view
let activityIndicatorView: UIActivityIndicatorView = {
//define properties
let aiv = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
aiv.translatesAutoresizingMaskIntoConstraints = false
//start spinner
aiv.startAnimating()
return aiv
}()
//create controls container view containing the spinner
let controlsContainerView: UIView = {
//set properties of controls container view
let controlView = UIView()
controlView.backgroundColor = UIColor(white: 0, alpha: 1)
return controlView
}()
override init(frame: CGRect){
super.init(frame: frame)
//function below this override init
setupPlayerView()
//add subview with controls (e.g. spinner)
controlsContainerView.frame = frame
addSubview(controlsContainerView)
//add to subview and center spinner in subview
controlsContainerView.addSubview(activityIndicatorView)
activityIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
activityIndicatorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
//enable interaction
controlsContainerView.isUserInteractionEnabled = true
//function below this override init
defInteractions()
//backgorund color of player
backgroundColor = .black
}
//define interactions (doupletap and singletap)
func defInteractions (){
//singletap
let singleTap = UITapGestureRecognizer(target: self, action: #selector(singleTapDetected(_:)))
singleTap.numberOfTapsRequired = 1
//controlsContainerView
controlsContainerView.addGestureRecognizer(singleTap)
}
//define type
var player: AVPlayer?
//set playing to false
var isPlaying: Bool = false
//PLAY?PAUSE VIDEO WHEN video tapped
func singleTapDetected(_ sender: UITapGestureRecognizer) {
//play or pause
if(!isPlaying){
//play
player?.play()
isPlaying = true
}
else{
//pause
player?.pause()
isPlaying = false
}
}
//setup video in background
func setupPlayerView() {
//insert url
let urlString = "https://blurtime.com/images/testvideo.mov"
//check URL if can be converted to NSURL
if let videoURL = NSURL(string: urlString){
//player's video
if self.player == nil {
player = AVPlayer(url: videoURL as URL)
}
//add sub-layer
let playerLayer = AVPlayerLayer(player: player)
self.layer.addSublayer(playerLayer)
playerLayer.frame = self.frame
//when are frames actually rendered (when is video loaded)
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
//loop through video
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil, using: { (_) in
DispatchQueue.main.async {
self.player?.seek(to: kCMTimeZero)
self.player?.play()
}
})
}
}
//what to do when video is loaded (spinner shall disappear)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
//when frames are actually being rendered
if keyPath == "currentItem.loadedTimeRanges" {
activityIndicatorView.stopAnimating()
controlsContainerView.backgroundColor = .clear
}
}
//reuired as suggested by XCode
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
However, if I add the subview with the PostPropsView class it doesn't completely work anymore. If the yellow areas are tapped it still functions as desired but not in the red area anymore... (picture on the right) Why does it work in the lower yellow area and why doesn't it anymore if the red area is tapped? Can anyone provide a solution (and explanation) to this problem?
The new view is on top of the view that has the tap gesture recognizer, and is intercepting those touches, and doesn't have a handler for them.
Perhaps create a new UIView, which contains both the PostPropsView and your video player, and put the tap gesture recognizer on that instead of the video? I don't believe you can add the UIView directly to the video player.
As Jake T. said, the tap gesture recognizer is absorbing the tap, so what you have to do is to add this on your TapGesture declaration
let singleTap = UITapGestureRecognizer(target: self, action: #selector(singleTapDetected(_:)))
singleTap.numberOfTapsRequired = 1
singleTap.cancelsTouchesInView = false
With that, you'll get what you want
So my app contains a uitableview/uitableviewcell and inside the uitableview/uitableviewcell is an AVplayer. I want to be able to pause the video player when the button is tapped. When I made the AV player a global variable, I was able to pause and play the video, but the video wasn't being replaced by the next video when I scrolled.
// video player
let videoURL = URL(string: uVideoUrl)!
let player = AVPlayer(url: videoURL)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = CGRect(x: cell.backgroundImg.frame.origin.x, y: cell.backgroundImg.frame.origin.y, width: cell.backgroundImg.frame.width - 5, height: cell.backgroundImg.frame.height)
cell.layer.addSublayer(playerLayer)
// play button
let playButton = UIButton(frame: CGRect(x: 15, y: 75, width: 30, height: 30))
playButton.setImage(UIImage(named: "playimg.png"), for: .normal)
** playButton.addTarget(self, action: #selector(playVideo(player:player)), for: .touchUpInside) **
cell.addSubview(playButton)
Here's the function to play the video.
func playVideo(player:AVPlayer) {
player.play()
}
The text/code with ** is the code I can't figure out how to let me pass in the player for me to play the video.
You can't pass parameters on selectors but there are a few ways to do this.
Here's one way by using Notification(swift 3), NSNotification(swift 2.3, etc)
class ViewController: UIViewController {
var player: AVPlayer!
override func viewDidLoad() {
super.viewDidLoad()
let videoURL = URL(string: uVideoUrl)!
player = AVPlayer(url: videoURL) // initialize player that is on the class level scope
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = CGRect(x: cell.backgroundImg.frame.origin.x, y: cell.backgroundImg.frame.origin.y, width: cell.backgroundImg.frame.width - 5, height: cell.backgroundImg.frame.height)
cell.layer.addSublayer(playerLayer)
let playButton = UIButton(frame: CGRect(x: 15, y: 75, width: 30, height: 30))
playButton.setImage(UIImage(named: "playimg.png"), for: .normal)
}
func playButtonClicked(sender: UIButton) {
let notifName = NSNotification.Name(rawValue: "SOMENOTIFICATIONNAMETHATISUNIQUE")
NotificationCenter.default.post(name: notifName, object: nil, userInfo: ["player": player])
}
}
class SomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let notifName = NSNotification.Name(rawValue: "SOMENOTIFICATIONNAMETHATISUNIQUE")
NotificationCenter.default.addObserver(self, selector: #selector(self.playClicked(notification:)), name: notifName, object: nil)
}
func playClicked(notification: Notification) {
if let player = notification.userInfo!["player"] as? AVPlayer {
// do stuff with player
}
}
}
What happens here is that your player is now passed like a parameter using the Notification's userInfo
You could add additional data to your UIButton using the objective C associated object system. The following code adds an additional variable player to UIButton. I've done this in the past with Int's. Haven't tried it with such a complex object as AVPlayer but don't see any reason why this wouldn't be possible.
private var playerAssociationKey: UInt8 = 0
extension UIButton {
var player: Int! {
get {
return objc_getAssociatedObject(self, &playerAssociationKey) as? Int
}
set(newValue) {
objc_setAssociatedObject(self, &playerAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
}
See How to have stored properties in Swift, the same way I had on Objective-C? for more details.
The button always sends itself as the argument of its action message. You can't make it send the player as the argument.
Based on the code in your question, it looks like each cell has an AVPlayer and a button. So why not just have the button send a message to the player directly? Use an extension on AVPlayer to define a play/pause toggle method:
extension AVPlayer {
func togglePlayPause(_ sender: AnyObject?) {
// Normal playback rate is 1. Paused rate is 0.
self.rate = 1 - self.rate
}
}
Then tell your button to send that message to the player like this:
playButton.addTarget(player, action: #selector(AVPlayer.togglePlayPause(_:)), for: .touchUpInside)
I have to draw a label or button on top of video relay next previous , leave comment . List of video have it, once user select one item from the table,it need to play, Once player play finished, those buttons or label should come on top of video
Here is my code :
comPlayerControl = AVPlayerViewController()
if let player = comPlayerControl {
let videoURL: String = "http://cdnapi.kaltura.com/p/11/sp/11/playManifest/entryId/"+selectedSubmission.transcodeRefId+"/format/applehttp/protocol/http/a.m3u8"
let playerItem = AVPlayerItem(URL: NSURL(string: videoURL)! )
commmentPlayer = AVPlayer(playerItem: playerItem)
player.player = commmentPlayer
player.view.frame = videoCell.frame
player.view.sizeToFit()
player.showsPlaybackControls = true
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(CommentsTableViewController.playerDidFinishPlaying(_:)),
name: AVPlayerItemDidPlayToEndTimeNotification,
object: playerItem
)
comPlayerControl.delegate = self
videoCell.addSubview(player.view)
}
func playerDidFinishPlaying(note: NSNotification) {
print("Video Finished")
let DynamicView=UIView(frame: CGRectMake(100, 200, 100, 100))
DynamicView.backgroundColor=UIColor.greenColor()
DynamicView.layer.cornerRadius=25
DynamicView.layer.borderWidth=2
DynamicView.layer.zPosition = 1;
comPlayerControl.view.addSubview(DynamicView)
}
requirement like this
You're using an AVPlayerViewController, so there's no reason to access your application's window like in Alessandro Ornano's answer. Why reinvent the wheel? Every AVPlayerViewController has a contentOverlayView property which allows you to place views between the player and the controls.
First, create a new AVPlayerItem and listen for the AVPlayerItemDidPlayToEndTimeNotification notification on that item. Load the item into your player and begin playback.
Once the item completes, the selector your specified to listen for the AVPlayerItemDidPlayToEndTimeNotification notification will be called. In that selector, access the contentOverlayView directly and add your buttons:
In some view controller or other object:
let playerVC = AVPlayerViewController()
// ...
func setupPlayer {
let playerItem = AVPlayerItem(...)
playerVC.player?.replaceCurrentItemWithPlayerItem(playerItem)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(VC.itemFinished), name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)
self.presentViewController(playerVC, animated: true) {
self.playerVC.player?.play()
}
}
func itemFinished() {
let btn = UIButton(type: .System)
btn.addTarget(self, action: #selector(VC.buttonTapped), forControlEvents: .TouchUpInside)
self.playerVC.contentOverlayView?.addSubview(btn)
}
func buttonTapped() {
print("button was tapped")
// replay/comment logic here
}
As stated in the comments (and a rejected edit), buttons may not work in the contentOverlayView. For an alternate solution, see Pyro's answer.
You could also subclass AVPlayerViewController and do everything inside an instance of your subclass, but Apple warns against that:
Do not subclass AVPlayerViewController. Overriding this class’s methods is unsupported and results in undefined behavior.
I think the best way to make the avplayer buttons is explained here: IOS 8 Video Playback using AVPlayer and AVPlayerViewController .
So , I prefeer and agree with these instructions, but if you still want to
add these buttons you can try to add them to the self.window
if let app = UIApplication.sharedApplication().delegate as? AppDelegate, let window = app.window {
let myFirstButton = UIButton()
myFirstButton.setTitle("test", forState: .Normal)
window.addSubview(myFirstButton)
...
}
I recommend looking up AVPlayerLayer as a way to show buttons and other content on top of your video.
See the Advances in AVFoundation Playback WWDC presentation.
Also, check out the AVFoundationSimplePlayer-iOS example project.
Essentially, you create a view to host your player, and you make the layer behind the view into an AVPlayerLayer.
class PlayerView: UIView {
var player: AVPlayer? {
get {
return playerLayer.player
}
set {
playerLayer.player = newValue
}
}
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
override class var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
Based on the your question and from the comment/code of Ramis i have made a sample code which you may try
As mentioned by JAL the contentOverlayView should be the best option to display the control over the video in the AVPlayerController, but as per my sample demo the contentOverlayView don't have any user interaction for the buttons or other controls, as if you check in the 3D view of the AVPlayerController it has AVTouchIgnoringView/UIView in front of the contentOverlayView which may be problem in user interaction with contentOverlayView.
So another solution is to add the overlay view in the AVPlayerViewController
func addContentOverlayView() {
OverlayView.frame = CGRectMake(0,30,AVPlayerVC.view.bounds.width, 100)
OverlayView.hidden = true
OverlayView.backgroundColor = UIColor ( red: 0.5, green: 0.5, blue: 0.5, alpha: 0.379 )
let btnNext = UIButton(frame:CGRectMake(AVPlayerVC.view.bounds.width - 60,0,60,44))
btnNext.setTitle(">>", forState:.Normal)
btnNext.addTarget(self, action:"playNext", forControlEvents:.TouchUpInside)
// btnNext.layer.borderColor = UIColor ( red: 0.0, green: 0.0, blue: 1.0, alpha: 0.670476140202703 ).CGColor
// btnNext.layer.borderWidth = 1.0
OverlayView.addSubview(btnNext)
let btnReplay = UIButton(frame:CGRectMake((AVPlayerVC.view.bounds.width/2)-40,0,80,44))
btnReplay.setTitle("Replay", forState:.Normal)
btnReplay.addTarget(self, action:"replayVideo", forControlEvents:.TouchUpInside)
OverlayView.addSubview(btnReplay)
let btnPrevious = UIButton(frame:CGRectMake(0,0,80,44))
btnPrevious.setTitle("<<", forState:.Normal)
btnPrevious.addTarget(self, action:"previousVideo", forControlEvents:.TouchUpInside)
OverlayView.addSubview(btnPrevious)
let btnComment = UIButton(frame:CGRectMake((AVPlayerVC.view.bounds.width/2)-70,40,140,44))
btnComment.setTitle("Comments", forState:.Normal)
btnComment.addTarget(self, action:"openComments", forControlEvents:.TouchUpInside)
OverlayView.addSubview(btnComment)
AVPlayerVC.view.addSubview(OverlayView);
}
func playNext() {
prevItem = AVPlayerVC.player?.currentItem
OverlayView.hidden = true
commmentQueuePlayer.advanceToNextItem()
}
func replayVideo() {
OverlayView.hidden = true
AVPlayerVC.player?.currentItem?.seekToTime(kCMTimeZero)
AVPlayerVC.player?.play()
}
func previousVideo() {
OverlayView.hidden = true
if prevItem != AVPlayerVC.player?.currentItem {
if (commmentQueuePlayer.canInsertItem(prevItem!, afterItem:AVPlayerVC.player?.currentItem)) {
//commmentQueuePlayer.insertItem(prevItem!, afterItem:AVPlayerVC.player?.currentItem)
commmentQueuePlayer.replaceCurrentItemWithPlayerItem(prevItem)
prevItem = AVPlayerVC.player?.currentItem
replayVideo()
}
} else {
replayVideo()
//Else display alert no prev video found
}
}
func stopedPlaying() {
if prevItem == nil {
prevItem = AVPlayerVC.player?.currentItem
}
OverlayView.hidden = false
}
At the initial setup we set the AVPlayerController,AVQueuePlayer etc... at that time we can add the overlay on the AVPlayerController
For the previous item there is no direct available and as per documentation the item will be remove once it's next item is played , so we have two option like replaceCurrentItemWithPlayerItem or insertItem(item: AVPlayerItem, afterItem: AVPlayerItem?)
If you need to check complete code you can check it from :
https://gist.github.com/Pyrolr/debb4fca8f608b1300e099a5b3547031
Note: This is just like prototype, it is not working perfectly in all the cases but it can help you in understanding the basic functionality you wnat and you can improve/optimise based on your requirements
Check Below code, I am able to add the button on overlay as well as button event is being called.
var playerViewController: AVPlayerViewController?
var OverlayView = UIView()
private lazy var button1: UIButton = {
let selfType = type(of: self)
let button = UIButton(frame: .init(origin: .zero, size: .init(width: selfType.musicWidth, height: selfType.musicHeight)))
button.setImage(UIImage(named: "star"), for: .normal)
button.addTarget(self, action: #selector(button1DidSelect), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: selfType.musicWidth),
button.heightAnchor.constraint(equalToConstant: selfType.musicHeight)
])
return button
}()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
guard let url = URL(string: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4") else { return }
let player = AVPlayer(url: url)
self.playerViewController = AVPlayerViewController()
guard let playerViewController = self.playerViewController else { return }
playerViewController.player = player
present(playerViewController, animated: true) {
playerViewController.player?.play()
}
self.addButtonsOnOverlayView()
self.playerViewController?.showsPlaybackControls = false
self.isHideOverlayControls = false
}
#objc private func button1DidSelect() {
print("button1 Selected")
}
private func addButtonsOnOverlayView() {
guard let overlayView = self.playerViewController?.contentOverlayView else { return }
if !self. button1.isDescendant(of: overlayView) {
overlayView.addSubview(self.musicFirstButton)
self.playerViewController?.showsPlaybackControls = true
}
NSLayoutConstraint.activate([
button1.leadingAnchor.constraint(equalTo: overlayView.leadingAnchor, constant: 20),
button1.topAnchor.constraint(equalTo: overlayView.topAnchor, constant: 20),
])
}