Array Mutation whilst being Enumerated - objective-c

so my issue is happening only when I have two enemies on a team in my game. If it's a one versus one.. I do not get the issue.
Please look at my code and see if you can gather as to why I'm getting this fatal error.
Removes Target from players target array
-(void)removeTarget:(PlayerClass *)target withSender:(PlayerClass *)sender {
if ([sender.targets containsObject:target]) {
[sender.targets removeObject:target];
}
}
Adds Target to players target array
-(void)addTarget:(PlayerClass *)target withSender:(PlayerClass *)sender {
//check if target already exists
if ([sender.targets count] > 0) {
for (PlayerClass *players in sender.targets) {
if ([players.name isEqualToString:target.name]) {
//Checked if exists, if target exists in list then move on.
goto outer;
}
}
}
[sender.targets addObject:target];
outer:;
}
In the Update to determine whether they're a target or not
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
for (PlayerClass *player in _players) {
....
if (player.health > 0) { //If player is alive!
....
//Checks if the player has targets, if so & visible - Engage.
if ([player.targets count] > 0) {
for (PlayerClass *enemy in player.targets) {
if (![player.team isEqualToString:enemy.team]) {
if (enemy.health > 0) {
if ([self lineOfSightBetweenPlayers:player andPlayer:enemy]) {
[self attackWithPlayer:player againstPlayer:enemy];
break;
} else {
[player removeTarget:enemy withSender:player];
}
} else {
[player removeTarget:enemy withSender:player];
}
} else {
[player removeTarget:enemy withSender:player];
}
}
}
}
Now from debugging I've gathered that the players don't add their team mates as targets. However, the player will gather more than one target if they can see more than one target on the opposing team. However, the issue I'm guessing lies in my technique to removing a target from the array? Can anyone check over and make sure I'm not delivering a school boy error here?
Thanks in advance.

Very simple fix. Wasn't thinking outside the box.. which tends to happen when code starts getting very large!
//Checks if the player has targets, if so & visible - Engage.
if ([player.targets count] > 0) {
for (PlayerClass *enemy in player.targets) {
if (![player.team isEqualToString:enemy.team]) {
if (enemy.health > 0) {
if ([self lineOfSightBetweenPlayers:player andPlayer:enemy]) {
[self attackWithPlayer:player againstPlayer:enemy];
break;
} else {
[player removeTarget:enemy withSender:player];
break;
}
} else {
[player removeTarget:enemy withSender:player];
break;
}
} else {
[player removeTarget:enemy withSender:player];
break;
}
}
}
Fixed my issue, I wasn't breaking out. Thus enumerating after removal.

Related

didRangeBeaconRegion method gives repeated beacon device in objective c

i'm new to ibeacon apps,in my app i need to find the iBeacon device.here i'm having three beacon device.i find the device by the didRangeBeaconRegion method.i showed these detected beacon in a table view.
My problem is this method calls every seconds so my value having repeated beacons and many numbers rows.
how to show the three beacons details only in the table view.
i searched in the android app it shows only that three beacons only.
below is my code,
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region{
if(beacons.count>0)
{
CLBeacon *beacon=[[CLBeacon alloc]init];
beacon=[beacons firstObject];
[uuidArr addObject:beacon.proximityUUID.UUIDString];
[minArr addObject:beacon.minor];
[majArr addObject:beacon.major];
[rssiArr addObject:[NSString stringWithFormat:#"%ld",(long)beacon.rssi]];
[distanceArr addObject:[NSString stringWithFormat:#"%f m",beacon.accuracy]];
NSString *proxStr;
if (beacon.proximity == CLProximityUnknown)
{
proxStr = #"Unknown";
}
else if (beacon.proximity == CLProximityImmediate)
{
proxStr= #"Immediate";
}
else if (beacon.proximity == CLProximityNear) {
proxStr = #"Near";
}
else if (beacon.proximity == CLProximityFar) {
proxStr = #"Far";
}
[proxArr addObject:proxStr];
}
else
{
}
[self.tblView reloadData];
}
You are loading the first object only to the beacon so you get the first beacon Value in the Beacons Array i.e if you have 4 beacon device means you get the first beacon only & didRangeBeacons method calls every second in that your adding the first object to your array like uuidArr,minArr like that,
Now Solution is:
1.remove all the Value in array above the if Condition.
2.Don't take the first object only.
3.put one for loop to add the list of devices.
code:
[uuidArr removeAllObjects];
[minArr removeAllObjects];
[majArr removeAllObjects];
[rssiArr removeAllObjects];
[distanceArr removeAllObjects];
[proxArr removeAllObjects];
if(beacons.count>0)
{
CLBeacon *beacon=[[CLBeacon alloc]init];
for(int i=0;i<beacons.count;i++)
{
beacon=beacons[i];
[uuidArr addObject:beacon.proximityUUID.UUIDString];
[minArr addObject:beacon.minor];
[majArr addObject:beacon.major];
[rssiArr addObject:[NSString stringWithFormat:#"%ld",(long)beacon.rssi]];
[distanceArr addObject:[NSString stringWithFormat:#"%f m",beacon.accuracy]];
NSString *proxStr;
if (beacon.proximity == CLProximityUnknown)
{
proxStr = #"Unknown";
}
else if (beacon.proximity == CLProximityImmediate)
{
proxStr= #"Immediate";
}
else if (beacon.proximity == CLProximityNear) {
proxStr = #"Near";
}
else if (beacon.proximity == CLProximityFar) {
proxStr = #"Far";
}
[proxArr addObject:proxStr];
}
}
now you get the number of devices you having.i hope this is help to you

Dynamic NSOutlineView data source

So I have implemented a PXSourceList data source that pretty much is a duplicate of Apple's example of a NSOutlineView data source.
This is how it goes...
- (NSUInteger)sourceList:(PXSourceList*)sourceList numberOfChildrenOfItem:(id)item; {
if (item == nil) {
// item is nil so it's part of the very top hierarchy.
// return how many sections we need.
return 2;
}
else {
if ([item class] == [TSFileSystemItem class] ) {
return [item numberOfChildren];
// if item isn't nil and it's a TSFileSystemItem, then return it's children.
}
if ([item class] == [TSWorkspaceItem class]) {
return 2; // i don't know, random items.
}
else {
NSLog(#"this is a special object.");
}
}
}
- (BOOL)sourceList:(PXSourceList *)aSourceList isItemExpandable:(id)item {
if (item == nil) {
return YES;
}
else {
// if the number of children of the item is -1
BOOL gibberhook = ([item numberOfChildren] != -1);
return gibberhook;
}
}
-(id)sourceList:(PXSourceList *)aSourceList child:(NSUInteger)index ofItem:(id)item {
if (item == nil) {
return [TSFileSystemItem rootItem];
}
else {
return [(TSFileSystemItem *)item childAtIndex:index];
}
}
- (id)sourceList:(PXSourceList *)aSourceList objectValueForItem:(id)item {
if (item == nil) {
return #"/";
} else {
if (item == [TSFileSystemItem rootItem]) {
return PROJECT_FILES;
}
else {
return [item relativePath];
}
}
}
The mysterious TSFileSystemItem is from here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/OutlineView/Articles/UsingOutlineDataSource.html.
All of this is OK, except I want to divide my source list to have multiple sections (root cells). One displaying a file hierarchy (check) and the other...
Well the other is going to contain a NSMutableArray that I add items to from the other section. Sounds complicated? Better explanation. Click an item from section with file hierarchy, and it is added to the other section.
I have tried to solve this mess with the help of Apple's docs, but I still can't find a simple, efficient, stable way of making 2 sections with the functions I mentioned above. If only it was as easy as configuring a data source for UITableView...
Can anybody kindly help me out?
When the childrenForItem delegate method is called and item in nil, this requests the root of the tree. If you return and array then the tree will have a root node for each element in that array.
- (NSArray *)childrenForItem:(id)item {
if (item == nil) {
return [self.rootTreeNode childNodes];
} else {
return [item childNodes];
}
}

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.

Issue in comparing NSString inside NSMutable Array

i write a method that loops through an array of buttons and checks if a string is equal to any of the buttons titles inside the array, but it doesn't work although the string passed to that method equals some strings inside the array, here's my code:
-(void)checkDuplicatesInSection:(NSString*)btnLabel
{
for (UIButton* btn in self.test) {
if([btnLabel isEqualToString:btn.titleLabel.text])
{
NSLog(#"Inside check Dublicates--->Title Existed");
} else {
NSLog(#"Inside check Dublicates--->Title Not Existed");
}
}
}
// self.test---> it's an array contains group of buttons
// btnLabel----> it's a string passed to that method
What I don't understand is why when I run the program, I get both Inside check Dublicates--->Title Existed and "Inside check Dublicates--->Title Not Existed.
The code:
if([btnLabel isEqualToString:btn.titleLabel.text])
{
NSLog(#"Inside check Dublicates--->Title Existed");
} else {
NSLog(#"Inside check Dublicates--->Title Not Existed");
}
will be executed multiple times because it is in a for loop. That's why you get both logs printed when you run your code.
To test whether self.test contains the string btn.titleLabel.text you should modify your code into:
-(void)checkDuplicatesInSection:(NSString*)btnLabel
{
BOOL found = NO;
for (UIButton* btn in self.test) {
if([btnLabel isEqualToString:btn.titleLabel.text]) {
found = YES;
break;
}
}
if (found) {
NSLog(#"Inside check Dublicates--->Title Existed");
} else {
NSLog(#"Inside check Dublicates--->Title Not Existed");
}
}
Or you can simply use the method -containsObject: *:
-(void)checkDuplicatesInSection:(NSString*)btnLabel
{
BOOL found = [self.test containsObject:btn.titleLabel.text];
if (found) {
NSLog(#"Inside check Dublicates--->Title Existed");
} else {
NSLog(#"Inside check Dublicates--->Title Not Existed");
}
}
* This will work even if btn.titleLabel.text is an NSString.

Problem with recursive objective-c void-method

thats my first question here and i hope someone can help me.
I´m new at the iPhone programming and want to try an easy app...
It´s an SudokuSolver which is working with an recursive Method. In JAVA this code is making no problems, but in Objective-C the code isn´t stopping when Sudoku is solved. It´s still trying to solve the Sudoku and stops later.
Anyone an idea?!
Here´s the code.
- (SudokuSolver *) initWithField: (int[9][9]) field {
self = [super init];
if(self) {
for (int i=0; i<9; i++) {
for (int j=0; j<9; j++) {
sudokuField[i][j] = field[i][j];
if (field[i][j]) {
sudokuFieldStatic[i][j] = 1;
} else {
sudokuFieldStatic[i][j] = 0;
}
}
}
}
return self;
}
- (void) solve {
[self solveFieldAtRow:0 andCol:0];
}
- (void) solveFieldAtRow: (int) row andCol: (int) col {
if (row > 8) {
return;
} else {
while (sudokuField[row][col] != 0) {
if (++col > 8) {
col = 0;
row++;
if (row > 8) {
return;
}
}
}
for (int num=1; num<10; num++) {
if ([self checkRow:row forNumber:num] && [self checkCol:col forNumber:num] && [self checkFieldAtRow:row andCol:col forNumber:num]) {
sudokuField[row][col] = num;
[self showFieldInConsole:0];
if (col < 8) {
[self solveFieldAtRow:row andCol:col+1];
} else {
[self solveFieldAtRow:row+1 andCol:0];
}
}
}
sudokuField[row][col] = 0;
}
}
The code isn't stopping when the puzzle is solved because you don't check whether the puzzle is solved after the recursive call. So even if the recursive call found a solution, the code just continues on even after finding a solution until it has tried every possibility.
Since you say you have Java code that works, I suggest you compare the logic of the Java program versus this code. You'll probably find the Java code does include such a test.
Edit From your comment above, I see that you won't find such a test in your Java code, because there you are abusing exceptions to "return" from the recursion when a solution is found. The proper way is to have each recursive call return a true value if it found a solution and false if it didn't. And then each step should check if its child call succeeded, and itself return success if so. Something like this:
- (BOOL) solveFieldAtRow: (int) row andCol: (int) col {
if (row > 8) {
// reached the end, so it must have succeeded
return YES;
} else {
while (sudokuField[row][col] != 0) {
if (++col > 8) {
col = 0;
row++;
if (row > 8) {
// reached the end, so it must have succeeded
return YES;
}
}
}
for (int num=1; num<10; num++) {
if ([self checkRow:row forNumber:num] && [self checkCol:col forNumber:num] && [self checkFieldAtRow:row andCol:col forNumber:num]) {
sudokuField[row][col] = num;
[self showFieldInConsole:0];
BOOL result;
if (col < 8) {
result = [self solveFieldAtRow:row andCol:col+1];
} else {
result = [self solveFieldAtRow:row+1 andCol:0];
}
if (result) {
// Our child call succeeded, so we pass that back up
// the stack.
return YES;
}
}
}
sudokuField[row][col] = 0;
// If we get here, we could not find a solution. Return failure
// back up the stack.
return NO;
}
}