I'm developing an application that has an Keyboard extension, At some uses of the Keyboard extension (for example: when I use the switch keyboard button) the extension is going through the viewDidDisappear and at this case I know I can clean the keyboard and it's getting deallocated with all it's views and the memory is freed. At other uses (for example: when using the Notes application and pressing the Done/Back button) the extension is going through the viewWillDisappear but not through the viewDidDisappear, So in this case I'm not cleaning the Keyboard as it's not reaching my cleaning methods and therefore it's not getting deallocated.
At this point I would expect that when I return to the keyboard by going back to the note for example I would receive the previous UIInputViewController with the view I already built.
Unfortunately at this point the isViewLoaded() method returns false and my UIInputViewController is going again through the loadView method therefore building again all the views all over again, as a result the application memory grows until at some point I reach the nasty didReceiveMemoryWarning method callback.
I would like to restore somehow the view I created for my keyboard in the UIInputViewController that was initialized first. Does some one know is it possible and how could this be achieved?
I was looking into the restorationIdentifier to accomplish this task, correct me if I'm wrong but this is used with an ordinary UIViewController and can't be used with the UIInputViewController as not my AppDelegate is responsible to launch it.
Update
From further research I made, I came to a conclusion that no matter what the operation you commit:
Use Done/Back button in the Notes application.
Switch to the Messages app and use the keyboard there.
Use the “switch keyboard" button
And other scenarios, the UIInputViewController class always goes through the init method.
From my understanding of the object oriented programming this mean that whoever calls my keyboard to reaper does it using the constructor and basically creates a new instance of the keyboard. That means that I will never have an available view on first reappearance of the keyboard in any of the behaviors and I always have to build the keyboard view for the new instance and cleanup the old one. Is this right? or there is a way to presereve the view of the UIInputViewController?
Unless you're using your own custom application with the keyboard to store its previous instance, there's no way to do this. In everything besides a custom application designed by you, it is beyond your scope
Related
I have a simple app in Swift with just a few views:
A UIWebView
some TableViews
and another view with some data I download from my server
It all works well until when using the app I press the home button, leave there for a while then the iPad goes on sleep mode. A few days later I tap on the app icon and it won't start:
first tap on the icon will select the icon (goes a little darker) and deselect it a few seconds later
second tap will launch the LaunchScreen and crash a few seconds later
double tap the home button and quit the app will sometimes work
I'm just wondering if there is something I need to set on my code to handle idle/long periods of inactivity in something like viewWillDisappear or other methods?
If so I already have this in all my controllers:
override func viewWillDisappear(animated: Bool) {
timer.invalidate()
webView.removeFromSuperview()
}
Maybe I need to call super. in there too? or something else I'm missing?
You should definitely call super in your viewWillDisappear(animated:) method. See UIViewController Class Reference documentation. Also you might want to confirm why you are removing your webView from the view controller's hierarchy.
Discussion
This method is called in response to a view being removed
from a view hierarchy. This method is called before the view is
actually removed and before any animations are configured.
Subclasses can override this method and use it to commit editing
changes, resign the first responder status of the view, or perform
other relevant tasks. For example, you might use this method to revert
changes to the orientation or style of the status bar that were made
in the viewDidDisappear: method when the view was first presented. If
you override this method, you must call super at some point in your
implementation.
You probably have some null pointer exception and crash. Maybe you are calling some variable that is not set (and checked if not null).
Try disabling app funcionality (like downloading, storing and using data from server) and see where you app starts working normal again and then procede from there.
Sorry for vague answer but withouth code and maybe some log it is really hard to give specific answer.
And NO, you dont have to do anything special to handle idle/long periods of inactivity.
I have a UITableViewController displaying a list of audio files, when tapping on an audio file I push another UITableViewController showing a detailed view of the audio file. This view also features a "Download" button and a UIProgressIndicator view.
I have a Download Manager class (implemented as a Singleton) which takes care of downloading the file. Its download method takes a block which is called with updates about the download progress. I'm using it to update the UIProgressIndicator view. This works fine up to the point where you leave the detail view controller and come back at a later point in time when the file is still downloading. Of course, the progress block specified earlier is still available, but the referenced UIProgressIndicator view inside of it is not, thus it is not updated anymore.
I'm wondering, if it's a sensible idea to just re-set the block upon re-entering the detail view controller (viewDidLoad) or if the block-based approach is not really suitable for this case? Maybe it'd be better to use KVO?
Any suggestions?
Thanks!
The block approach is useful if the lifespan of the downloader is controlled by the VC. This way, when the VC is released, it releases the downloader (the downloader would not be a singleton).
If not you risk creating captured objects (the VC) that cannot be released because they are referenced in your block, and your block is referenced by an "eternal" object (the singleton).
As the VC lifespan is potentially shorter than the downloader, a better option would be to use some subscription-based observing of the downloader singleton.
This way your VC subscribes in (e.g.) viewWillAppear and unsubscribes in viewWillDisappear (important).
You can also use a global progress notification (via NSNotificationCenter), key-value observing or any other means.
The important part is that when your VC is released, nothing in the downloader points to it.
I placed my code for iAd/AdMob ads in...
-(void)viewWillAppear:(BOOL)animated{}
Ads work perfectly fine the way I have them now on all iOS devices.
When I connected my iPhone to Xcode and clicked on Product -->Analyze a message states...
The viewWillAppear:instance method in UIViewController subclass 'iPhoneSIX' is missing a [super viewWillAppear:] call
I just accidentally stumbled upon this Product-->Analyze thing. Do I really need to add [super viewWillAppear] even though everything works perfectly fine on all devices as it currently is. Will Apple reject my app if I don't pay attention to the Product-->Analyze issue navigator?
Also, what does ...
[super viewWillAppear:YES];
What does calling this do?
According to Apple: (emphasis mine)
This method is called before the receiver's view is about to be
added to a view hierarchy and before any animations are configured for
showing the view. You can override this method to perform custom tasks
associated with displaying the view. For example, you might use this
method to change the orientation or style of the status bar to
coordinate with the orientation or style of the view being presented.
If you override this method, you must call super at some point in your
implementation.
Apple doesn't gets that specific when deciding to Accept or Reject your app. It only follows the guidelines, which doesn't get that much into the weeds of your specific methods.
Calling [super viewWillAppear:YES] is a best practice, and I would recommend it. Always including super ensures that any code in the super classes get called before executing any additional code. So if you or someone else coded a super class that expected some code to be executed, you are guaranteed to still execute it, rather than just overwriting the whole method in the subclass.
Say you have a view controller of type MyViewController which is a subclass of UIViewController. Then say you have another view controller of type MyOtherViewController, which is a subclass of MyViewController. Say you're coding now some things in viewWillAppear in MyOtherViewController. If you call super first, it will call viewWillAppear in MyViewController before executing any code. If viewWillAppear in MyViewController calls super first, then it will call viewWillAppear in UIViewController before executing any code.
I'm quite certain Apple will not reject your app for failing to call super on an overridden method, primarily because there are cases where you may specifically want to avoid calling super.
That said, as Josh Gafni mentions it is definitely a best practice to do so, unless you have a very good reason for not. Also bear in mind some view controller subclasses (can't recall specifically which ones, but maybe UICollectionViewController) will only work properly if their view lifecycle methods get called appropriately, so not calling super can definitely break some classes (sometimes in subtle ways you may not realize).
Therefore my suggestion is add the call to super (generally as the first line in the method) and see if things continue to work fine. If not, spend a bit of time trying to understand what is happening differently and see if you can solve it in a different way. In general you should always (as a force of habit) provide calls to super on any view lifecycle methods you override whenever possible.
I am sure the answer to this is "no" as the documentation is very clear. But I am a little confused. A standard UIAlertView is pretty dull and I want to improve the look and it seems that other apps do it (see the example below).
Another possibility is that they are not subclassed UIAlertViews. In which case, how is this achieved?
The page UIAlertViews states
Appearance of Alert Views
You cannot customize the appearance of alert views.
So how do we get the something like the example shown here?
No, do not subclass it. From the docs:
Subclassing Notes
The UIAlertView class is intended to be used as-is
and does not support subclassing. The view hierarchy for this class is
private and must not be modified.
What you can do though is create a UIView and have it act similar to a UIAlertView. It's isn't very difficult and seems to be what they are doing in your op.
Apple's docs say that you should not subclass it. That means that there are probably internal reasons that would make it difficult to make it work right.
You might or might not be able to make a subclass of UIAlertView work, but you do so at your own risk, and future iOS releases might break you without warning. If you tried to complain Apple would laugh and tell you "I told you so".
Better to create a view that looks and acts like an alert but is your own custom view/view controller. Beware that even this is dangerous, because Apple has been making sweeping changes to the look and feel of it's UI elements recently. If you implement a view controller that looks and acts like a variant of the current alert view, Apple could change that look and/or behavior in the future and your UI app would end up looking odd and outdated. We've been bitten by this sort of thing before.
Rethink your strategy. Why do you need to use an Alert View? Besides having a modal view displayed top-most on your view stack, there's not much else that it does. Instead, subclass UIView or UIViewController to define your own interface, using images and ui elements to give it the style and input functionality as needed.
I usually subclass UIView, and attach it to the app's window's view so that I'm certain that it will be displayed on top of anything else. And you can use blocks to provide hooks into the various input elements of your new view (did user press OK, or did user enter text?)
For example:
// Instantiate your custom alert
UIView *myCustomAlert = [[UIMyCustomUIViewAlert alloc] initWithFrame:CGRectMake(...)];
// Suppose the new custom alert has a completion block for when user clicks on some button
// Or performs some action...
myCustomAlert.someEventHandler = ^{
// This block should be invoked internally by the custom alert view
// in response to some given user action.
};
// Display your custom alert view
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[window addSubview: myCustomAlert];
// Make sure that your custom alert view is top-most
[window bringSubviewToFront: myCustomAlert];
Using this method, however, will not pause the thread's execution like UIAlertView does. Using this method, everything will continue running as usual. So if you need to pause execution while your custom alert is showing, then it gets much trickier.
But otherwise, creating your own custom alerts is quite straightforward, just as you would customize any other view. You could even use Interface Builder.
Hope this helps.
No. You absolutely should not subclass a UIAlertView for any reason. Apple explicitly states this in their documentation (see "Subclassing Notes"). They even tell you that it relies on private methods - and we all know that meddling in private methods in an AppStore app is immediate grounds for rejection.
HOWEVER, there isn't a need to subclass UIAlertView on iOS 7. Apple introduced a new Custom ViewController Transitions feature in iOS 7.0 that lets you present completely custom ViewControllers with completely custom transitions. In other words, you could very easily make your own UIAlertView or even something better. There's a nice tutorial on the new feature here:
In fact, there are lots of good tutorials on this - a quick Google search on the topic turns up a huge wealth of information.
What's the purpose for making UIViewController a subclass of UIResponder? Was it done solely to pass the rotation events?
I could not find any definitive info on that in the docs.
Update
I understand that if something is made a UIResponder, this something is suppose to be included in the responder chain and process events. But I have two gnawing doubts.
As far as I know, UIViewController is put into the responder chain right after its view. Why do we need a view controller in the responder chain at all? Its view is already there, so why don't we let the view process the events that were not handled by its subviews?
OK, I'm ready to agree that we might need this. But I would like to see some real life examples, when processing events in a view controller is really needed and is the best/easiest/most appropriate way to do something.
I think your problem may simply be a failure of object oriented thinking.
Per the docs:
The responder chain is a linked series of responder objects to which
an event or action message is applied.
In UIKit the view controller sits in the responder chain between its view and the view to which the controller was pushed. So it is offered any event or action that its views don't handle.
The topmost view controller's next responder is the window, the window's next responder is the application, the application's next responder is the application delegate and the application delegate is where the buck stops.
Your question "Was it done solely to pass the rotation events?" applies the incorrect test; it implies that at some point the responder chain had otherwise been fully engineered and somebody thought 'oh, wait, what about rotation? Better chuck the view controllers into the chain'.
The original question will have been: is it helpful if events or actions can be handled by the view controller if none of the views handle them? The answer should obviously be 'yes' as — even on a touch-screen device — there will be events or actions that aren't inherently related to a view.
The most obvious examples are those related to physical inputs other than the screen. So device rotation is one. Key presses on a bluetooth keyboard are another. Remote controls are a third. The accelerometer is a fourth.
The next most obvious example is any system-generated events or actions that should go to the single most local actor rather than to everyone. In iOS that's generally requests for a more specific actor, like the most local undo manager or the identity of the input view to show if focus comes to you.
A slightly less obvious example is that exemplified by UIMenuController — a pop-up view that posts a user-input event that may need to traverse several view controllers to get to the one that should act on it. iOS 5's child view controllers increase the number of possibilities here enormously; quite often you're going to have one parent view controller with the logic to do a bunch of things and children that want to pass messages up to whomever knows how to handle them, without hard coding the hierarchy.
So, no, view controllers weren't added to to the responder chain just to handle rotation events. They were added because logically they belong to be there based on the initial definition of the responder chain.
This is going to sound like a glib answer, but it really isn't. UIViewController is a subclass of UIResponder so that is can respond to user actions (e.g. touches, motion, etc.).
If a view does not respond to an event it is passed up the responder chain giving higher level objects a chance to handle it. Hence, view controllers and the application class are all subclasses of UIResponder
You can find more detailed information about the responder chain in Cocoa Application Competencies for iOS: Responder Object on Apple's developer site.
UIViewController is in the responder chain to allow for it to process any event. There are more then just the events you think of (touches) that pass through this chain. Motion events get passed through the chain, touch events that a specific view doesn't handle, you can also force things through the responder chain using [UIApplication sendEvent:...] with a nil target.
The other thing you may notice is UIApplication is also a subclass of UIResponder. All events that aren't handled will end up there.
In MVC conception handling of event occurred in view should perform controller.
There is a feature, to set "nil" to "target" parameter of -[UIControl addTarget:action:forControlEvents:] in with case the responder chain is searched for an object willing to respond to the action.
That is, because of search option, UIViewController is subclass of UIResponder.
So, you can send your custom control events in same manner to get benefits of "search" option.