I'm currently making a single view app in Swift 4 / Xcode 10 where there is an animated background and 6 buttons on the screen, with each button linking to a separate ViewController. When I run the app, the animated background seems to move to the front layer and blocks all of the buttons. How do I make it so that the buttons show up in front of the background?
The problem is, I did not create the buttons programatically, but instead dragged and dropped them from the objects library in the interface builder. This is why when I tried the solutions posted here and here, it didn't work, because I don't know if there is any actual code that references any of the buttons. For example, when I tried
view.bringSubview(toFront: theButton)
I wasn't sure what to put in place of theButton, or even where to put that snippet of code.
Below is the code I have on my 'home screen' view controller. My most pressing question is where I would insert code to move either the buttons to the front or the background to the back.
import UIKit
import AVFoundation
import AVKit
class ViewController: UIViewController {
#IBOutlet weak var videoView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private func setupView() {
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "Ocean_Waves_slow_motion_videvo", ofType: "mov")!)
let player = AVPlayer(url: path)
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.videoView.frame
self.videoView.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
player.play()
player.actionAtItemEnd = AVPlayer.ActionAtItemEnd.none
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.videoDidPlayToEnd(_:)), name: NSNotification.Name(rawValue: "AVPlayerItemDidPlayToEndTimeNotification"), object: player.currentItem)
}
#objc func videoDidPlayToEnd(_ notification: Notification) {
let player: AVPlayerItem = notification.object as! AVPlayerItem
player.seek(to: CMTime.zero)
}
}
Here is what my Main.storyboard looks like. If it helps, the hierarchy is currently:
View > Video View > View > Button Button Button Button Button Button
1st View -> Video View -> buttonsBgView -> Button Button Button Button Button Button
Don't add the buttonsBgView as subview of Video View. Video View and buttonsBgView should be subview of 1st View.
1st View -> Video View
1st View -> buttonsBgView -> Button Button Button Button Button Button
Create a IBOutlet for the buttonsBgView, and bring that view to front
class ViewController: UIViewController {
#IBOutlet weak var videoView: UIView!
#IBOutlet weak var buttonsBgView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
setupView()
buttonsBgView.backgroundColor = .clear
view.bringSubview(toFront: buttonsBgView)
}
}
Related
I am trying to make local video playable with AVPlayer in xcode 10.1, swift 4.2, macOS app. I have created the AVKit Player View object in UI and made outlet in viewController.swift. Also created button in where all the actions should happen. You can see the code here -
import Cocoa
import AVKit
import AVFoundation
import AppKit
class ViewController: NSViewController {
#IBOutlet weak var playerView: AVPlayerView!
#IBAction func buttonAction(_ sender: Any) {
let videoURL = "/Users/ramix/Downloads/test.mp4"
let video = AVPlayer(url: URL(fileURLWithPath: videoURL))
let videoPlayer = AVPlayerView()
videoPlayer.player = video
present(videoPlayer, animator:true, completion:{
video.play()
})
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
I saw in multiple places that there was used AVPlayerViewController, but for some reason I dont have option to choose that method. Also this code returns this error -
Cannot invoke 'present' with an argument list of type '(AVPlayerView, animator: Bool, completion: () -> Void)'
I am new to swift, and I would like to ask for your help.
Thank you!
It seems you already created an outlet to an AVPlayerView which you probably added to the view hierarchy in Interface Builder. So you don't need to create a new player view, just assign the player to the outlet view like so:
self.playerView.player = video
Btw, the error happens because present is meant for presenting other view controllers, not for displaying views.
I have a UIViewcontroller that contains a UIView and a button widget. Here is an image of the viewcontroller class
Image of my main VC
I'm trying to play a video on the uiview(like in background) and use the button to stop or play the video but the thing is when I press the play button the video comes(redeners) on top the of the button and I can see and unpause it(well it should restart becuase I haven't written the code to handle that)
here is my ibaction function's code associated with that button:
#IBOutlet weak var videoView: UIView!
#IBAction func playButtonPressed(_ sender: UIButton)
{
let path = URL(fileURLWithPath: Bundle.main.path(forResource: "color spiral", ofType: "mov")!)
let player = AVPlayer(url: path)
let newLayer = AVPlayerLayer(player: player)
newLayer.frame = self.videoView.bounds
self.videoView.superview?.layer.addSublayer(newLayer)
newLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
player.play()
}
this is what I get when I press the button:
image of simulator after i press the button
so how do I play the video but not over my button or keep the button showing?
Try dragging the [button] in Document Outline (left panel of Interface Builder) outside of "video view" and into the "view".
I want my several view controllers to have a player in the bottom. This player consists of 2 views: the player and a button which toggles it (can be hidden or expanded).
Now I use the code below in each view controller to add this player.
#IBOutlet weak var broadcastView: BroadcastView!
#IBOutlet weak var broadcastViewBottomConstraint: NSLayoutConstraint!
#IBOutlet weak var avatarImageView: UIImageView!
#IBAction func toggleBroadcastMode(_ sender: ToggleBroadcastButton) {
if sender.isExpanded {
broadcastViewBottomConstraint.hideBroadcastView()
} else {
broadcastViewBottomConstraint.expandBroadcastView()
}
animateBroadcastToggle()
sender.toggle()
broadcastView.toggleBroadcastView()
}
Is there a way not to duplicate the code over and over? Maybe I can create parent VC or View to do it? If so, then how?
I personally would subclass a UINavigationController and have it in there, that way you can navigate through the flow while the player stays looking good at the bottom, if you need a VC to interact with it then you can
if let nav = navigationController as? MyPlayerNavController {
nav.PlayThis()
}
you can have it change size and everything from there and you wont lose it during transitions and stuff like the music app when playing music.
Add it as a subview of the keyWindow. That way it will always stay in all the viewControllers.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var overlayViewFrame = UIScreen.main.bounds
overlayViewFrame.origin.y = overlayViewFrame.height - 200
overlayViewFrame.size.height = 200
let overlayView = UIView(frame:overlayViewFrame)
overlayView.backgroundColor = UIColor.cyan
UIApplication.shared.keyWindow?.addSubview(overlayView)
}
This code is a sample generic code. If you want certain VCs to not show this, keep this overlay view in a singleton, and hide in appropriate VCs.
Output screenshots:
Currently I am having a dialogue view with four controls at the bottom. Each control is loading a different view inside the dialogue. One of these controls is setting an AVPlayer inside the dialogue view and is playing it. Unfortunately the AVPlayer itself comes without playback controls.
The AVPlayerViewController how ever does have playback controls. Is it possible to place a AVPlayerViewController inside a UIView so that it does not get started as a new screen? I would like to place it inside my UIView so that everything is taking place inside my dialgoue.
You can initialize the AVPlayerViewController and add it as a child to your ViewController , and insert it as a subview.
[self addChildViewController:yourPlayerViewController];
[self.view addSubview:yourPlayerViewController.view];
[yourPlayerViewController didMoveToParentViewController:self];
And to remove it:
[yourPlayerViewController willMoveToParentViewController:nil];
[yourPlayerViewController.view removeFromSuperview];
[yourPlayerViewController removeFromParentViewController];
EDIT SWIFT METHOD:
Check here for Swift
I'm using Swift 3.2 / Xcode 9.2 / iOS 11
This is how to do it in 9 easy steps. The steps are explained above each line
**Make sure you import AVKit to have access to the AVPlayerViewController or you won't be able to use it
// 1. to have access to the AVPlayerViewController import AVKit
import AVKit
// 2. create your class properties
var videoUrl: URL?
var player: AVPlayer?
let avPVC = AVPlayerViewController()
override func viewDidLoad() {
super.viewDidLoad()
// 3. make sure the url your using isn't nil
guard let videoUrl = self.videoUrl else { return }
// 4. init an AVPlayer object with the url then set the class property player with the AVPlayer object
self.player = AVPlayer(url: videoUrl)
// 5. set the class property player to the AVPlayerViewController's player
avPVC.player = self.player
// 6. set the the parent vc's bounds to the AVPlayerViewController's frame
avPVC.view.frame = self.view.bounds
// 7. the parent vc has a method on it: addChildViewController() that takes the child you want to add to it (in this case the AVPlayerViewController) as an argument
self.addChildViewController(avPVC)
// 8. add the AVPlayerViewController's view as a subview to the parent vc
self.view.addSubview(avPVC.view)
// 9. on AVPlayerViewController call didMove(toParentViewController:) and pass the parent vc as an argument to move it inside parent
avPVC.didMove(toParentViewController: self)
}
SWIFT 4 - Subview Method
The initial method works alright, but I feel like the view controller method seems messy when I really just want a subview. So, most of the time I play the video in a custom view (rather than using the entire AVPlayerViewController).
I use this method to show the video in a subview which completely fills an existing view. In the example below, urlToVideo is the URL to the video that you want to play, and existingView is the view we want to place our new view into (as a subview).
First, create a VideoPlayerView class in a new VideoPlayerView.swift file, with this, which is directly copy/paste from Apple's website, except that I called it VideoPlayerView rather than PlayerView.
import Foundation
import UIKit
import AVFoundation
/// A simple `UIView` subclass that is backed by an `AVPlayerLayer` layer.
class VideoPlayerView: 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
}
}
Then, when I want to play the video, I create the subview and play it:
let player = AVPlayer(url: urlToVideo)
let playerFrame = CGRect(x: 0, y: 0, width: existingView.frame.width, height: existingView.frame.height)
let videoPlayerView = VideoPlayerView(frame: playerFrame)
videoPlayerView.player = player
existingView.addSubview(videoPlayerView)
player.play()
My solution is to have your view like a canvas and basically draw the AVPlayerViewController on top of it:
#IBOutlet weak var layerVideoView: UIImageView!
guard let url = Bundle.main.path(forResource: "santana", ofType: "mp4") else { return debugPrint("video not found") }
let path = URL(fileURLWithPath: url)
let player = AVPlayer(url: path)
let playerController = AVPlayerViewController()
playerController.player = player
playerController.view.frame = CGRect(x: layerVideoView.frame.origin.x, y: layerVideoView.frame.origin.y, width: layerVideoView.frame.width, height: layerVideoView.frame.height)
self.addChild(playerController)
self.view.addSubview(playerController.view)
LayerVideoView is an image view, but you can use just a UIView if that suits.
Its is important to add constraints in the Storyboard to your View because your Video Player will mimic the shape of your View.
So I am using a custom View (.xib) which contains couple of objects: UIImage view & UIButton. The custom view in contained and used in View controller UIView , however I can't seem to figure out how to detect if the button(from .xib) is tapped and launch a function in the view controller. Any suggestions?
I can However add UITap gesture to the entire Viewcontroller UIView, however Thats not what i need.
#IBAction func calculateTapped(sender : AnyObject) {
//You can get action here
}
Step 1
Link Your all button to to xib cocoa class
class DialogHandler: UIView {
#IBOutlet weak var acceptBtn: UIButton!
#IBOutlet weak var closeBtn: UIButton!
}
step 2
Add Below code in viewDidLoad()
if let customView = Bundle.main.loadNibNamed("DialogDemo", owner: self, options: nil)?.first as? DialogHandler {
dialogView = customView
customView.acceptBtn.addTarget(self, action: #selector(ViewController.acceptBtnPressed(sender:)), for: .touchUpInside)
}
step 3
Create Function for Button that can handle your click event
#objc func acceptBtnPressed (sender:UIButton) { print("Button Pressed") }