How to check if AVPlayer has Video or just Audio? - xcode

I try to play some "media" but at the time the AVPlayer starts I don't know if it is audio or Video.
I connected the player Layer and it works fine.
self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:[[PCPlayerManager sharedManager] audioPlayer]];
avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
avPlayerLayer.frame = CGRectMake(0, 0, videoView.frame.size.width, videoView.frame.size.height);
[videoView.layer addSublayer:avPlayerLayer];
But how can I check if there is Video so I can add/remove some Options?

From Apple’s sample code project AVSimplePlayer:
// Set up an AVPlayerLayer according to whether the asset contains video.
if ([[(AVAsset *)asset tracksWithMediaType:AVMediaTypeVideo] count] != 0)

For Swift 4.2 you can do the following:
func isAudioAvailable() -> Bool? {
return self.player?._asset?.tracks.filter({$0.mediaType == AVMediaType.audio}).count != 0
}
func isVideoAvailable() -> Bool? {
return self.player?._asset?.tracks.filter({$0.mediaType == AVMediaType.video}).count != 0
}
or as extension
extension AVPlayer {
var isAudioAvailable: Bool? {
return self._asset?.tracks.filter({$0.mediaType == AVMediaType.audio}).count != 0
}
var isVideoAvailable: Bool? {
return self._asset?.tracks.filter({$0.mediaType == AVMediaType.video}).count != 0
}
}

I'm not sure but AVPlayerItem has the following array [mPlayerItem.asset.tracks] This contains two objects,one for video and another for audio.Access it as follows [mPlayerItem.asset.tracks objectAtIndex:0] for video and [mPlayerItem.asset.tracks objectAtIndex:1] for audio.

This helped me:
// source from https://stackoverflow.com/a/55581216 Changed for last release AVFoundation
func isAudioAvailable() -> Bool? {
return self.player.currentItem?.tracks.filter({$0.assetTrack!.mediaType == AVMediaType.audio}).count != 0
}
func isVideoAvailable() -> Bool? {
return self.player.currentItem?.tracks.filter({$0.assetTrack!.mediaType == AVMediaType.video}).count != 0
}

I found this better solution
func isVideoAvailable(item: AVPlayerItem) -> Bool {
for video in item.tracks {
if video.assetTrack?.mediaType == .video {
return true
}
}
return false
}
And this
if currentItem.tracks.contains(where: { track in
track.assetTrack?.mediaType == .video
}) {
// This Code goes Here!
}

UPDATED: sean-stayns answer in Swift 5.5
extension AVPlayer {
var isAudioAvailable: Bool? {
return self.currentItem?.asset.tracks.filter({$0.mediaType == .audio}).count != 0
}
var isVideoAvailable: Bool? {
return self.currentItem?.asset.tracks.filter({$0.mediaType == .video}).count != 0
}
}

Related

Where to call NotificationCenter.default.addObserver() ? in my Xcode Game Project

Where to call NotificationCenter.default.addObserver() ? in my Xcode Game Project
I successfully call the following from my func application (AppDelegate), but when I toggle the Gamepad on/off, my selectors are not being called.
class GameScene: SKScene {
func ObserveForGameControllers() {
// print("ObserveForGameControllers")
NotificationCenter.default.addObserver(
self,
selector: #selector(connectControllers),
name: NSNotification.Name.GCControllerDidConnect,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(disconnectControllers),
name: NSNotification.Name.GCControllerDidDisconnect,
object: nil)
} // ObserveForGameControllers
}
My selectors look like this:
#objc func connectControllers() {}
#objc func disconnectControllers() {}
One last thing:
Here are my Gamepad settings in my Project
It seems I really need some suggestions here.
Appreciate it.
EDIT
I have been in contact with a very talented jrturton on trying to discover why I am unable to detect the presence of my Gamepad as documented above.
He has asked for a more complete presentation of my Swift code. I initially thought of Dropbox, but he has asked for this EDIT .. so here goes:
I began with a iOS Game Project which presented me with AppDelegate, GameScene, GameViewController + Storyboard.
I’ve already covered AppDelegate above, which per jrturton’s recommendation is now reduced to the standard AppDelegate func’s which essentially are empty, such as:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// currently empty
}
Next, the GameScene ..
import SwiftUI
import WebKit
import SpriteKit
import GameplayKit
import GameController
class GameScene: SKScene {
override func sceneDidLoad() {
super.sceneDidLoad()
// print("sceneDidLoad")
ObserveForGameControllers()
} // sceneDidLoad
func ObserveForGameControllers() {
// print("ObserveForGameControllers")
NotificationCenter.default.addObserver(
self,
selector: #selector(connectControllers),
name: NSNotification.Name.GCControllerDidConnect,
object: nil)
NotificationCenter.default.addObserver(
self,
selector: #selector(disconnectControllers),
name: NSNotification.Name.GCControllerDidDisconnect,
object: nil)
} // ObserveForGameControllers
#objc func connectControllers() {
// print("CONNECT")
self.isPaused = false
var indexNumber = 0
for controller in GCController.controllers() {
if controller.extendedGamepad != nil {
controller.playerIndex = GCControllerPlayerIndex.init(rawValue: indexNumber)!
indexNumber += 1
setupControllerControls(controller: controller)
}
}
} // connectControllers
#objc func disconnectControllers() {
// print("DIS-CONNECT")
self.isPaused = true
} // disconnectControllers
func setupControllerControls(controller: GCController) {
controller.extendedGamepad?.valueChangedHandler = {
(gamepad: GCExtendedGamepad, element: GCControllerElement) in
self.controllerInputDetected(gamepad: gamepad,
element: element,
index: controller.playerIndex.rawValue)
}
} // setupControllerControls
func controllerInputDetected(gamepad: GCExtendedGamepad,
element: GCControllerElement,
index: Int) {
// A-Button
if (gamepad.buttonA == element)
{
if (gamepad.buttonA.value != 0)
{
// These print(..) statements will be replaced later
// by code to access my Javascript methods.
print("movePaddleDown")
}
}
// B-Button
else if (gamepad.buttonB == element)
{
if (gamepad.buttonB.value != 0)
{
print("movePaddleRight")
}
}
// Y-Button
else if (gamepad.buttonY == element)
{
if (gamepad.buttonY.value != 0)
{
print("movePaddleUp")
}
}
// X-Button
else if (gamepad.buttonX == element)
{
if (gamepad.buttonX.value != 0)
{
print("movePaddleLeft")
}
}
// leftShoulder
else if (gamepad.leftShoulder == element)
{
if (gamepad.leftShoulder.value != 0)
{
print("cyclePages")
}
}
// rightShoulder
else if (gamepad.rightShoulder == element)
{
if (gamepad.rightShoulder.value != 0)
{
print("newGame")
}
}
// leftTrigger
else if (gamepad.leftTrigger == element)
{
if (gamepad.leftTrigger.value != 0)
{
print("pauseGame")
}
}
// rightTrigger
else if (gamepad.rightTrigger == element)
{
if (gamepad.rightTrigger.value != 0)
{
print("resumeGame")
}
}
// Left Thumbstick
else if (gamepad.leftThumbstick == element)
{
if (gamepad.leftThumbstick.xAxis.value > 0)
{
print("movePaddleRight")
}
else if (gamepad.leftThumbstick.xAxis.value < 0)
{
print("movePaddleLeft")
}
else if (gamepad.leftThumbstick.xAxis.value == 0)
{
print("decreaseSpeed")
}
else if (gamepad.leftThumbstick.yAxis.value > 0)
{
print("movePaddleDown")
}
else if (gamepad.leftThumbstick.yAxis.value < 0)
{
print("movePaddleUp")
}
else if (gamepad.leftThumbstick.yAxis.value == 0)
{
print("decreaseSpeed")
}
}
// Right Thumbstick
if (gamepad.rightThumbstick == element)
{
if (gamepad.rightThumbstick.xAxis.value > 0)
{
print("movePaddleRight")
}
else if (gamepad.rightThumbstick.xAxis.value < 0)
{
print("movePaddleLeft")
}
else if (gamepad.rightThumbstick.xAxis.value == 0)
{
print("decreaseSpeed")
}
else if (gamepad.rightThumbstick.yAxis.value > 0)
{
print("movePaddleDown")
}
else if (gamepad.rightThumbstick.yAxis.value < 0)
{
print("movePaddleUp")
}
else if (gamepad.rightThumbstick.yAxis.value == 0)
{
print("decreaseSpeed")
}
}
// D-Pad
else if (gamepad.dpad == element)
{
if (gamepad.dpad.xAxis.value > 0)
{
print("scrollWindowRight")
}
else if (gamepad.dpad.xAxis.value < 0)
{
print("scrollWindowLeft")
}
else if (gamepad.dpad.yAxis.value > 0)
{
print("scrollWindowDown")
}
else if (gamepad.dpad.yAxis.value < 0)
{
print("scrollWindowUp")
}
}
} // controllerInputDetected
} // class GameScene: SKScene
Now, the GameViewController ..
import UIKit
import SpriteKit
import GameplayKit
import WebKit
// This is now available across Classes
var theWebView: WKWebView!
class GameViewController: UIViewController, WKNavigationDelegate {
override func loadView() {
// print("loadView")
let webConfiguration = WKWebViewConfiguration()
theWebView = WKWebView(frame: .zero, configuration: webConfiguration)
theWebView.navigationDelegate = self
view = theWebView
} // loadView
override func viewDidLoad() {
super.viewDidLoad()
// print("viewDidLoad")
loadURL(webAddress: "https://www.lovesongforever.com/firstgame")
} // viewDidLoad
func loadURL(webAddress: String) {
let theURL = URL(string: webAddress)
let theRequest = URLRequest(url: theURL!)
theWebView.load(theRequest)
theWebView.allowsBackForwardNavigationGestures = false
} // loadURL
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
}
else {
return .all
}
} // supportedInterfaceOrientations
override var prefersStatusBarHidden: Bool {
return true
} // prefersStatusBarHidden
} // class GameViewController
Note that when I RUN my iOS App, thanks to the overridden loadView() above, it presents the following in the Simulator:
Simulator presentation
But, that’s is as far as it goes, because pressing all the buttons on my Gamepad does not result in detection of my Gamepad, as evidenced when I UN-comment all the above print(..) statements. In particular, those within:
#objc func connectControllers() and
#objc func disconnectControllers() and
func controllerInputDetected( .. )
So, hopefully that is all there currently is ..
You are doing this in your app delegate:
let itsGameScene = GameScene()
itsGameScene.ObserveForGameControllers()
You're creating an instance of GameScene, making it listen for notifications (adding self as the observer)... and then probably throwing that instance away.
Your game isn't actually using SpriteKit, and SpriteKit isn't needed to deal with game controllers, you just need GameController.
You have no need to create this scene at all. The best place to observe the game controller notifications would be in your GameViewController in viewDidLoad. Move the code (the observation method and the controller-related ones) you had in your scene into the view controller, and delete the scene file.

Argument passed to call that takes no arguments.. Firebase

Not sure why i'm getting this.. any suggestions would be grateful!
I ran into issues with my original coding where I had Firebase pod and Firebase Package.. so I started from scratch since that wasnt fixing itself.. now I get this.. and I am at a loss for how to resolve it.
static func fetchUsers() -> AnyPublisher<[UserProfile], Error> {
Future< [UserProfile], Error > { promise in
self.db.collection("Users")
.getDocuments { (snapshot, error) in
if let error = error {
promise(.failure(error))
return
}
guard let snapshot = snapshot else {
promise(.failure(FirebaseError.badSnapshot))
return
}
var users = [UserProfile]()
snapshot.documents.forEach { document in
print(users.count)
if let user = try? document.data(as: UserProfile.self){
if users.contains(where: { $0.id == user.id}) {return}
users.append(user)
} else {
print("Not working")
}
}
promise(.success(users))
}
}
.eraseToAnyPublisher()
}
I believe this is the syntax you're after:
var users = [UserProfile]()
users = snapshot.documents.compactMap { (document) -> UserProfile? in
if users.contains(where: { $0.id == user.id}) {
return nil
} else {
return try? document.data(as: UserProfile.self)
}
}
Also be aware that when you iterate something in Swift and encounter a false condition on an iteration, return will return out of the greater scope, not just that iteration. Therefore, use continue.
for x in y {
guard x > 0 else {
continue // continues loop
}
// ...
}

use of undeclared type UISegment swift tvOS

I am working on tvOS application & using UISegmentedControl . On didUpdateFocusInContext i had following code to change the image of UISegment like -
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
if let previousItem = context.previouslyFocusedView as! UISegment
{
if previousItem.selectedSegmentIndex == 0
{
previousItem.insertSegmentWithImage(UIImage(named: "white_save_star.png"), atIndex: previousItem.selectedSegmentIndex, animated: false)
}
else if previousItem.selectedSegmentIndex == 1
{
previousItem.insertSegmentWithImage(UIImage(named: "white_goto-arrow.png"), atIndex: previousItem.selectedSegmentIndex, animated: false)
}
}
if let nextItem = context.nextFocusedView as? UISegment
{
if nextItem.selectedSegmentIndex == 0
{
nextItem.insertSegmentWithImage(UIImage(named: "Blue_save_star.png"), atIndex: nextItem.selectedSegmentIndex, animated: false)
}
else if nextItem.selectedSegmentIndex == 1
{
nextItem.insertSegmentWithImage(UIImage(named: "Blue_goto-arrow.png"), atIndex: nextItem.selectedSegmentIndex, animated: false)
}
}
}
but getting following error use of undeclared type UISegment
while printing po context.previouslyFocusedView
result -
▿ Optional<UIView>
- Some : <UISegment: 0x7f861c87da10; frame = (0 0; 300 70); opaque = NO; layer = <CALayer: 0x7f861c87e180>>
as you can see in output it is clear that UISegment class exists.
any help will be appreciated
Here is something which works
if String(describing: type(of: context.nextFocusedView!)) == "UISegment"{
//handle segment focus
}

Swift 3: how to write this for(;;) loop

Looks like Apple doesn't like C loops, but doesn't provide good approach over it (or I couldn't find it). I have such loop to go from some view to the root in UI hierarchy:
for var parentView = view; parentView != nil; parentView = parentView.parent {
...
}
How to write this in Swift 3 manner?
This would be a way to do it in Swift 3:
var parentView: View! = view
while parentView != nil {
// Do stuff
parentView = parentView.parent
}
If you want to group the loop progression stuff next to while and not at the end of block, you may use defer, like this:
var parentView: View! = view
while parentView != nil {
defer { parentView = parentView.parent }
// Do stuff
}
If you want to limit the scope of parentView, you can encapsulate everything in a do block:
do {
var parentView: View! = view
while parentView != nil {
defer { parentView = parentView.parent }
// Do stuff
}
}
But it's quite verbose so you could define a new generic function for similar loops, like this:
func kindaCStyleLoop<T>(first: T, obtainNext: T -> T?, action: T -> ()) {
var current: T! = first
repeat {
action(current)
current = obtainNext(current)
} while current != nil
}
kindaCStyleLoop(view, obtainNext: { $0.parent }) {
// Do stuff with $0
}
And a last one that relies on GeneratorType and SequenceType to enable using the for-in-loop syntax:
struct CStyleGenerator<T> : GeneratorType, SequenceType {
let getNext: T -> T?
var current: T!
init(first: T, getNext: T -> T?) {
self.getNext = getNext
self.current = first
}
mutating func next() -> T? {
defer {
if current != nil {
current = getNext(current)
}
}
return current
}
}
for parentView in CStyleGenerator(first: view, getNext: { $0.parent }) {
// Do stuff with parentView
}
Correct but too-late answer: there are built-in functions in Swift 3 which provide the solution:
public func sequence<T>(first: T, next: (T) -> T?) -> UnfoldSequence<T, (T?, Bool)>
public func sequence<T, State>(state: State, next: (inout State) -> T?) -> UnfoldSequence<T, State>
We can use them in this manner:
sequence(first: view, next: {
// do something with $0...
return $0.superview
})
For instance
for view in views where view.superview != nil {
}

modify file icon like dropbox in Mac [duplicate]

I want to badge a file and folder with some color (image). How can this be achieved?
I tried with icon service, and it works for files, but it is not working with folders.
I saw this behavior working Dropbox (10.4, 10.5 and 10.6)- how can this be done?
The blog post Cocoa Tutorial: Custom Folder Icons was very close one for me, but it was not working as expected.
Is there another solution other than icon service?
The following function is the solution I found for the problem
BOOL AddBadgeToItem(NSString* path,NSData* tag)
{
FSCatalogInfo info;
FSRef par;
FSRef ref;
Boolean dir = false;
if (tag&&(FSPathMakeRef([path fileSystemRepresentation],&par,&dir)==noErr))
{
HFSUniStr255 fork = {0,{0}};
sint16 refnum = kResFileNotOpened;
FSGetResourceForkName(&fork);
if (dir)
{
NSString *name = #"Icon\r";
memset(&info,0,sizeof(info));
((FileInfo*)(&info.finderInfo))->finderFlags = kIsInvisible;
OSErr error = FSCreateResourceFile(&par,[name lengthOfBytesUsingEncoding:NSUTF16LittleEndianStringEncoding],(UniChar*)[name cStringUsingEncoding:NSUTF16LittleEndianStringEncoding],kFSCatInfoFinderXInfo,&info,fork.length, fork.unicode,&ref,NULL);
if( error == dupFNErr )
{
// file already exists; prepare to try to open it
const char *iconFileSystemPath = [[path stringByAppendingPathComponent:#"\000I\000c\000o\000n\000\r"] fileSystemRepresentation];
OSStatus status = FSPathMakeRef((const UInt8 *)iconFileSystemPath, &ref, NULL);
if (status != noErr)
{
fprintf(stderr, "error: FSPathMakeRef() returned %d for file \"%s\"\n", (int)status, iconFileSystemPath);
}
}else if ( error != noErr)
{
return NO;
}
}
else
{
BlockMoveData(&par,&ref,sizeof(FSRef));
if (FSCreateResourceFork(&ref,fork.length,fork.unicode,0)!=noErr)
{
//test
if (FSOpenResourceFile(&ref,fork.length,fork.unicode,fsRdWrPerm,&refnum)!=noErr) {
return NO;
}
if (refnum!=kResFileNotOpened) {
UpdateResFile(refnum);
CloseResFile(refnum);
if (FSGetCatalogInfo(&par,kFSCatInfoFinderXInfo,&info,NULL,NULL,NULL)==noErr) {
((ExtendedFileInfo*)(&info.extFinderInfo))->extendedFinderFlags = kExtendedFlagsAreInvalid;
FSSetCatalogInfo(&par,kFSCatInfoFinderXInfo,&info);
}
}
//Test end
return NO;
}
}
OSErr errorr = FSOpenResourceFile(&ref,fork.length,fork.unicode,fsRdWrPerm,&refnum);
if (errorr!=noErr) {
return NO;
}
if (refnum!=kResFileNotOpened) {
CustomBadgeResource* cbr;
int len = [tag length];
Handle h = NewHandle(len);
if (h) {
BlockMoveData([tag bytes],*h,len);
AddResource(h,kIconFamilyType,128,"\p");
WriteResource(h);
ReleaseResource(h);
}
h = NewHandle(sizeof(CustomBadgeResource));
if (h) {
cbr = (CustomBadgeResource*)*h;
memset(cbr,0,sizeof(CustomBadgeResource));
cbr->version = kCustomBadgeResourceVersion;
cbr->customBadgeResourceID = 128;
AddResource(h,kCustomBadgeResourceType,kCustomBadgeResourceID,"\p");
WriteResource(h);
ReleaseResource(h);
}
UpdateResFile(refnum);
CloseResFile(refnum);
if (FSGetCatalogInfo(&par,kFSCatInfoFinderXInfo,&info,NULL,NULL,NULL)==noErr) {
((ExtendedFileInfo*)(&info.extFinderInfo))->extendedFinderFlags = kExtendedFlagHasCustomBadge;
FSSetCatalogInfo(&par,kFSCatInfoFinderXInfo,&info);
}
}
}
return NO;
}
You can do this using the -setIcon:forFile:options: method on NSWorkspace, which lets you just specify an NSImage to apply to the file/folder at the path you give it.
The intended approach is to create a Finder Sync app extension.

Resources