How to programmatically open an NSComboBox's list? - objective-c

I've been around this for a while.. I thought this should be an easy task, but it isn't =D
What I am trying to do, is to display the combobox's list when the user clicks the combobox but not specifically in the button.
Any Idea?
Thanks in advance!

This answer fits the title of the question, but not question itself. Omer wanted to touch a text field and have the box popup.
This solution shows the popup when the user enters text.
I found this answer on cocoabuilder from Jens Alfke. I reposted his code here. Thanks Jens.
original cocoabuilder post: (http://www.cocoabuilder.com/archive/cocoa)
#interface NSComboBox (MYExpansionAPI)
#property (getter=isExpanded) BOOL expanded;
#end
#implementation NSComboBox (MYExpansionAPI)
- (BOOL) isExpanded
{
id ax = NSAccessibilityUnignoredDescendant(self);
return [[ax accessibilityAttributeValue:
NSAccessibilityExpandedAttribute] boolValue];
}
- (void) setExpanded: (BOOL)expanded
{
id ax = NSAccessibilityUnignoredDescendant(self);
[ax accessibilitySetValue: [NSNumber numberWithBool: expanded]
forAttribute: NSAccessibilityExpandedAttribute];
}
I used this code in my controlTextDidChange: method.
- (void) controlTextDidChange:(NSNotification *) aNotification {
NSTextField *textField = [aNotification object];
NSString *value = [textField stringValue];
NSComboBox *box = [self comboBox];
if (value == nil || [value length] == 0) {
if ([box isExpanded]) { [box setExpanded:NO]; }
} else {
if (![box isExpanded]) { [box setExpanded:YES]; }
}
}

Returns true if the NSComboBox's list is expanded
comboBox.cell?.isAccessibilityExpanded() ?? false
Open the NSComboBox's list
comboBox.cell?.setAccessibilityExpanded(true)
Close the NSComboBox's list
comboBox.cell?.setAccessibilityExpanded(false)
Ref. jmoody’s answer.

You can use the following code line:
[(NSComboBoxCell*)self.acomboBox.cell performSelector:#selector(popUp:)];

Put
comboBoxCell.performSelector(Selector("popUp:"))
Into
override func controlTextDidChange(obj: NSNotification) {}
is what I ended up with. Thanks #Ahmed Lotfy
Here's the full code, it works for me on OSX 10.11
override func controlTextDidChange(obj: NSNotification) {
if let comboBoxCell = self.comboBox.cell as? NSComboBoxCell {
comboBoxCell.performSelector(Selector("popUp:"))
}
}

Thanks to jmoody and Jens Alfke mentioned above. Here is a SWIFT translation of the above solution.
import Cocoa
class CComboBoxEx: NSComboBox {
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
// Drawing code here.
}
func isExpanded() -> Bool{
if let ax:AnyObject? = NSAccessibilityUnignoredDescendant(self) {
if ax!.accessibilityAttributeValue(NSAccessibilityExpandedAttribute) != nil {
return true
}
}
return false
}
func setExpanded (bExpanded:Bool) {
if let ax:AnyObject? = NSAccessibilityUnignoredDescendant(self) {
ax!.accessibilitySetValue(NSNumber(bool: bExpanded), forAttribute: NSAccessibilityExpandedAttribute)
}
}
}

NSComboBox was not designed to work this way. Because the user may want to edit the text in the control, they'll need to be able to click it without unexpectedly popping up the choices.
You would need to subclass NSComboBoxCell and change this behavior ... but then you'd have a standard-looking control that does not behave in a standard way. If you're determined to do this, take a look at the open source version of NSComboBoxCell. The interesting methods appear to be -popUpForComboBoxCell: and friends.

Based on the other answers I wrote this solution (tested with Xcode 10.2.1, Swift 5). It uses the same ideas but it's a little shorter.
// Put this extension for NSComboBox somewhere in your project
import Cocoa
public extension NSComboBox {
var isExpanded: Bool{
set {
cell?.setAccessibilityExpanded(newValue)
}
get {
return cell?.isAccessibilityExpanded() ?? false
}
}
}
// Set your corresponding NSViewController as NSComboBoxDelegate
// in the storyboard and add this piece of code
// to expand the combobox when the user types
class MyViewController: NSViewController, NSComboBoxDelegate {
func controlTextDidChange(_ notification: Notification) {
guard let comboBox = notification.object as? NSComboBox else { return }
if comboBox.isExpanded == false {
comboBox.isExpanded = true
}
}
}

Related

Autosave Expanded Items of NSOutlineView doesn't work

I am trying to use the "Autosave Expanded Items" feature. When I expand a group with its children and restart the application all children are collapsed again and I don't know why they won't stay expanded.
I'm using core data to store my source list items.
This is what I have done/set so far:
Checked "Autosave Expanded Items" in NSOutlineView (Source List)
Set a name for "Autosave"
dataSource and delegate outlets assigned to my controller
This is my implementation for outlineView:persistentObjectForItem and outlineView:itemForPersistentObject.
- (id)outlineView:(NSOutlineView *)anOutlineView itemForPersistentObject:(id)object
{
NSURL *objectURI = [[NSURL alloc] initWithString:(NSString *)object];
NSManagedObjectID *mObjectID = [_persistentStoreCoordinator managedObjectIDForURIRepresentation:objectURI];
NSManagedObject *item = [_managedObjectContext existingObjectWithID:mObjectID error:nil];
return item;
}
- (id)outlineView:(NSOutlineView *)anOutlineView persistentObjectForItem:(id)item
{
NSManagedObject *object = [item representedObject];
NSManagedObjectID *objectID = [object objectID];
return [[objectID URIRepresentation] absoluteString];
}
Any ideas? Thanks.
EDIT:
I have a clue! The problem is maybe that the tree controller has not prepared its content on time. The methods applicationDidFinishLaunching, outlineView:persistentObjectForItem etc. are being be executed before the data has loaded or rather the NSOutlineView hasn't finished initializing yet. Any ideas how to solve this?
I've had the problem that my implementation of -outlineView:itemForPersistentObject: was not called at all. It turns out that this method is called when either "autosaveExpandedItems" or "autosaveName" is set.
My solution was to set both properties in Code and NOT in InterfaceBuilder. When i set the properties after the delegate is assigned, the method gets called.
I got this to work - you need to return the corresponding tree node instead of "just" its represented object.
In itemForPersistentObject:, instead of return item; you need return [self itemForObject:item inNodes:[_treeController.arrangedObjects childNodes]];
with
- (id)itemForObject:(id)object inNodes:(NSArray *)nodes {
for (NSTreeNode *node in nodes) {
if ([node representedObject] == object)
return node;
id item = [self itemForObject:object inNodes:node.childNodes];
if (item)
return item;
}
return nil;
}
where _treeController is the NSTreeController instance that you use to populate the outline view.
Expanding on Karsten's solution:
The method -outlineView:itemForPersistentObject: gets called after doing what Karsten suggests, but ONLY if you also set the datasource before setting the delegate.
So if Karsten's answer doesn't seem to work, check where your datasource is set and adjust accordingly.
(wanted to write this as a comment but I'm not allowed due to my newbie status ...)
Swift 5 answer
Karsten is right, itemForPersistentObject must return a NSTreeNode.
Here is a Swift 5 version of the solution:
// This method should return a NSTreeNode object
func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
guard let uriAsString = object as? String,
let uri = URL(string: uriAsString) else { return nil }
if let psc = self.managedObjectContext.persistentStoreCoordinator,
let moID = psc.managedObjectID(forURIRepresentation: uri),
let group = self.managedObjectContext.object(with: moID) as? MyGroupEntity,
let nodes = self.expensesTreeController.arrangedObjects.children {
return self.findNode(for: group, in: nodes)
}
return nil
}
/// Utility method to find the corresponding NSTreeNode for a given represented object
private func findNode(for object: NSManagedObject, in nodes: [NSTreeNode]) -> NSTreeNode? {
for treeNode in nodes {
if (treeNode.representedObject as? NSManagedObject) === object {
return treeNode
}
}
return nil
}
I never got this working.
This is my current way of doing it:
First, I added an attribute "isExpanded" and saved for each node the status in the database.
Second, I expand the nodes when my treeController has prepared its content.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[treeSectionController addObserver:self
forKeyPath:#"content"
options:0
context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == treeSectionController) {
NSArray *sectionArray = [[treeSectionController arrangedObjects] childNodes];
for (NSTreeNode *node in sectionArray) {
if([[node representedObject] isExpandedValue]) {
[outlinePilesView expandItem:node];
}
}
[treeSectionController removeObserver:self forKeyPath:#"content"];
}
}
Wow! 6 years later and this is still causing headaches.
I couldn't get this working initially, even with Karsten's helpful solution re setting autoSaveName & autosaveExpandedItems in code; itemForPersistentObject was still being called before the outlineView was populated. The solution for me, whilst not very elegant, was to set a delay of .5 seconds before setting autosaveExpandedItems & autoSaveName. The half second delay in my app is not noticeable. I used Vomi's code as well. Delegate and dataSource are set in IB bindings. Here's full solution:
override func viewDidLoad() {
super.viewDidLoad()
let _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (timer) in
self.keywordsOutlineView.autosaveExpandedItems = true
self.keywordsOutlineView.autosaveName = "KeywordsOutlineView"
timer.invalidate()
}
}
func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any? {
if let node = item as? NSTreeNode {
if let object = node.representedObject as? FTKeyword {
return object.objectID.uriRepresentation().absoluteString
}
}
return nil
}
// This method should return a NSTreeNode object
func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
if outlineView == keywordsOutlineView {
guard let uriAsString = object as? String,
let uri = URL(string: uriAsString) else { return nil }
if let psc = self.managedObjectContext.persistentStoreCoordinator,
let moID = psc.managedObjectID(forURIRepresentation: uri),
let group = self.managedObjectContext.object(with: moID) as? FTKeyword,
let nodes = self.keywordsTreeController.arrangedObjects.children {
return self.findNode(for: group, in: nodes)
}
return nil
}
return nil
}
/// Utility method to find the corresponding NSTreeNode for a given represented object
private func findNode(for object: NSManagedObject, in nodes: [NSTreeNode]) -> NSTreeNode? {
for treeNode in nodes {
if (treeNode.representedObject as? NSManagedObject) === object {
return treeNode
}
}
return nil
}

Expand NSOutlineView item on single click anywhere in row

What is the most expedient way to expand NSOutlineView rows on a single click of the entire row? (not the disclosure triangle)
Is there a single setting for this? Or some magical mode setting that changes the behavior similar to source list style?
Simplest way to do both expand and collapse together seems to be target/action with single clicks. I had tried overriding the selection functions in the outlineview delegate, and that worked for expansion but not collapse. Code below for single click expand/collapse:
[self.myOutlineView setTarget:self]; // Needed if not done in IB
[self.myOutlineView setAction:#selector(outlineViewClicked:)];
[self.myOutlineView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
- (void) outlineViewClicked:(NSOutlineView*)sender
{
id clickItem = [sender itemAtRow:[self.itemsOutlineView clickedRow]];
if (clickItem)
{
BOOL optionPressed = (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask);
[sender isItemExpanded:clickItem] ?
[sender.animator collapseItem:clickItem collapseChildren:optionPressed] :
[sender.animator expandItem:clickItem expandChildren:optionPressed];
}
}
Expanding that to collapse siblings:
- (void) outlineViewClicked:(NSOutlineView*)sender
{
id clickItem = [sender itemAtRow:[self.itemsOutlineView clickedRow]];
if (!clickItem)
return;
BOOL optionPressed = (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask);
// Collapse the sibling nodes (tree controller type NSTreeNode)
for (NSTreeNode* node in ((NSTreeNode*)clickItem).parentNode.childNodes)
{
if (node != clickItem && [sender isItemExpanded:node])
[sender.animator collapseItem:node];
}
[sender isItemExpanded:clickItem] ?
[sender.animator collapseItem:clickItem collapseChildren:optionPressed] : [sender.animator expandItem:clickItem expandChildren:optionPressed];
}
The optionPressed BOOL and the subsequent use in expandChildren and collapseChildren was suggested by #ben-haller, who noted that "an option-click on the disclosure triangle expands or collapses all of its contained items." (Quoted from About Outline Views) Ben's modification extends that option-click behavior to the "anywhere in the row" methods here.
Swift version:
outlineView.target = self
outlineView.action = #selector(outlineViewClicked(_:))
#objc private func outlineViewClicked(_ sender: NSOutlineView) {
expandOrCollapseRow(outlineView: sender)
}
private func expandOrCollapseRow(outlineView: NSOutlineView) {
if let clickedItem = outlineView.item(atRow: outlineView.clickedRow) {
if outlineView.isItemExpanded(clickedItem) {
outlineView.animator().collapseItem(clickedItem)
} else {
outlineView.animator().expandItem(clickedItem, expandChildren: true)
}
}
}

Check if mapView already contains an annotation

I have a method of adding secondary nearby annotations (ann2) when I tap on another annotation (ann1). But when I deselect and re-select the exact same annotation (ann1) the ann2 re-creates it self and is getting added again. Is there a way to check if the annotation already exists on the map and if yes then do nothing otherwise add the new annotation. I have already checked this: Restrict Duplicate Annotation on MapView but it did not help me.. Any advice is appreciated. This is what I have so far:
fixedLocationsPin *pin = [[fixedLocationsPin alloc] init];
pin.title = [NSString stringWithFormat:#"%#",nearestPlace];
pin.subtitle = pinSubtitle;
pin.coordinate = CLLocationCoordinate2DMake(newObject.lat, newObject.lon);
for (fixedLocationsPin *pins in mapView.annotations) {
if (MKMapRectContainsPoint(mapView.visibleMapRect, MKMapPointForCoordinate (pins.coordinate))) {
NSLog(#"already in map");
}else{
[mapView addAnnotation:pin];
}
In this case I get the log already on map but I also get the drop animation of the annotation adding to the map. Any ideas?
Thank you in advance..
Your for loop isn't checking if the annotation is on the screen, it is checking if the coordinates of the pin are currently within the visible area. Even if it was checking if the pin object was already in the mapView.annotations it would never be true, because you've only just created pin a few lines earlier, it can't possibly be the same object as on in the mapView.annotations. It might though have the same coordinates and title, and that's what you need to check:
bool found = false;
for (fixedLocationsPin *existingPin in mapView.annotations)
{
if (([existingPin.title isEqualToString:pin.title] &&
(existingPin.coordinate.latitude == pin.coordinate.latitude)
(existingPin.coordinate.longitude == pin.coordinate.longitude))
{
NSLog(#"already in map");
found = true;
break;
}
}
if (!found)
{
[mapView addAnnotation:pin];
}
Annotations array exist in map object so you just have to check
if ( yourmap.annotations.count==0)
{
NSLog(#"no annotations");
}
NSNumber *latCord = [row valueForKey:#"latitude"];
NSNumber *longCord = [row valueForKey:#"longitude"];
NSString *title = [row valueForKey:#"name"];
CLLocationCoordinate2D coord;
coord.latitude = latCord.doubleValue;
coord.longitude = longCord.doubleValue;
MapAnnotation *annotation = [[MapAnnotation alloc]initWithCoordinate:coord withTitle:title];
if([mkMapView.annotations containsObject:annotation]==YES){
//add codes here if the map contains the annotation.
}else {
//add codes here if the annotation does not exist in the map.
}
if (sampleMapView.annotations.count > 0) {
sampleMapView.removeAnnotation(detailMapView.annotations.last!)
}
Following my comment on Craig's answer, I think the solution could look like something like this :
import MapKit
extension MKMapView {
func containsAnnotation(annotation: MKAnnotation) -> Bool {
if let existingAnnotations = self.annotations as? [MKAnnotation] {
for existingAnnotation in existingAnnotations {
if existingAnnotation.title == annotation.title
&& existingAnnotation.coordinate.latitude == annotation.coordinate.latitude
&& existingAnnotation.coordinate.longitude == annotation.coordinate.longitude {
return true
}
}
}
return false
}
}
This code allows you to check if a mapView contains a given annotation. Use this in a "for" loop on all your annotations:
for annotation in annotations {
if mapView.containsAnnotation(annotation) {
// do nothing
} else {
mapView.addAnnotation(annotation)
}
PS: this works well if you need to add new annotations to a mapView. But if you need also to remove entries, you may have to do the opposite: check that each existing annotation exists in the new array of annotations ; if not, remove it.
Or you could remove everything and add everything again (but then you will have the change animated ...)

Disabling "Define" in a UITextField

I have a UITextField that displays only numeric values (0-9, ., -). When a user selects the contents of the text field, a menu with "copy","paste" and "define" appears. Since the textfield only displays numerical values, I don't want the "define" option to appear. How do I disable the dictionary "define" option in a UITextField?
Edit:
I've solved this and posted the solution below
Swift - iOS 8
You can do it by subclassing UITextField and overriding canPerformAction:WithSender method.
class MyTextFieldWithoutDefine: UITextField {
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if action == "_define:" {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
List of all actions:
cut:
copy:
select:
selectAll:
paste:
delete:
_promptForReplace:
_transliterateChinese:
_showTextStyleOptions:
_define:
_addShortcut:
_accessibilitySpeak:
_accessibilitySpeakLanguageSelection:
_accessibilityPauseSpeaking:
makeTextWritingDirectionRightToLeft:
makeTextWritingDirectionLeftToRight:
the solution in the question comment area did not work for me (ios8), i got error with:
action == #selector(defineSelection:)
i was able to remove 'define' from edit menu by specifing the options i wanted to include in the menu:
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == #selector(copy:) ||
action == #selector(selectAll:)) {
return true;
}
return false;
}
more complete answer at: How to disable copy paste option from UITextField programmatically
(thank you serge-k)
If you use a NIB, set the "Correction" property of the UITextField to "NO"(Default value is YES).
If you use code, set the "autocorrectionType" as "UITextAutocorrectionTypeNO".
Try this:
UITextField* textField = //...;
textField.autocorrectionType = UITextAutocorrectionTypeNo;

Skip to Previous AVPlayerItem on AVQueuePlayer / Play selected Item from queue

I am playing a Tv-show that has been sliced to different chapters on my project using an AVQueuePlayer.
I also want to offer the possibility to skip to the previous/next chapter or to select a different chapter on the fly, while the AVQueuePlayer is already playing.
Skipping to next Item is no problem with the advanceToNextItem provided by AVQueuePlayer, but there is nothing alike for skipping back or playing a certainitem from the queue.
So I am not quite sure what would be the best approach here:
Using an AVPlayer instead of AVQueuePlayer, invoke replaceCurrentItemWithPlayerItem: at actionAtItemEnd to play the nextItem and just use 'replaceCurrentItemWithPlayerItem' to let the User select a certain Chapter
or
reorganise the queue or the current player by using 'insertItem:afterItem:' and 'removeAllItems'
Additional information:
I store the Path to the different videos in the order they should appear in a NSArray
The user is supposed to jump to certain chapters by pressing buttons that represent the chapter. The Buttons have tags, that are also the indexes of the corresponding videos in the array.
Hope I could make myself clear?
Anyone having any experience with this situation?
If anyone knows where to buy a good IOS VideoPlayerFramework which provides the functionality, I would also appreciate the link.
If you want your program can play previous item and play the selected item from your playeritems(NSArray),you can do this.
- (void)playAtIndex:(NSInteger)index
{
[player removeAllItems];
for (int i = index; i <playerItems.count; i ++) {
AVPlayerItem* obj = [playerItems objectAtIndex:i];
if ([player canInsertItem:obj afterItem:nil]) {
[obj seekToTime:kCMTimeZero];
[player insertItem:obj afterItem:nil];
}
}
}
edit:
playerItems is the NSMutableArray(NSArray) where you store your AVPlayerItems.
The first answer removes all items from AVQueuePlayer, and repopulates queue starting with iteration passed as index arg. This would start the newly populated queue with previous item(assuming you passed correct index) as well the rest of the items in existing playerItems array from that point forward, BUT it does not allow for multiple reverses, e.g. you are on track 10 and want to go back and replay track 9, then replay track 5, with above you cannot accomplish. But here you can...
-(IBAction) prevSongTapped: (id) sender
{
if (globalSongCount>1){ //keep running tally of items already played
[self resetAVQueue]; //removes all items from AVQueuePlayer
for (int i = 1; i < globalSongCount-1; i++){
[audioQueuePlayer advanceToNextItem];
}
globalSongCount--;
[audioQueuePlayer play];
}
}
The following code allows you to jump to any item in your. No playerhead advancing. Plain and simple. playerItemList is your NSArray with AVPlayerItem objects.
- (void)playAtIndex:(NSInteger)index
{
[audioPlayer removeAllItems];
AVPlayerItem* obj = [playerItemList objectAtIndex:index];
[obj seekToTime:kCMTimeZero];
[audioPlayer insertItem:obj afterItem:nil];
[audioPlayer play];
}
djiovann created a subclass of AVQueuePlayer that provides exactly this functionality.
You can find it on github.
I haven't tested it yet but from browsing through the code it seems to get the job done. Also the code is well documented, so it should at least serve as a good reference for a custom implementation of the functionality (I suggest using a category instead of subclassing though).
This should be the responsability of the AVQueuePlayer object and not your view controller itself, thus you should make it reusable and expose other answers implementations through an extension and use it in a similar way of advanceToNextItem() :
extension AVQueuePlayer {
func advanceToPreviousItem(for currentItem: Int, with initialItems: [AVPlayerItem]) {
self.removeAllItems()
for i in currentItem..<initialItems.count {
let obj: AVPlayerItem? = initialItems[i]
if self.canInsert(obj!, after: nil) {
obj?.seek(to: kCMTimeZero, completionHandler: nil)
self.insert(obj!, after: nil)
}
}
}
}
Usage (you only have to store an index and a reference to initial queue player items) :
self.queuePlayer.advanceToPreviousItem(for: self.currentIndex, with: self.playerItems)
One way of maintaining an index is to observe the AVPlayerItemDidPlayToEndTime notification for each of your video items :
func addDidFinishObserver() {
queuePlayer.items().forEach { item in
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: item)
}
}
func removeDidFinishObserver() {
queuePlayer.items().forEach { item in
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)
}
}
#objc func playerDidFinishPlaying(note: NSNotification) {
if queuePlayer.currentItem == queuePlayer.items().last {
print("last item finished")
} else {
print("item \(currentIndex) finished")
currentIndex += 1
}
}
This observation can also be really useful for other use cases (progress bar, current video timer reset ...).
Swift 5.2
var playerItems = [AVPlayerItem]()
func play(at itemIndex: Int) {
player.removeAllItems()
for index in itemIndex...playerItems.count {
if let item = playerItems[safe: index] {
if player.canInsert(item, after: nil) {
item.seek(to: .zero, completionHandler: nil)
player.insert(item, after: nil)
}
}
}
}
#saiday's answer works for me, here is swift version of his answer
func play(at index: Int) {
queue.removeAllItems()
for i in index..<items.count {
let obj: AVPlayerItem? = items[i]
if queue.canInsert(obj!, after: nil) {
obj?.seek(to: kCMTimeZero, completionHandler: nil)
queue.insert(obj!, after: nil)
}
}
}
If you want to play a song from any index using AVQueuePlayer.Then this below code can help to.
NSMutableArray *musicListarray (add song that you want to play in queue);
AVQueuePlayer *audioPlayer;
AVPlayerItem *item;
-(void) nextTapped
{
nowPlayingIndex = nowPlayingIndex + 1;
if (nowPlayingIndex > musicListarray.count)
{
}
else
{
[self playTrack];
}
}
-(void) playback
{
if (nowPlayingIndex < 0)
{
}
else
{
nowPlayingIndex = nowPlayingIndex - 1;
[self playTrack];
}
}
-(void) playTrack
{
#try
{
if (musicArray.count > 0)
{
item =[[AVPlayerItem alloc] initWithURL: [NSURL URLWithString:musicListarray
[nowPlayingIndex]]];
[audioPlayer replaceCurrentItemWithPlayerItem:item];
[audioPlayer play];
}
}
#catch (NSException *exception)
{
}
}
-(void) PlaySongAtIndex
{
//yore code...
nowPlayingIndex = i (stating index from queue)[audioPlayer play];
[self playTrack];
}
Here PlaySongAtIndex call when you want to play a song.