animating button Allowuserinteraction not working - ios

I have a UIbutton created in code in an NSObject class which controls a game in a UIViewController class. The button works fine throughout most of the game, but at a certain point I want the button to fadein/out. Once the fadein/out starts animating the button is no longer interactive. I have set the .AllowUserInteraction option, but still no luck. I am stumped on this one, so any help much appreciated!
Class GameController: NSObject {
var gameView: UIView!
var nextButton: UIButton!
func gameViewSetUp() {
// create the nextButton
let imageNextButton = UIImage(named: "rightArrow") as UIImage?
self.nextButton = UIButton(type: .Custom)
nextButton.frame = CGRectMake(ScreenWidth-100,ScreenHeight-250,60,60)
nextButton.setTitle("", forState: UIControlState.Normal)
nextButton.setImage(imageNextButton, forState: .Normal)
nextButton.contentMode = .ScaleAspectFill
nextButton.backgroundColor = UIColor.clearColor()
nextButton.layer.cornerRadius = 5.0
nextButton.layer.borderWidth = 2.5
nextButton.layer.borderColor = UIColor.clearColor().CGColor
nextButton.addTarget(self, action: "nextPressed", forControlEvents: .TouchUpInside)
nextButton.userInteractionEnabled = true
gameView.addSubview(nextButton)
func playStageX() {
nextButtonWink()
}
}
func nextButtonWink() {
UIView.animateWithDuration(1.5, delay: 0, options: [.AllowUserInteraction, .Repeat, .CurveEaseInOut, .Autoreverse],
animations: {
// self.nextButton.userInteractionEnabled = true // I tried this as well but no impact.
self.nextButton.alpha = 0
}, completion: nil)
}
class GameViewController: UIViewController {
private var controller: GameController
required init?(coder aDecoder: NSCoder) {
controller = GameController()
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
let gameView = UIView(frame: CGRectMake(0, 0, ScreenWidth, ScreenHeight))
self.view.addSubview(gameView)
controller.gameView = gameView
}

Take out self.nextButton.alpha = 0 to test this out, I believe that button events will not fire when alpha is zero. When it comes to animations, the actual object's alpha will get set to zero before the animation starts, and what you are seeing is like a rendered interactive screenshot of your view as it animates
If that is the case, then you need to set the alpha to something like 0.0100000003 or override the button to be interactive at alpha 0

Swift 3 Xcode 8:
AllowUserAction is now deprecated to allowUserAction.... go figure
func nextButtonWink() {
UIView.animateWithDuration(1.5, delay: 0, options: [.allowUserInteraction,
animations: {

Related

YouTube player opening unnecessarily during scrolling of CollectionView

I am working on a chatbot where the different type of response comes from the server and I display the response using UICollectionView cells in chat screen. Different type of cells presents according to server response. when server response with playing video, I am presenting the cell that contains youtube player. I am using https://github.com/kieuquangloc147/YouTubePlayer-Swift. The issue is when I scroll chat screen (collectionView) youtube player is opening again and again. Sometimes it is blocking all the UI element and stop scrolling. I tried different methods but can't able to resolve it. Here is the code:
PlayerView:
import UIKit
class PlayerView: UIView, YouTubePlayerDelegate {
override init(frame: CGRect) {
super.init(frame: frame)
addYotubePlayer()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// youtube player
lazy var youtubePlayer: YouTubePlayerView = {
let viewFrame = UIScreen.main.bounds
let player = YouTubePlayerView(frame: CGRect(x: 0, y: 0, width: viewFrame.width - 16, height: viewFrame.height * 1/3))
player.delegate = self
return player
}()
// used as an overlay to dismiss the youtube player
let blackView = UIView()
// youtube player loader
lazy var playerIndicator: UIActivityIndicatorView = {
let indicator = UIActivityIndicatorView()
indicator.activityIndicatorViewStyle = .whiteLarge
indicator.hidesWhenStopped = true
return indicator
}()
// shows youtube player
func addYotubePlayer() {
if let window = UIApplication.shared.keyWindow {
blackView.frame = window.frame
self.addSubview(blackView)
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
let tap = UITapGestureRecognizer(target: self, action: #selector(handleDismiss))
tap.numberOfTapsRequired = 1
tap.cancelsTouchesInView = false
blackView.addGestureRecognizer(tap)
let centerX = UIScreen.main.bounds.size.width / 2
let centerY = UIScreen.main.bounds.size.height / 2
blackView.addSubview(playerIndicator)
playerIndicator.center = CGPoint(x: centerX, y: centerY)
playerIndicator.startAnimating()
blackView.addSubview(youtubePlayer)
youtubePlayer.center = CGPoint(x: centerX, y: centerY)
blackView.alpha = 0
youtubePlayer.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.blackView.alpha = 1
self.youtubePlayer.alpha = 1
}, completion: nil)
}
}
func play(_ videoID: String) {
youtubePlayer.loadVideoID(videoID)
}
#objc func handleDismiss() {
blackView.removeFromSuperview()
UIApplication.shared.keyWindow?.viewWithTag(24)?.removeFromSuperview()
UIApplication.shared.keyWindow?.removeFromSuperview()
}
func playerReady(_ videoPlayer: YouTubePlayerView) {
self.playerIndicator.stopAnimating()
}
func playerStateChanged(_ videoPlayer: YouTubePlayerView, playerState: YouTubePlayerState) {
}
func playerQualityChanged(_ videoPlayer: YouTubePlayerView, playbackQuality: YouTubePlaybackQuality) {
}
}
YouTubePlayerCell (Which I present in collectionView wthe hen server responds for video):
import UIKit
class YouTubePlayerCell: ChatMessageCell {
var player: PlayerView = PlayerView(frame: UIScreen.main.bounds)
override func setupViews() {
super.setupViews()
setupCell()
}
func setupCell() {
messageTextView.frame = CGRect.zero
textBubbleView.frame = CGRect.zero
}
func loadVideo(with videoID: String) {
player.tag = 24
UIApplication.shared.keyWindow?.addSubview(player)
player.play(videoID)
}
override func prepareForReuse() {
super.prepareForReuse()
player.removeFromSuperview()
UIApplication.shared.keyWindow?.viewWithTag(24)?.removeFromSuperview()
}
}
Here is how I am presenting the YouTubePlayerCell in cellForItemAt method of UICollectionView
let message = messages[indexPath.row]
if message.actionType == ActionType.video_play.rawValue {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ControllerConstants.youtubePlayerCell, for: indexPath) as? YouTubePlayerCell {
self.resignResponders()
if let videoId = message.videoData?.identifier {
cell.loadVideo(with: videoId)
}
return cell
}
}
Full Source Code can be found here: https://github.com/imjog/susi_iOS/tree/ytplayer
I can see that in the below code
if let videoId = message.videoData?.identifier {
cell.loadVideo(with: videoId)
}
you are calling loadVideo method, which is responsible for showing the player.
So while scrolling you are reusing the cell and it calls loadVideo method and present the player. so the solution is don't start playing the video by default on displaying the cell, provide a play/pause button on the cell video overlay and on clicking the the button start playing the video.
If my analysis is wrong please let me know, what exact issue you have.
Why do you add the player as a subView each time you have to play the video ? My suggestion would be, as you are adding the player view on the whole screen, you can have just one instance of the view and add it just once(may be at the beginning) and keep it hidden. To play the video just unhide the player and load the video.
Instead best practice would be to have a View controller for Youtube Player and present it with the video id each time you need to play and then dismissing when done.
Thanks for your answers. I solve this by this way:
Rather than presenting Player on setting on the cell, I am now adding a thumbnail to the cell and a button on thumbnail view so that whenever the user clicks play button, it opens a new controller (Previously I was presenting in UIWindow) and presenting it as modalPresentationStyle of overFullScreen by using protocol because cell cannot present a ViewController.
Protocol: (In YouTubePlayerCell class)
protocol PresentControllerDelegate: class {
func loadNewScreen(controller: UIViewController) -> Void
}
Final YouTubePlayer.swift:
import UIKit
import Kingfisher
protocol PresentControllerDelegate: class {
func loadNewScreen(controller: UIViewController) -> Void
}
class YouTubePlayerCell: ChatMessageCell {
weak var delegate: PresentControllerDelegate?
var message: Message? {
didSet {
addThumbnail()
}
}
lazy var thumbnailView: UIImageView = {
let imageView = UIImageView()
imageView.image = ControllerConstants.Images.placeholder
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 15
imageView.isUserInteractionEnabled = true
return imageView
}()
lazy var playButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(ControllerConstants.Images.youtubePlayButton, for: .normal)
button.addTarget(self, action: #selector(playVideo), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
override func setupViews() {
super.setupViews()
setupCell()
prepareForReuse()
}
func setupCell() {
messageTextView.frame = CGRect.zero
textBubbleView.frame = CGRect(x: 8, y: 0, width: 208, height: 158)
textBubbleView.layer.borderWidth = 0.2
textBubbleView.backgroundColor = .white
}
override func prepareForReuse() {
super.prepareForReuse()
thumbnailView.image = nil
}
func addThumbnail() {
textBubbleView.addSubview(thumbnailView)
textBubbleView.addConstraintsWithFormat(format: "H:|-4-[v0]-4-|", views: thumbnailView)
textBubbleView.addConstraintsWithFormat(format: "V:|-4-[v0]-4-|", views: thumbnailView)
self.downloadThumbnail()
self.addPlayButton()
}
func addPlayButton() {
thumbnailView.addSubview(playButton)
playButton.heightAnchor.constraint(equalToConstant: 44).isActive = true
playButton.widthAnchor.constraint(equalToConstant: 44).isActive = true
playButton.centerXAnchor.constraint(equalTo: thumbnailView.centerXAnchor).isActive = true
playButton.centerYAnchor.constraint(equalTo: thumbnailView.centerYAnchor).isActive = true
}
func downloadThumbnail() {
if let videoID = message?.videoData?.identifier {
let thumbnailURLString = "https://img.youtube.com/vi/\(videoID)/default.jpg"
let thumbnailURL = URL(string: thumbnailURLString)
thumbnailView.kf.setImage(with: thumbnailURL, placeholder: ControllerConstants.Images.placeholder, options: nil, progressBlock: nil, completionHandler: nil)
}
}
#objc func playVideo() {
if let videoID = message?.videoData?.identifier {
let playerVC = PlayerViewController(videoID: videoID)
playerVC.modalPresentationStyle = .overFullScreen
delegate?.loadNewScreen(controller: playerVC)
}
}
}
Delegate implementation in CollectionViewController:
extension ChatViewController: PresentControllerDelegate {
func loadNewScreen(controller: UIViewController) {
self.present(controller, animated: true, completion: nil)
}
}
Final source code can be found here: https://github.com/fossasia/susi_iOS/pull/372

Fade image of button from one image to another via a float value

In my use case, I have a slider that moves from 0.0 to 1.0. When moving that slider I want a button's image to fade from one image to another smoothly. The button will only transition between two images. As one fades in, the other fades out and vice-versa.
There are answers on SO on how to change the image of a button over a fixed duration but I can't find something based on a changing float.
So is there a way to change the image of a button over a duration from one image to another based on a float that changes from 0.0 to 1.0?
EDIT:
For example, imagine a button that transitions smoothly from an image of a red star to a green star as you move the slider back and forth. (I know you could accomplish this other ways but I want to use two images)
Give this a shot. It is doing what you want I think. The trick is to set the layer speed to 0 and just update the timeOffset.
import UIKit
class ViewController: UIViewController {
lazy var slider : UISlider = {
let sld = UISlider(frame: CGRect(x: 30, y: self.view.frame.height - 60, width: self.view.frame.width - 60, height: 20))
sld.addTarget(self, action: #selector(sliderChanged), for: .valueChanged)
sld.value = 0
sld.maximumValue = 1
sld.minimumValue = 0
sld.tintColor = UIColor.blue
return sld
}()
lazy var button : UIButton = {
let bt = UIButton(frame: CGRect(x: 0, y: 60, width: 150, height: 150))
bt.center.x = self.view.center.x
bt.addTarget(self, action: #selector(buttonPress), for: .touchUpInside)
bt.setImage(UIImage(named: "cat1"), for: .normal)
return bt
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(button)
self.view.addSubview(slider)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
addAnimation()
}
func addAnimation(){
let anim = CATransition()
anim.type = kCATransitionFade
anim.duration = 1.0
button.layer.add(anim, forKey: nil)
button.layer.speed = 0
button.setImage(UIImage(named: "cat2"), for: .normal)
}
#objc func sliderChanged(){
print("value \(slider.value)")
button.layer.timeOffset = CFTimeInterval(slider.value)
}
#objc func buttonPress(){
print("pressed")
}
}
Here is the result:
For Swift 4
//This is slider
#IBOutlet weak var mSlider: UISlider!
//Add Target for valueChanged in ViewDidLoad
self.mSlider.addTarget(self, action: #selector(ViewController.onLuminosityChange), for: UIControlEvents.valueChanged)
//called when value change
#objc func onLuminosityChange(){
let value = self.mSlider.value
print(value)
if value > 0.5 {
btnImage.imageView?.alpha = CGFloat(value)
UIView.transition(with: self.btnImage.imageView!,
duration:0.3,
options: .showHideTransitionViews,
animations: { self.btnImage.setImage(newImage, for: .normal) },
completion: nil)
}else{
btnImage.imageView?.alpha = 1 - CGFloat(value)
UIView.transition(with: self.btnImage.imageView!,
duration:0.3,
options: .showHideTransitionViews,
animations: { self.btnImage.setImage(oldImage, for: .normal) },
completion: nil)
}
}
Or Inset an Action directly via Storyboard/XIB
This is another workaround, since i tired with single button but couldn't achieve the result, so what i did is i took two buttons overlaying each other, with same size, constraints and each with separate image.
var lastSlideValue : Float = 0.0
#IBOutlet weak var slideImage: UISlider!
#IBOutlet weak var btnImageOverlay: UIButton!
#IBOutlet weak var btnSecondOverlayImage: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
btnImageOverlay.imageView?.alpha = 1.0
btnSecondOverlayImage.imageView?.alpha = 0.0
}
#IBAction func sliderValueChanged(_ sender: Any) {//UIControlEventTouchUpInside connected method
let sliderr = sender as? UISlider
let value = sliderr?.value
if lastSlideValue < (sliderr?.value ?? 0.0) {
btnSecondOverlayImage.imageView?.alpha = CGFloat(value!)
btnImageOverlay.imageView?.alpha = 1 - CGFloat(value!)
} else if lastSlideValue == sliderr?.value {
print("equal")
} else {
btnImageOverlay.imageView?.alpha = CGFloat(value!)
btnSecondOverlayImage.imageView?.alpha = 1 - CGFloat(value!)
}
}
#IBAction func userTouchedSlider(_ sender: Any) { //UIControlEventValueChanged connected method
let sliderr = sender as? UISlider
lastSlideValue = (sliderr?.value)!
}
You could use cross fade animations with single button, but binding it with slider is difficult task. Let me know if you need any assistance with the same.

How to get button to display different video according to the row it is pressed in?

Currently I have my code setup to where in each table view cell there is a button that displays a video after is pressed. Each table view cell row contains the button (there are x amount of cells) however no matter which row the button is tapped in it always leads to the same video. Is there a way to make to where, depending on the row the button is in, it displays a video? My code only has one video file in it currently but how could I make it to where depending on the cell the button is tapped in, it shows a specific video? For example, if the button is tapped in row one I want it to show a certain video, and the same for two, and three, and so on. Right now they all display the same video.
Here is my code for the table view cell:
import UIKit
import AVFoundation
import AVKit
class VideoPlayerView: UIView {
let pauseButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "Triangle 2"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = UIColor.white
button.isHidden = false
button.addTarget(self, action: #selector(handlePause), for: .touchUpInside)
return button
}()
var player: AVPlayer?
var isPlaying = false
func handlePause() {
if isPlaying {
player?.pause()
pauseButton.alpha = 1.0 }
else { player?.play()
pauseButton.alpha = 0.01
}
isPlaying = !isPlaying
}
//container view that holds sublayers for the video control objects
let controlsContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0, alpha: 1.0)
return view
}()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
//setupPlayerView()
//configures container view (video's background)
controlsContainerView.frame = frame
addSubview(controlsContainerView)
backgroundColor = UIColor.black
//following adds pause/play button to video
controlsContainerView.addSubview(pauseButton)
pauseButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
pauseButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
//function that sets up video playback
private func setupPlayerView() {
//variable that contains video url
let fileUrl = URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/dunk.mov")
player = AVPlayer(url: fileUrl)
//video only renders if you specify 'playerLayer'
let playerLayer = AVPlayerLayer(player: player)
self.layer.insertSublayer(playerLayer, at: 1)
playerLayer.frame = frame
player?.play()
//attached obeserver of 'player' to tell when 'player' is ready
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
}
//method called every time you add obserever to an object
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
//strring that lets AVPlayer know its ready
if keyPath == "currentItem.loadedTimeRanges" {
//configures container view while video is playing
controlsContainerView.backgroundColor = UIColor.clear
pauseButton.alpha = 0.05
isPlaying = true
}
}
}
class DrillsTableViewCell: UITableViewCell {
var videoURL:[URL] = [URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/dunk.mov"), URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/mk.MOV")]
var video = URL(fileURLWithPath: String())
#IBOutlet weak var logoImage: UIImageView!
#IBOutlet weak var drillTitle: UILabel!
#IBOutlet weak var playButton: UIButton!
#IBAction func watchButton(_ sender: Any) {
print(123)
//controls video background view
if let keyWindow = UIApplication.shared.keyWindow {
let view = UIView(frame: keyWindow.frame)
view.backgroundColor = UIColor.white
view.frame = CGRect(x: 0.0, y: 0.0, width: keyWindow.frame.width, height: keyWindow.frame.height)
let videoPlayerFrame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: keyWindow.frame.width * 9 / 16)
let videoPlayerView = VideoPlayerView(frame: videoPlayerFrame)
view.addSubview(videoPlayerView)
keyWindow.addSubview(view)
UIView.animate(
withDuration: 0.5,
delay: 0,
options: .curveEaseOut,
animations: {
view.frame = keyWindow.frame
},
completion: { completedAnimation in
//possible features implemented later
UIApplication.shared.isStatusBarHidden = true
}
)
}
}
}
Code for table view:
class DrillsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var arrayForKey2 = [[String]]()
var keyIndex = Int()
var headLabel = String()
var labels = Array(trainingDict.keys)
#IBOutlet weak var tableView: DrillsTableView!
#IBOutlet weak var drillLabel: UILabel!
#IBOutlet weak var labelBackground: UIView!
#IBAction func back(_ sender: Any) {
performSegue(withIdentifier: "back", sender: self)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayForKey2.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell" , for: indexPath) as! DrillsTableViewCell
cell.playButton.tag = indexPath.row
//clear background color needed in order to display gradient cell
cell.backgroundColor = UIColor.clear
//gradient configuration
gradient = CAGradientLayer()
gradient.frame = tableView.bounds
gradient.colors = [UIColor.black.cgColor, UIColor.darkGray.cgColor, UIColor.black.cgColor]
tableView.layer.insertSublayer(gradient, at: 0)
gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
//Possible method for 'drillLabel' gradient
drillLabel.font = UIFont(name: "Symbol", size: 24.0)
//attributes for watch/play button
cell.playButton.layer.shadowColor = UIColor.black.cgColor
cell.playButton.layer.shadowOffset = CGSize(width: 2, height: 2)
cell.playButton.layer.shadowOpacity = 0.7
cell.playButton.layer.shadowRadius = 1
//details for cell label display
cell.borderWidth = 1.5
cell.borderColor = UIColor.white
cell.drillTitle.text = "\(arrayForKey2[keyIndex][indexPath.row])"
cell.drillTitle.font = UIFont(name: "Symbol", size: 18.0)
cell.drillTitle.textColor = UIColor.white
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
drillLabel.text = labels[keyIndex]
}
}
I believe you should refactor your code to get required behaviour. Please check the following code:
First make changes in VideoPlayerView method named setupPlayerView .Replace your implementation with this:
func setupPlayerView(for url: URL) {
player = AVPlayer(url: url)
//video only renders if you specify 'playerLayer'
let playerLayer = AVPlayerLayer(player: player)
self.layer.insertSublayer(playerLayer, at: 1)
playerLayer.frame = frame
player?.play()
//attached obeserver of 'player' to tell when 'player' is ready
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
}
Now make change in DrillsTableViewCell ,make changes in videosURLs and I added a new variable singleVideoURL, your new class will look like this:
class DrillsTableViewCell: UITableViewCell {
var videoURLs:[URL] = [URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/dunk.mov"), URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/mk.MOV")]
#IBOutlet weak var logoImage: UIImageView!
#IBOutlet weak var drillTitle: UILabel!
#IBOutlet weak var playButton: UIButton!
#IBAction func watchButton(_ sender: UIButton) {
print(123)
//controls video background view
if let keyWindow = UIApplication.shared.keyWindow {
let view = UIView(frame: keyWindow.frame)
view.backgroundColor = UIColor.white
var singleVideoURL = videoURLs[sender.tag]
view.frame = CGRect(x: 0.0, y: 0.0, width: keyWindow.frame.width, height: keyWindow.frame.height)
let videoPlayerFrame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: keyWindow.frame.width * 9 / 16)
let videoPlayerView = VideoPlayerView(frame: videoPlayerFrame)
videoPlayerView .setupPlayerView(for: singleVideoURL)
view.addSubview(videoPlayerView)
keyWindow.addSubview(view)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: {
view.frame = keyWindow.frame
}, completion: { (completedAnimation) in
//possible features implemented later
UIApplication.shared.isStatusBarHidden = true
})
}
First you have to distinguish the buttons added in the cell by giving them tag value in tableView:cellForRowAtIndexPath: method
cell.button.tag = indexpath.row
This will set different tag values to the buttons present in each cell.
Then add below to it:
cell.button.addTarget(self, action:(YourController.buttonMethodPlayVideo(:)) , forControlEvents: .TouchUpInside)
Create a method for button to perform action on click:
func buttonMethodPlayVideo(sender: UIButton) {
print(sender.tag)
}
In above method when you will click on button you will get different tag values.
According to that value you can play different videos or pass different video name to player to play vudeo.
Setup an array to put your videos' paths into, and its indexe should match the index of the cell. In addition, you can pass the row number of the cell to the button inside as its tag. So when you tap on that button, you can find a specific video path in the array through the button's tag.

Where to put code if I want each row to display a different video file? [duplicate]

Currently I have my code setup to where in each table view cell there is a button that displays a video after is pressed. Each table view cell row contains the button (there are x amount of cells) however no matter which row the button is tapped in it always leads to the same video. Is there a way to make to where, depending on the row the button is in, it displays a video? My code only has one video file in it currently but how could I make it to where depending on the cell the button is tapped in, it shows a specific video? For example, if the button is tapped in row one I want it to show a certain video, and the same for two, and three, and so on. Right now they all display the same video.
Here is my code for the table view cell:
import UIKit
import AVFoundation
import AVKit
class VideoPlayerView: UIView {
let pauseButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "Triangle 2"), for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = UIColor.white
button.isHidden = false
button.addTarget(self, action: #selector(handlePause), for: .touchUpInside)
return button
}()
var player: AVPlayer?
var isPlaying = false
func handlePause() {
if isPlaying {
player?.pause()
pauseButton.alpha = 1.0 }
else { player?.play()
pauseButton.alpha = 0.01
}
isPlaying = !isPlaying
}
//container view that holds sublayers for the video control objects
let controlsContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0, alpha: 1.0)
return view
}()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
//setupPlayerView()
//configures container view (video's background)
controlsContainerView.frame = frame
addSubview(controlsContainerView)
backgroundColor = UIColor.black
//following adds pause/play button to video
controlsContainerView.addSubview(pauseButton)
pauseButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
pauseButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
//function that sets up video playback
private func setupPlayerView() {
//variable that contains video url
let fileUrl = URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/dunk.mov")
player = AVPlayer(url: fileUrl)
//video only renders if you specify 'playerLayer'
let playerLayer = AVPlayerLayer(player: player)
self.layer.insertSublayer(playerLayer, at: 1)
playerLayer.frame = frame
player?.play()
//attached obeserver of 'player' to tell when 'player' is ready
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
}
//method called every time you add obserever to an object
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
//strring that lets AVPlayer know its ready
if keyPath == "currentItem.loadedTimeRanges" {
//configures container view while video is playing
controlsContainerView.backgroundColor = UIColor.clear
pauseButton.alpha = 0.05
isPlaying = true
}
}
}
class DrillsTableViewCell: UITableViewCell {
var videoURL:[URL] = [URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/dunk.mov"), URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/mk.MOV")]
var video = URL(fileURLWithPath: String())
#IBOutlet weak var logoImage: UIImageView!
#IBOutlet weak var drillTitle: UILabel!
#IBOutlet weak var playButton: UIButton!
#IBAction func watchButton(_ sender: Any) {
print(123)
//controls video background view
if let keyWindow = UIApplication.shared.keyWindow {
let view = UIView(frame: keyWindow.frame)
view.backgroundColor = UIColor.white
view.frame = CGRect(x: 0.0, y: 0.0, width: keyWindow.frame.width, height: keyWindow.frame.height)
let videoPlayerFrame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: keyWindow.frame.width * 9 / 16)
let videoPlayerView = VideoPlayerView(frame: videoPlayerFrame)
view.addSubview(videoPlayerView)
keyWindow.addSubview(view)
UIView.animate(
withDuration: 0.5,
delay: 0,
options: .curveEaseOut,
animations: {
view.frame = keyWindow.frame
},
completion: { completedAnimation in
//possible features implemented later
UIApplication.shared.isStatusBarHidden = true
}
)
}
}
}
Code for table view:
class DrillsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var arrayForKey2 = [[String]]()
var keyIndex = Int()
var headLabel = String()
var labels = Array(trainingDict.keys)
#IBOutlet weak var tableView: DrillsTableView!
#IBOutlet weak var drillLabel: UILabel!
#IBOutlet weak var labelBackground: UIView!
#IBAction func back(_ sender: Any) {
performSegue(withIdentifier: "back", sender: self)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayForKey2.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell" , for: indexPath) as! DrillsTableViewCell
cell.playButton.tag = indexPath.row
//clear background color needed in order to display gradient cell
cell.backgroundColor = UIColor.clear
//gradient configuration
gradient = CAGradientLayer()
gradient.frame = tableView.bounds
gradient.colors = [UIColor.black.cgColor, UIColor.darkGray.cgColor, UIColor.black.cgColor]
tableView.layer.insertSublayer(gradient, at: 0)
gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
//Possible method for 'drillLabel' gradient
drillLabel.font = UIFont(name: "Symbol", size: 24.0)
//attributes for watch/play button
cell.playButton.layer.shadowColor = UIColor.black.cgColor
cell.playButton.layer.shadowOffset = CGSize(width: 2, height: 2)
cell.playButton.layer.shadowOpacity = 0.7
cell.playButton.layer.shadowRadius = 1
//details for cell label display
cell.borderWidth = 1.5
cell.borderColor = UIColor.white
cell.drillTitle.text = "\(arrayForKey2[keyIndex][indexPath.row])"
cell.drillTitle.font = UIFont(name: "Symbol", size: 18.0)
cell.drillTitle.textColor = UIColor.white
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
drillLabel.text = labels[keyIndex]
}
}
I believe you should refactor your code to get required behaviour. Please check the following code:
First make changes in VideoPlayerView method named setupPlayerView .Replace your implementation with this:
func setupPlayerView(for url: URL) {
player = AVPlayer(url: url)
//video only renders if you specify 'playerLayer'
let playerLayer = AVPlayerLayer(player: player)
self.layer.insertSublayer(playerLayer, at: 1)
playerLayer.frame = frame
player?.play()
//attached obeserver of 'player' to tell when 'player' is ready
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
}
Now make change in DrillsTableViewCell ,make changes in videosURLs and I added a new variable singleVideoURL, your new class will look like this:
class DrillsTableViewCell: UITableViewCell {
var videoURLs:[URL] = [URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/dunk.mov"), URL(fileURLWithPath: "/Users/jordanlagrone/Desktop/BlackHeartBB/BlackHeartBB/mk.MOV")]
#IBOutlet weak var logoImage: UIImageView!
#IBOutlet weak var drillTitle: UILabel!
#IBOutlet weak var playButton: UIButton!
#IBAction func watchButton(_ sender: UIButton) {
print(123)
//controls video background view
if let keyWindow = UIApplication.shared.keyWindow {
let view = UIView(frame: keyWindow.frame)
view.backgroundColor = UIColor.white
var singleVideoURL = videoURLs[sender.tag]
view.frame = CGRect(x: 0.0, y: 0.0, width: keyWindow.frame.width, height: keyWindow.frame.height)
let videoPlayerFrame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: keyWindow.frame.width * 9 / 16)
let videoPlayerView = VideoPlayerView(frame: videoPlayerFrame)
videoPlayerView .setupPlayerView(for: singleVideoURL)
view.addSubview(videoPlayerView)
keyWindow.addSubview(view)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseOut, animations: {
view.frame = keyWindow.frame
}, completion: { (completedAnimation) in
//possible features implemented later
UIApplication.shared.isStatusBarHidden = true
})
}
First you have to distinguish the buttons added in the cell by giving them tag value in tableView:cellForRowAtIndexPath: method
cell.button.tag = indexpath.row
This will set different tag values to the buttons present in each cell.
Then add below to it:
cell.button.addTarget(self, action:(YourController.buttonMethodPlayVideo(:)) , forControlEvents: .TouchUpInside)
Create a method for button to perform action on click:
func buttonMethodPlayVideo(sender: UIButton) {
print(sender.tag)
}
In above method when you will click on button you will get different tag values.
According to that value you can play different videos or pass different video name to player to play vudeo.
Setup an array to put your videos' paths into, and its indexe should match the index of the cell. In addition, you can pass the row number of the cell to the button inside as its tag. So when you tap on that button, you can find a specific video path in the array through the button's tag.

Add Target for UIButton Not Being Called

I posted a similar post recently and I figured out what the problem is but I don't know how to fix the problem.
In my ImageViewerViewController, my cancelButtonTapped function is not being called. But I don't think the problem, necessarily, lies in this code. Here is the code:
var image: UIImage!
var imageView: UIImageView!
var scrollView: UIScrollView!
var disableSavingImage: Bool!
var pan: UIPanGestureRecognizer!
var cancelButton: UIButton!
var cancelButtonImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.0)
setCancelButton()
}
func setCancelButton() {
self.cancelButton = UIButton(type: UIButtonType.RoundedRect)
self.cancelButton.addTarget(self, action: "cancelButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside)
self.cancelButton.setImage(cancelButtonImage, forState: UIControlState.Normal)
self.cancelButton.tintColor = UIColor.whiteColor()
self.cancelButton.frame = CGRectMake(self.view.frame.size.width - (self.cancelButtonImage.size.width * 2) - 18, 18, self.cancelButtonImage.size.width * 2, self.cancelButtonImage.size.height * 2)
self.view.addSubview(self.cancelButton)
}
func cancelButtonTapped(sender: UIButton) {
print("cancelButtonTapped")
}
func centerPictureFromPoint(point: CGPoint, ofSize size: CGSize, withCornerRadius radius: CGFloat) {
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.view.backgroundColor = UIColor(colorLiteralRed: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
}) { (finished: Bool) -> Void in
}
}
I think the problem lies in my CathyTaskLogMessageViewController, when I change the code from
self.view.addSubview(imageViewerViewController.view) to self.presentViewController(imageViewerViewController, animated: true, completion: nil), my add target function gets called. Here's the code:
func defaultPictureButtonTapped(sender: UIButton) {
let imageViewerViewController = ImageViewerViewController()
imageViewerViewController.image = self.defaultPictureButton.imageView!.image
imageViewerViewController.cancelButtonImage = UIImage(named: "cancelButtonImage")
self.view.addSubview(imageViewerViewController.view)
imageViewerViewController.centerPictureFromPoint(self.defaultPictureButton.frame.origin, ofSize: self.defaultPictureButton.frame.size, withCornerRadius: self.defaultPictureButton.layer.cornerRadius)
// self.presentViewController(imageViewerViewController, animated: true, completion: nil)
}
However, I don't want to present a view controller. I want to add it as a subview. Anybody have any idea to fix this? And thank you so much for your help in advance.
The issue is in how you add child view controller.
You need to call addChildViewController(imageViewerViewController) before self.view.addSubview(imageViewerViewController.view)
Once what happened to me while using subviews was that the view was in front of the subview and hence the button on the subview was not getting tapped.
Try this:-
imageViewerViewController.view.layer.zPosition = 2
//You can set any value in place of 2 which is greater than zposition value of the view
self.view.addSubview(imageViewerViewController.view)
Hope this works.

Resources