I am using AVPlayerViewController to play mp3 audio files. I can get the player to appear programmatically with no problems. I have also added subviews in order to set an overlay for an imageView and textView. This works fine but I cannot get the textView to scroll. I was able to test the same basic code using just a regular UIViewController and the textView scrolls fine. So, I'm wondering if there's an issue with scrolling using AVPlayerViewController or if I've missed something. Please note: This is NOT a duplicate of my other post about AVPlayerViewController. In the other post, I'm asking if it's possible to use Storyboard somehow to customize in this way. Any help/suggestions regarding scrolling here would be greatly appreciated. Code is as follows:
let url = URL(string:mystream)
let player = AVPlayer(url: url!)
let controller = AVPlayerViewController()
controller.player = player
controller.view.frame = self.view.frame
controller.contentOverlayView!.backgroundColor = UIColor(red: 150/255, green: 51/255, blue: 251/255, alpha: 1)
//.. view
var myView = UIView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height))
//.. imageView
var imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 325, height: 325))
imageView.image = myImage
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
//.. textView
var myTextView = UITextView(frame: CGRect(x: 0, y: 0, width: 350, height: 600))
//myTextView.text = currentList
myTextView.text = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. "
myTextView.font = UIFont(name: "Chalkboard SE", size: 18)
myTextView.textColor = UIColor.yellow
myTextView.backgroundColor = UIColor.gray
//..
myTextView.contentMode = .scaleAspectFit
myTextView.isScrollEnabled = true
myTextView.isUserInteractionEnabled = true
myTextView.isEditable = true
myTextView.isSelectable = true
myTextView.bounces = true
myTextView.showsVerticalScrollIndicator = true
myTextView.showsHorizontalScrollIndicator = true
myTextView.contentSize = CGSize(width: 700, height: 700)
myTextView.translatesAutoresizingMaskIntoConstraints = false
myView.addSubview(imageView)
myView.addSubview(myTextView)
controller.contentOverlayView?.addSubview(myView)
//.. @@@@@@@@@
imageView.topAnchor.constraint(equalTo: myView.safeAreaLayoutGuide.topAnchor, constant: 50).isActive = true
imageView.centerXAnchor.constraint(equalTo: myView.centerXAnchor, constant: 0).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 325).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 325).isActive = true
myTextView.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 15).isActive = true
myTextView.centerXAnchor.constraint(equalTo: myView.centerXAnchor, constant: 0).isActive = true
myTextView.widthAnchor.constraint(equalToConstant: 325).isActive = true
myTextView.heightAnchor.constraint(equalToConstant: 200).isActive = true
//.. @@@@@@@@@
present(controller, animated: true) {
player.play()
}
Here is a picture of what the presented player should look like... I need the textView to scroll...
UPDATE: So, after seeing Shawn's answer and thinking about this, I would like to try to also implement a tabBar type design like say Spotify where the user can navigate to various tabs, still have the "player" playing music, and have a "mini player" at the bottom of the screen. See my next screenshot. Best way to do this? AVPlayerViewController? AVPlayer?
There are a lot of gesture recognizers already present on an AVPlayerViewController
to pan
, pinch
etc to either interact with the media and / or dismiss the AVPlayerViewController
once presented.
Because of this, I believe your UITextView does not receive it's interaction events and so nothing happens when the user tries to scroll.
Infact, Apple documents that the contentOverlayView
of the AVPlayerViewController
should be used for non interactive views.
contentOverlayView
A view that displays between the video content and the playback controls.
Discussion
Use the content overlay view to add noninteractive custom views, such as a logo or watermark, between the video content and the controls.
What I suggest is the following like I suggested in your other question:
UIViewController
AVPlayerViewController
as a child view to this custom view controllerUIImageView
and the UITextView
to this custom UIViewController
and not to the AVPlayerViewController
Here is how I would do that is as follows:
import UIKit
import AVKit
class CustomPlayerVC: UIViewController
{
var image: UIImage?
var streamURL: URL?
var metaText = ""
private let imageView = UIImageView()
private let myTextView = UITextView()
private let playerViewController = AVPlayerViewController()
// Bool to check if your player view has been configured
// so that you configure only once
private var isPlayerConfigured = false
// Custom init
init(withStreamURL streamURL: URL?,
image: UIImage?,
metaText: String)
{
self.streamURL = streamURL
self.image = image
self.metaText = metaText
super.init(nibName: nil, bundle: nil)
}
// Storyboard support if you want to add this view controller
// in your storyboard
required init?(coder: NSCoder)
{
super.init(coder: coder)
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
// Check if the player is already configured
if !isPlayerConfigured
{
isPlayerConfigured = true
configurePlayer()
configureImageView()
configureTextView()
}
}
private func configurePlayer()
{
if let streamURL = streamURL
{
let player = AVPlayer(url: streamURL)
playerViewController.player = player
// Add the AVPlayerController as a child of the current
// view controller
self.addChild(playerViewController)
// Configure the player view
let playerView = playerViewController.view
playerView?.backgroundColor = .clear
playerView?.frame = self.view.bounds
// Add the AVPlayerViewController's view as a subview
// of the current view
self.view.addSubview(playerView!)
playerViewController.didMove(toParent: self)
// Start playing the content
playerViewController.player?.play()
// Add this to hide the default quick time player logo
let contentBGView = UIView(frame: view.bounds)
contentBGView.backgroundColor
= UIColor(red: 150/255, green: 51/255, blue: 251/255, alpha: 1)
playerViewController.contentOverlayView!.addSubview(contentBGView)
}
}
// Configure your image view with auto-layout
private func configureImageView()
{
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = image
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
imageView.topAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 50)
.isActive = true
imageView.centerXAnchor
.constraint(equalTo: view.centerXAnchor, constant: 0)
.isActive = true
imageView.widthAnchor
.constraint(equalToConstant: 325)
.isActive = true
imageView.heightAnchor
.constraint(equalToConstant: 325)
.isActive = true
imageView.layer.cornerRadius = 20
imageView.clipsToBounds = true
}
// Configure your text view with auto-layout
private func configureTextView()
{
myTextView.translatesAutoresizingMaskIntoConstraints = false
myTextView.text = metaText
myTextView.font = UIFont.systemFont(ofSize: 18)
myTextView.textColor = UIColor.yellow
myTextView.backgroundColor = UIColor.gray
view.addSubview(myTextView)
// Your auto layout constraints
myTextView.topAnchor
.constraint(equalTo: imageView.bottomAnchor, constant: 15)
.isActive = true
myTextView.centerXAnchor
.constraint(equalTo: view.centerXAnchor, constant: 0)
.isActive = true
myTextView.widthAnchor
.constraint(equalToConstant: 325)
.isActive = true
myTextView.heightAnchor
.constraint(equalToConstant: 200)
.isActive = true
}
}
And then when you want to transition to your player, this is what you do:
private func playAudio()
{
let text = "your long text goes here"
let streamURL = URL(string: "http://stream.radiojar.com/nhq0vcqwuueuv")
// Initialize the custom player view controller
let customPlayerVC
= CustomPlayerVC(withStreamURL: streamURL,
image: UIImage(named: "art"),
metaText: text)
// This is important to avoid seeing the full screen player button
customPlayerVC.modalPresentationStyle = .fullScreen
present(customPlayerVC, animated: true) {
// do what you want
}
}
The end result is a player which plays your audio stream, displays the image and gives you a text view which can be scrolled.
Here is a glimpse of the user interaction: