Below is the hierarchy in question.
The "ViewController - MyConnection" in the top left is my main ViewController. I implement a NavigationController system. From the main VC a user can go to the "Choose Server VC" or the "Choose Test Type" VC using Push. Having done that a user can then press back and return to the main VC, no problem.
A user can also Push to the "Preferences" VC from the main VC. From the prefences VC a user can also go to the "Choose Server VC" or the "Choose Test Type" VC, again without issue.
However, when going back to the preferences VC AND THEN back to the Main VC the app crashes!
Using the debugger the viewWillAppear gets called in the Main VC but that's where if fails.
I'm sure it's something simple I'm missing. All the segues have unique names.
The debugger doesn't offer a great amount of detail about what is causing the crash. Any way to get more detail out of the debugger?
EDITS:
viewWillAppears in ViewController-MyConnection:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView reloadData];
}
In Preferences VC:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView reloadData];
}
There are no ViewWillAppear methods in the other two VCs
Related
I have single view controller with a UIButton containing the following code:
import UIKit
class ViewController: UIViewController {
required init?(coder: NSCoder) {
super.init(coder: coder)
}
#IBSegueAction
func makeAnotherController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> ViewController? {
return ViewController(coder: coder)
}
}
According to Apple's docs, this is the correct signature for a #IBSegueAction, and it compiles OK, but I'm unable to connect the button to the #IBSegueAction by dragging from the storyboard scene to the assistant editor.
I can connect without issue to other outlets or actions.
Any thoughts? Xcode 11 bug or am I doing something wrong?
Update
Briefly, Apple's docs say…
Create a connection from a segue to an #IBSegueAction method on its source view controller. On new OS versions that support Segue Actions, that method will be called … An IBSegueAction method takes up to three parameters: a coder, the sender, and the segue’s identifier. The first parameter is required, and the other parameters can be omitted from your method’s signature if desired
I'm unable to complete the first step (create a connection)
This was my misunderstanding of Apple's docs. When they say…
Create a connection from a segue to an #IBSegueAction method on its source view controller.
they literally mean drag from the line that represents the segue to the view controller.
I have a music app that uses the MPMusicPlayerController.
I originally wrote it using the systemMusicPlayer option under iOS 9.
I had some trouble with the player not shutting down correctly under certain circumstances so I switched to the appplicationMusicPlayer
(see Quitting app causes error "Message from debugger: Terminated due to signal 9")
However, as an application player, I can't get a lot of the benefits like control center handling, bluetooth data display, etc.
So, I switched back to the systemMusicPlayer.
I have also changed to Xcode 9.2 and a compile target of iOS 10.3.
Now when I run my app, it can take several seconds for it to respond to controls like play/pause or next/previous. My whole UI is painfully unresponsive.
I tried switching back to applicationMusicPlayer, recompiled, and sure enough - the UI is at normal speed.
So now I'm in a crappy position - with systemMusicPlayer, the app is barely usable, but with applicationMusicPlayer I lose a ton of capabilities.
This seems directly related to either iOS 11.2.2 on my iPhone, or something to do with targeting iOS 10.3+
Does anyone have any information about what is going on and how to fix it
EDIT:
I created a very basic player and it seems to work fine in either mode, so now I'm puzzled - I'll be testing other MP commands to see what the
issue is but since even my UI slows down I'm not sure.
EDIT 2:
I believe I've found the culprit to be NotificationCenter, and also getting States from the MPMusicPlayerController. I've updated my sample code below which shows the problem. Once running, clicking the 'next' button will be slow sometimes, but clicking 'previous' can cause delays of up to two seconds!!
Here is the basic code if you want to create a simple player.
Be sure to add three buttons in the storyboard and connect them accordingly.
//
// ViewController.swift
// junkplayer
//
import UIKit
import MediaPlayer
let notificationCenter = NotificationCenter.default
let myMP:MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer
//let myMP:MPMusicPlayerController = MPMusicPlayerController.applicationMusicPlayer()
class ViewController: UIViewController {
#IBOutlet weak var xxx: UIButton!
#IBOutlet weak var nextbut: UIButton!
#IBOutlet weak var prevbut: UIButton!
var qrySongs = MPMediaQuery()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
myMP.repeatMode = MPMusicRepeatMode.none
myMP.shuffleMode = MPMusicShuffleMode.off
myMP.prepareToPlay()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
stopMPNotifications()
startMPNotifications()
}
#IBAction func nextbut(_ sender: Any) {
myMP.skipToNextItem()
}
#IBAction func prevbut(_ sender: Any) {
myMP.skipToPreviousItem()
}
#IBAction func playbut(_ sender: UIButton) {
qrySongs = MPMediaQuery.songs()
myMP.setQueue(with: qrySongs)
myMP.play()
}
func startMPNotifications(){
notificationCenter.addObserver(self, selector: #selector(showNowPlaying), name: .MPMusicPlayerControllerNowPlayingItemDidChange, object: myMP)
notificationCenter.addObserver(self, selector: #selector(handlePlayState), name: .MPMusicPlayerControllerPlaybackStateDidChange, object: myMP)
myMP.beginGeneratingPlaybackNotifications()
}
func stopMPNotifications(){
notificationCenter.removeObserver(self, name: .MPMusicPlayerControllerPlaybackStateDidChange, object: myMP)
notificationCenter.removeObserver(self, name: .MPMusicPlayerControllerNowPlayingItemDidChange, object: myMP)
myMP.endGeneratingPlaybackNotifications()
}
#objc func handlePlayState(){
if myMP.playbackState == .playing {
print("handlePlayState playback state = playing")
}else{
print("handlePlayState playback state NOT playing")
}
print("handlePlayState going to shownowplaying")
showNowPlaying()
}
#objc func showNowPlaying(){
if myMP.nowPlayingItem != nil {
print("shownowplaying nowplaying not null")
}
}
}
The app seems to lock up once you start playing, but if you swipe up to show the Control Centre then dismiss it, the app starts working fine immediately.
I am having the same problem. I think it’s something wrong with the API. It's especially slow for large queries. But you can put a predicate on the quart, that allows no cloud music to go on it.
For anyone browsing this thread in the future, this was a known bug within all versions of iOS 11.2. It affected anyone using the systemMusicPlayer.
Apple sure does love to discourage us third party music app developers, eh? ;)
Regardless of my conspiracy theories (and I'm sure you have yours too), this bug was fixed in iOS 11.3.
We warn users on iOS 11.2 about the bug and recommend that they upgrade to iOS 11.3 for a (more or less) lag-free experience.
I ran into this problem today - working code just stopped running correctly, but I found a (partial) workaround:
\\Do anything that updates (like changing the song title in the UI) in the selector called by this notification
NotificationCenter.default.addObserver(self, selector: #selector(remoteMusicChange), name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange, object: nil)
The issue is that calls to say go to the next track for systemMusicPlayer are no longer (always) happening immediately. If you wait for the notification, at least you can be sure systemMusicPlayer has updated.
The problem with this is sometimes it can take a perceptibly long time for that notification to fire (and sometimes it's instant).
EDIT: https://forums.developer.apple.com/thread/96287 I am guessing this is related to these issues
EDIT2: Tested another related issue quickly in iOS12 and the problem no longer existed (changing playback speed) and the pauses when changing songs went away.
After I cancel the image picker, the viewWillAppear gets called but the viewDidAppear does not get called. And subsequent loading of that view by going up and back down the stack does not help it.
Only if I switch to another Tab and then switch back, do I get the viewdidappear calling.
I use this code to cancel the image picker (iOS 6)
-(void) imagePickerControllerDidCancel:(UIImagePickerController *)picker{
[self dismissViewControllerAnimated:NO completion:^{
self.imgScrollView.hidden=NO;
}];
}
You are trying to dismiss the viewController which presented the imagePickerController, try using picker instead of self something like this:
-(void) imagePickerControllerDidCancel:(UIImagePickerController*)picker
{
[picker dismissViewControllerAnimated:NO completion:
^{
self.imgScrollView.hidden=NO;
}];
}
our application shows interstitial iAds after each played level.
At the moment a level finishes, we create the ad:
_interstitial = [[ADInterstitialAd alloc] init];
_interstitial.delegate = self;
and the delegate implements the following callback methods:
- (void)interstitialAdDidUnload:(ADInterstitialAd *)interstitialAd
{
[self finish];
}
- (void)interstitialAd:(ADInterstitialAd *)interstitialAd didFailWithError:(NSError *)error
{
[self finish];
}
- (void)interstitialAdDidLoad:(ADInterstitialAd *)interstitialAd
{
[_interstitial presentFromViewController:self];
}
- (void)interstitialAdActionDidFinish:(ADInterstitialAd *)interstitialAd
{
[self finish];
}
whereas the finish method cleans up things and forwards the user to the main menu.
from the moment the ad object is created until the finish method is called, a spinning wheel is displayed on the screen.
Now the thing is that if no ad is loaded (for example if no internet connection is available) none of the callback functions ever gets called, so we also add a NSTimer when creating the Ad and check back after 10 seconds, like this:
_interstitial = [[ADInterstitialAd alloc] init];
_interstitial.delegate = self;
_timer = [NSTimer scheduledTimerWithTimeInterval:10
target:self
selector:#selector(checkForAds:)
userInfo:nil
repeats:false];
-(void)checkForAds:(NSTimer *)timer
{
[_timer invalidate];
if(!_interstitial.loaded)
[self finish];
}
but this seems very unclean - and also it leads to a 10 seconds spinning wheel delay after each level if no internet connection is available for example (or if the connection is very bad).
so, my questions:
what's the proper way to deal with this? I feel a timer here is not the right thing.
I'm currently considering to create the Ad object before the level starts, and then after the level finishes immediately check the _interstitial.loaded property and show the ad in this case or skip over it otherwise, without using a timer. The problem is that a level session can take quite long (between 1 and 60 minutes I'd say) and I read that ads can expire, however there is no indication in the documentation how long it usually takes until ads expire and what happens in this case. Will the _interstitial.loaded property return false if the ad expired in the meantime in the background? is some callback function called when the ad expires? Is it a viable approach to create the ad object before a level starts (that is, 1-60 minutes before the ad actually gets displayed) and later just check if _interstitial.loaded is true and in this case display the ad?
I am really new to Xcode iOS development.
I am implementing search with "auto-complete".(Asynchronously sending request to the server for each letter user has inserted and by this connection, I am fetching requested data from the server).
this part works pretty fine :)
My issue is to represent the search result in the table view , I googled a lot
but I couldn't find any good example that would help me.
when the tableView draws for first time everything is fine , but when I fetch another request:
(once the second key is pressed the request returns new data and i want to draw it in the table,without the previous data).
My application crashes with the exception "incorrect selector sent to instance 0x..."
I am storing the fetched data in NSArray *FetchedData;
afterwards I changed:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection: (NSInteger)section
{
return [FetchedData count];
}
And in other function (I think I have to change something here but I don't know what).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
cell.cellName.text = (NSString*)[FetchedData objectAtIndex:indexPath.row];
return cell;
}
After I getting the FetchedData I call the function:
[self.tableView reloadData];
but in the function above I am crashing after I trying to represent the next fetched data
that is different from the previous (it can even be empty)
I think I might need to delete all the previous cells,
but I don't know how to do so .(I tried some codes from google and from this site ,but I lack basic knowledge,I keep getting all the possible exceptions).
Please can some one give me a decent example with basic explanations ?I would very much appreciate it
Thanks.
You need to register your tableViewCell class somewhere (viewDidLoad),
[self.tableView registerClass:[CustomTableViewCellClass class] forCellReuseIdentifier:#"CellResuseIdentifier"];
and then in,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
CustomTableViewCellClass *cell = [self.tableView dequeueReusableCellWithIdentifier:#"CellReuseIdentifier"];
cell.cellName.text = (NSString*)[FetchedData objectAtIndex:indexPath.row];
return cell;
}
After you have fetched your data, call
[self.tableView reloadData];