Objective-C: Coding for various iOS versions - ios

I was wondering whether it is possible / how it is possible to code a class so it can be run on different iOS versions in Objective-C. For example the VisualEffectView is only available in iOS8 and after. Is it possible to declare a VisualEffectView if iOS >= 8 and UIView if not? If so can this be done within a header file?
I want to create an alert box to appear on top of a view controller when a save completes or error occurs. Depending on the iOS version it would be nice if a fancy blurry view is used or just a flat UIView.

In an if statement, use NSClassFromString. You'll discover immediately that UIVisualEffectView doesn't exist when it returns nil, and thus you can take one branch if it exists and another if it doesn't:
if (!NSClassFromString(#"UIVisualEffectView")) {
// ... use UIView ...
} else {
// ... use UIViewVisualEffectView ... {
}

As of iOS 5 you can the following syntax.
if ([UIVisualEffectView class]) {
// Create and use a UIVisualEffectView
}
This will occasionally bite you, NSMapTable is available in iOS versions prior to iOS 6, but was only "officially" available in iOS 6. When attempting to use it in iOS 5 there was some sporadic undocumented behavior.

As many have suggested, you can use the NSClassFromString function to find out at run time if the OS version has the class. If it doesn't (that is iOS 7 devices) and you still want live blurring, I'd recommend LiveFrost.

Related

IOS 9 native print preview doesn't render

I have built an app using the IOS 9 SDK.
I am printing using the UIPrintInteractionController and a subclass of UIPrintPageRenderer.
When the print dialog is displayed on the iPad or iPhone, UIPrintPageRenderer's drawPageAtIndex:inRect method is not called and the preview area on the dialog is just a gray box with a button that says Page 1.
This is an app that was originally written in the IOS 6 era and I am trying to update it for IOS 9, so perhaps I need to improve something.
Does anyone know what is happening here?
Thanks!
I don't understand what's happening exactly, but I had the same problem and fixed it by
1) ensuring that logic in my overrides is being executed on the UI thread
2) moving my AddPrintFormatter call to the constructor method instead of calling from a NumberOfPages override (which allowed me to delete this override property).
There seems to be some weird threading behavior now when overriding UIPrintPageRenderer (at least in Xamarin's implementation).
For example, adding this simple override causes a UIKit Consistency error for me:
public override nint NumberOfPages
{
get
{
return base.NumberOfPages;
}
}
So it seems the base method begins execution on the UI thread when there isn't an override, but the subclass's override begins execution on a worker thread.
The iOS9 SDK Release Notes document says:
"Apps that subclass UIPrintPageRenderer or UIPrintFormatter to draw content for printing must be built with the iOS 9 SDK for the preview to display. The behavior of UIPrintPageRenderer has been updated to call drawPageAtIndex:inRect: multiple times with potentially different page sizes and margins. Various methods on UIPrintPageRenderer may be called from a non-main thread, but never from multiple threads concurrently."
Turns out I was missing the numberOfPages override in the UIPrintPageRenderer derived class. The preview works after adding this override.
I'm giving carchase an upvote since the post tangentially mentioned the numberOfPages method, but my actual problem was not as subtle as suggested by carchase's post ;-).
It sounds like you need to add the print formatter.
[myRenderer addPrintFormatter:viewFormatter startingAtPageAtIndex:0];

UIActionSheet on iOS8

I have an app that connects to Chromecast - I referenced from the Chromecast Sample Code which uses UIActionSheet to display the list of Chromecast devices I can connect to.
This was working well for my app which has been running with BaseSDK=iOS6.1. Recently I tried to upgrade to BaseSDK=iOS8.1 and the UIActionSheet doesn't show anymore.
I understand that it has been deprecated in iOS8, but does that mean it wont work anymore? I thought deprecated methods typically take some time to "phase out".
So my main questions are:-
Can I still use only UIActionSheet? Is it just a matter of view hierarchies being changed which is why my ActionSheet is not showing anymore?
If the answer to question 1 is NO, what can I do to allow compatibility with both iOS7 and iOS8.
With iOS8.1 sdk UIActionSheet is deprecated, you should do runtime check to see if it is available and act accordingly using correct class:
if objc_getClass("UIAlertController") != nil {
// UIAlertController is available use it with style UIAlertControllerStyleActionSheet
} else {
// no UIAlertController use regular old action sheet
}
For iOS8 and you above, you will have to migrate to the UIAlertController for both the action sheet and alert pop up.
In all honesty, I find the new API easier to work with. There is a little more here (in Swift, but not any harder in Objective C).

What is the best way to manage support for both iOS6 and iOS7?

I always have an app requirement to support both iOS6 and iOS7. But because of iOS6 support, I can't use some feature of iOS7.
For example, I have to do UIView bounce animation, in iOS6 I have to do it with CAKeyframeAnimation or UIView animation, but iOS7 has UIKit dynamics which provide me UIDynamicAnimator class and I can do with it.
So should I always use older methods like CAKeyframeAnimation or UIView animation because it supports both iOS6 and iOS7 or write both by checking iOS version?
Ruthlessly strip things from the iOS 6 version that require ios7 features to work, particularly if they are cosmetic things like bouncing animations.
When challenged about this, tell your client they can spend 80% of their dev budget supporting an ever-shrinking pool of unconcerned users, or they can get real. API compatibility is only one headache between 6 and 7. The layout issues are far harder. Any new app being written today needs a really, really good reason to support 6, particularly with 8 just round the corner.
I believe the best way is to check iOS version and implement both variants of code.
Yes, maybe, it's quite a lot of work but in majority of cases newest features provided in new iOS releases are more flexible and simple to understand/integrate. In most cases they are also more plain in support. They are supported by Apple with more care and there is a small possibility of their deprecation.
You can use Foundation runtime variable NSFoundationVersionNumber and NSFoundationVersionNumber_iOS_6_1 macros to divide your code:
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
// write iOS 7+ code here
}
else {
// write iOS 6.1- code here
}
Note, sometimes some recent released API can be quite raw. In such cases you should take a decision individually whether you really need the migration immediately.
You can use class API to check for availability of classes.
if ([UIPrintInteractionController class]) {
// Create an instance of the class and use it.
}
else {
// The print interaction controller is not available.
}
For more information see link below Advanced App Tricks

Subclassing UIRefreshControl but still supporting iOS 5.1?

Added a UIRefreshControl to one of my tableviews here, and just used respondsToSelector on the the tableview controller to see if it has the refreshControl property before configuring and adding the UIRefreshControl using NSClassFromString(). Works perfectly and I can continue supporting iOS 5.1 (just without them getting the new control).
However… I want to override the beginRefreshing and endRefreshing methods to dynamically change the tint color of the control. And I figured subclassing UIRefreshControl would be the easiest way of doing this. But how would I do that and still support iOS 5.1?
Actually, assuming your base SDK is at least iOS 6.0, you can subclass UIRefreshControl as long as your deployment target is iOS 3.1 or later. That's because in iOS 3.1, support was added for weakly-linked classes.
With weakly-linked classes, if you send a message to a class that is not present in the running OS, it is the same as messaging nil. Thus, instead of using NSClassFromString(), you can just do this:
if ([UIRefreshControl class]) {
// Use it
}
else {
// Do something else
}
This works even when messaging your own subclass of a weakly-linked class. As Apple's "SDK Compatibility Guide" says,
If you subclass a weakly linked class and the superclass is unavailable, then the subclass also appears unavailable.
So you can just do this:
if ([MyRefreshControl class]) {
MyRefreshControl *control = [[MyRefreshControl alloc] init];
// Do something with the control
}
else {
// Do something else
}
This will work on devices running iOS 5.1 just as well as it works on devices running iOS 6. Your problem is solved.

iOS 6 iAd property and methods deprecated

Since iOS 6 release, there are some iAd properties and methods that are deprecated like :
currentContentSizeIdentifier
requiredContentSizeIdentifiers
ADBannerContentSizeIdentifierPortrait
ADBannerContentSizeIdentifierLandscape
So what's the best way to implement iAd now on both orientation ? Should we now resize the banner view frame manually ?
I have my application only support Landscape mode(should work for Portrait mode also), and have iAd showing up on the top of the application. And to make this work with ios6 I had to do this:
In Monotouch
storesAdBannerView = new ADBannerView();
storesAdBannerView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
For objc (which I dont use), I think it might be
[storesAdBannerView setAutoresizingMask:UIViewAutoresizingFlexibleWidth]
It seems that landscape ads are somewhat phased out.
See: http://www.iphonedevsdk.com/forum/iphone-sdk-development/108118-landscape-iad-banners-in-ios-6-edit-landscape-phased-out-completely.html
It need further confirmation, as I could not find any Apple document regarding that change.
I sAw 2 workaround there : http://www.touch-code-magazine.com/iad-code-is-broken-in-ios6/ Tested the first solution:
easy temporary solution – you can quickly get your app to compile again by using a cheap cheat, add explicitly #import to the files where you are accessing currentContentSizeIdentifier and the size name constants. That should get you going until you alter your app to use the new auto-layout features.
It works ok on xcode 4.5 (banner test ok).

Resources