iOS9 force LTR UINavigationController Push Direction for RTL Languages - localization

I came across this issue while I was working on an old app which had localisation for Arabic language which is written RTL. I noticed that in iOS9, my UINavigationController was showing animation right to left when pushing view controllers. My app views were not ready for it. They had been designed for left to right transition even in Arabic storyboard (because previously in older iOS, UINavigationController only supported LTR transition). Now this RTL animation required that I redesign a lot of assets so I wanted to force it to show LTR animation for all languages. After a bit of research I found the solution.

In iOS9, there is a new constant UISemanticContentAttributeForceLeftToRight for this purpose. The following code fixes the issue and forces left to right animation:
if(([[NSProcessInfo processInfo] respondsToSelector:#selector(isOperatingSystemAtLeastVersion:)]) &&
[[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9, 0, 0}]) {
[[UIView appearance] setSemanticContentAttribute:UISemanticContentAttributeForceLeftToRight];
}
We are checking iOS version first to avoid error in iOS version older than 9.
I added the above code in ViewDidLoadof the my base ViewController class (All of my ViewController classes inherit from this one).

Since it is only happening on iOS 9 in your app delegate write the following :
if #available(iOS 9.0, *) {
UIView.appearance().semanticContentAttribute = .ForceLeftToRight
} else {
// Fallback on earlier versions
};

Related

Objective-C: Coding for various iOS versions

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.

MBCalendarKit Faulty Frame

I am having a problem with the frame of my CKCalendarView's frame (Which comes with MBCalendarKit).
As you can see from the first and second pictures (and a little bit from the third too), the events are beginning to list way below the calendar. Which does not happen with framework's demo project.
The code I initialize it is as follows:
- (void)viewDidLoad {
[super viewDidLoad];
calendarView = [CKCalendarView new];
[calendarView setDisplayMode:CKCalendarViewModeDay];
[[self calendarViewArea] addSubview:calendarView];
}
The calendarViewArea is a UIView which starts right below my Navigation Bar. This is because when I add the calendarView directly to my ViewController's view its top part stays below my navigation bar.
I'm the author of MBCalendarKit.
Although the newer versions of MBCalendarKit are better at supporting the larger screened iPhone 6 and iPhone 6 Plus, there's no official iPad support.
Try the new 3.0.4 release, for example. Build it to the iPad simulator. It seems to work well, but if you "rotate" the device, it breaks the cell layout.
I'm unsure if I'll add official support in the future, but it's a known bug and
you should follow issue #31 if you're interested in this feature.

Views converted to ios7 flow under navigation/bottom bars in ios6

I have an app that uses stroryboards, a navigation controller and autolayout that runs fine under io6 and I would like to enable to run under both ios6 and ios7.
I have converted the app using xcode5 and it now runs fine under ios7, but when I run it under ios 6 the navigation bar and bottom bar are now transparent (they were not originally) and the view flows under them - hiding some of the controls. This persists even if I convert the ios7 app not to flow under these bars (by setting Extend Edges / Under Top Bars and Under Bottom Bars OFF).
I have worked around it by allowing ios7 to flow the view under the bars (what Apple wants us to do anyway) and then adding the following code to the viewDidLoad method of the fist view controller to force ios6 not to use transparent bars.
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
self.navigationController.toolbar.barStyle = UIBarStyleBlackOpaque;
}
This seems to be working, but I don't understand why I need to do this and I am concerned that I am missing something fundamental.
Edit: The other concern I have with this 'solution' is that the Preview in xcode5 under ios6 doesn't accurately reflect the layout as it runs under a simulator under ios 6 (I don;t have a real ios6 device handy to test it at the moment). The new preview feature in xcode5 appears to assume that the ios 6 will not flow the view under the top and bottom bars and thus squashes the controls together to fit into the available space.
Ali
Try this regardless of the OS version (it solved me a similar issue recently):
self.navigationController.navigationBar.translucent = NO;
self.navigationController.toolbar.translucent = NO;
I am not sure if this will help you, but there is another option to your situation: try using the __IPHONE_6_1 macro. I have an example:
if(__IPHONE_7_0) {
NSLog(#"Hello");
}
so you could just do
if(__IPHONE_OS_VERSION_MAX_ALLOWED <= __IPHONE_6_1) {
self.navigationController.navigationBar.translucent = NO;
self.navigationController.toolbar.translucent = NO;
}
hope this helps you

Use iOS 6 Style Segmented Control in iOS 7?

Is it possible to have a segmented control on an iOS 7 device show up as the iOS 6 version of the control?
We really aren't ready for an interface redesign and the new flat control doesn't jive with the rest of our UI. It would definitely be best to keep the iOS 6 style for now, if possible.
To clarify, I am compiling using the iOS 6.1 Base SDK. I am aware that this is the "obvious" answer to my question, but it does not work. Most other UI elements will show up with iOS 6 styling by doing this, but like the UIAlertView and UIActionSheet, the UISegmentedControl does not. However, unlike the UIAlertView and UIActionSheet, UISegmentedControls do not feel like a "system" item; they should be able to display in iOS 6 mode.
Edit: I thought it would be helpful if I finally included a picture with this (probably should have done this from the start). However, the answer I provided did fix the issue. Also, in retrospect, it looks like this might be the iOS 6 style after all, it's just displaying so wrong that it appears like iOS 7 style.
I manage to do a pretty good job of solving this problem by setting all the attributes manually, but it is not quite perfect.
This is what I ended up doing:
- (void)fixSegmentedControlForiOS7
{
NSInteger deviceVersion = [[UIDevice currentDevice] systemVersion].integerValue;
if(deviceVersion < 7) // If this is not an iOS 7 device, we do not need to perform these customizations.
return;
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont boldSystemFontOfSize:12], UITextAttributeFont,
[UIColor whiteColor], UITextAttributeTextColor,
nil];
[self.segmentedControl setTitleTextAttributes:attributes forState:UIControlStateNormal];
NSDictionary *highlightedAttributes = [NSDictionary dictionaryWithObject:[UIColor whiteColor] forKey:UITextAttributeTextColor];
[self.segmentedControl setTitleTextAttributes:highlightedAttributes forState:UIControlStateHighlighted];
self.segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
self.segmentedControl.tintColor = [UIColor colorWithRed:49.0 / 256.0 green:148.0 / 256.0 blue:208.0 / 256.0 alpha:1];
}
To fix images assigned with InterfaceBuilder use this code:
- (void)fixImagesOfSegmentedControlForiOS7
{
NSInteger deviceVersion = [[UIDevice currentDevice] systemVersion].integerValue;
if(deviceVersion < 7) // If this is not an iOS 7 device, we do not need to perform these customizations.
return;
for(int i=0;i<toSegmentedControl.numberOfSegments;i++)
{
UIImage* img = [toSegmentedControl imageForSegmentAtIndex:i];
UIImage* goodImg = [img imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// clone image with different rendering mode
[toSegmentedControl setImage:goodImg forSegmentAtIndex:i];
}
}
I just ran into this problem today myself. The app I'm working on updating is quite old, and still uses xib files, so I do not know if this works on storyboards or not. As others suggested above, you still need to use the iOS 6.1 SDK, but this alone is not enough. After performing the following steps, I was able to get the old UISegmentedControl appearance back:
Open the interface builder document in question
Go to the file inspector (first inspector tab; has a document icon)
Under the "Interface Builder Document" section, change "Opens in" to Xcode 4.6
I do believe this is a bug, and I would not be surprised if there isn't a workaround for UISegmentedControl instances created in code. I'm guessing this is somewhat related to the deprecation of the segmentedControlStyle property in iOS 7 (see https://developer.apple.com/library/ios/documentation/uikit/reference/UISegmentedControl_Class/DeprecationAppendix/AppendixADeprecatedAPI.html#//apple_ref/occ/instp/UISegmentedControl/segmentedControlStyle).
Hope this helps someone out there.
If you save the iPhoneOS6.1.sdk file from the previous version of XCode and add it to Xcode 5 in the same path you can then build an app against the 6.1 SDK so that when it runs on 7 everything is like 6. Linking against iOS7 SDK tells iOS to make everything look like iOS7 if possible. Essentially then you have an iOS6 app but building it with XCode 5.
If you use images on any of your UISegmentedControl segments, you'll need to add some code to set those properly on iOS 7, otherwise they'll be used as a template image and the selected segment will be a cutout of the segment's background.
UISegmentedControl under iOS 7 interprets its images as being in rendering mode UIImageRenderingModeAlwaysTemplate unless otherwise specified. I had to use -[UIImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] on each segment's image for iOS 7 to achieve the previous behavior.
Either you could:
Not update your app at all for iOS7 until you're ready to make some UI changes. Apps compiled against the iOS6 SDK will run in iOS6 Compatibility mode on iOS7 and will look exactly the same as they did in iOS6.
Apply custom background, separator, etc images to your segmented controls that mimic the look they had in iOS6.
Yes, it is possible if you recreate the control by your own. Create a fake segmented control that looks like and work like one.
In my app, I have set the Segmented control to "Bar" style. It renders in ios6 style on my ios7 iphone5 (whoa, 5,6,7). However, the text inside the segments are cut and have the three dots "..." added, no matter how wide the view is.
So the ios6 segmented control rendering in ios7 seems really buggy
Is it possible? Not really...
You could make your own custom segmented control.
Or you could use the UIAppearance proxy to customise your segmented control with images but then it's your responsibility to make it look like it was on iOS 6.

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.

Resources