Disable Autolayout Localization Behavior (RTL - Right To Left Behavior ) - ios

My application is localized in both English and Arabic.
Unfortunately, sometimes the autolayout behavior for localization is not required. By that, I mean reversing the leading and trailing spaces. I want to override this behavior. Is there any way to do that?

To make leading act always as left (and trailing always like right), i.e. make it language independent, you can remove checkmark on "Respect language direction" on all constrains.
You can find this checkmark in constrain settings in the Attributes inspector under "First Item" button.

The attributes leading and trailing are the same as left and right for left-to-right languages such as English, but in a right-to-left environment such as Hebrew or Arabic, leading and trailing are the same as right and left. When you create constraints, leading and trailing are the default values. You should usually use leading and trailing to make sure your interface is laid out appropriately in all languages, unless you’re making constraints that should remain the same regardless of language.
So, for your special cases, don't use leading and trailing, instead, explicitly use left and right when you create your constraints.

as in #Pavel answer, you should turn off 'Respect language direction' property. if you have lots of constraints, you can open xib or storyboard file in XML view and replace all 'leading' values with 'left' and all 'trailing' values with 'right' and you're done.

Try this
Create class for managing constraint in all views
#implementation RTLController
#pragma mark - Public
- (void)disableRTLForView:(UIView *)view
{
[self updateSubviewForParentViewIfPossible:view];
}
#pragma mark - Private
- (void)updateConstraintForView:(UIView *)view
{
NSMutableArray *constraintsToRemove = [[NSMutableArray alloc] init];
NSMutableArray *constraintsToAdd = [[NSMutableArray alloc] init];
for (NSLayoutConstraint *constraint in view.constraints) {
NSLayoutAttribute firstAttribute = constraint.firstAttribute;
NSLayoutAttribute secondAttribute = constraint.secondAttribute;
if (constraint.firstAttribute == NSLayoutAttributeLeading) {
firstAttribute = NSLayoutAttributeLeft;
} else if (constraint.firstAttribute == NSLayoutAttributeTrailing) {
firstAttribute = NSLayoutAttributeRight;
}
if (constraint.secondAttribute == NSLayoutAttributeLeading) {
secondAttribute = NSLayoutAttributeLeft;
} else if (constraint.secondAttribute == NSLayoutAttributeTrailing) {
secondAttribute = NSLayoutAttributeRight;
}
NSLayoutConstraint *updatedConstraint = [self constraintWithFirstAttribute:firstAttribute secondAtribute:secondAttribute fromConstraint:constraint];
[constraintsToRemove addObject:constraint];
[constraintsToAdd addObject:updatedConstraint];
}
for (NSLayoutConstraint *constraint in constraintsToRemove) {
[view removeConstraint:constraint];
}
for (NSLayoutConstraint *constraint in constraintsToAdd) {
[view addConstraint:constraint];
}
}
- (void)updateSubviewForParentViewIfPossible:(UIView *)mainView
{
NSArray *subViews = mainView.subviews;
[self updateConstraintForView:mainView];
if (subViews.count) {
for (UIView * subView in subViews) {
[self updateConstraintForView:subView];
[self updateSubviewForParentViewIfPossible:subView];
}
}
}
- (NSLayoutConstraint *)constraintWithFirstAttribute:(NSLayoutAttribute)firstAttribute secondAtribute:(NSLayoutAttribute)secondAttribute fromConstraint:(NSLayoutConstraint *)originalConstraint
{
NSLayoutConstraint *updatedConstraint =
[NSLayoutConstraint constraintWithItem:originalConstraint.firstItem
attribute:firstAttribute
relatedBy:originalConstraint.relation
toItem:originalConstraint.secondItem
attribute:secondAttribute
multiplier:originalConstraint.multiplier
constant:originalConstraint.constant];
return updatedConstraint;
}
#end
Add this code in to controller that should disable RTL behavior
RTLController *rtl = [[RTLController alloc] init];
[rtl disableRTLForView:self.view];

another easy way
Select any view from the storyboard
from the attributes inspector on the right select set "Semantic" to "Force Left-to-Right"

Related

UI handling according to Multiple language With out Autolayouts

I have two languages in my application which are English and Arabic.
How do I change the UI according to the selected Language With out Autolayouts?
For example:
If the user selects English we have to display text left to right.
If the user selects Arabic we have to display the text right to left.
The display rules includes Tableviews, Labels, Buttons, Images, Collection views, Views, Textfields, etc...
There are lot of scenarios you need to consider while working for
app which is changing the UI within app for Arabic. I used following
code. This will work perfectly. You need to handle it properly for
scrollView and UITableView. Need to be sure that UI is proper while pushing and
poping from view controllers.
- (void)reloadUI {
if (languageIsChanged == YES) {
[self reloadRTLView:self.view];
}
}
- (void)reloadRTLView:(UIView *)view {
[self changeViewRTL:view];
for (UIView *tempView in view.subviews) {
[self reloadRTLView:tempView];
}
}
- (void)changeViewRTL:(UIView*)tempView {
for (NSLayoutConstraint *constrain in tempView.constraints) {
NSLayoutAttribute firstAttribute = constrain.firstAttribute;
NSLayoutAttribute secondAttribute = constrain.secondAttribute;
if ((firstAttribute == NSLayoutAttributeLeading || firstAttribute == NSLayoutAttributeTrailing) && (secondAttribute == NSLayoutAttributeLeading || secondAttribute == NSLayoutAttributeTrailing)) {
if (firstAttribute == NSLayoutAttributeLeading) {
firstAttribute = NSLayoutAttributeTrailing;
} else if (firstAttribute == NSLayoutAttributeTrailing) {
firstAttribute = NSLayoutAttributeLeading;
}
if (secondAttribute == NSLayoutAttributeLeading) {
secondAttribute = NSLayoutAttributeTrailing;
} else if (secondAttribute == NSLayoutAttributeTrailing) {
secondAttribute = NSLayoutAttributeLeading;
}
constrain.constant *= -1;
NSLayoutConstraint *constrainNew = [NSLayoutConstraint constraintWithItem:constrain.firstItem attribute:firstAttribute relatedBy:constrain.relation toItem:constrain.secondItem attribute:secondAttribute multiplier:constrain.multiplier constant:constrain.constant];
[tempView removeConstraint:constrain];
[tempView addConstraint:constrainNew];
}
}
}
Up vote if this help you.
In swift we can use UIView.appearance().semanticContentAttribute = .forceRightToLeft for Right to left(Arabic).

UIView subview autolayout issue

I have UITableView Cell in which 4 UIViews are arranged in a vertical order like this,
In each UIView, I have two Labels like this,
I have taken a IBOutlet for Height Constraint of all of the Four UIViews and I want to make the height 0 of the view when data is not available in that view. The problem is View is not getting 0 height due to the 10 px bottom constraint of UILabel inside that View.
What i am getting is like this,
Code where constraints are handled
NSString *toName = [self jointNameFromArray:evidence.arrTo];
if ([Utility isEmptyString:toName]) {
cell.toViewHeight.constant = 0;
}
else {
cell.lblToName.text = toName;
}
NSString *fromName = [self jointNameFromArray:evidence.arrFrom];
if ([Utility isEmptyString:fromName]) {
cell.fromViewHeight.constant = 0;
}
else {
cell.lblFromName.text = fromName;
}
NSString *ccName = [self jointNameFromArray:evidence.arrCc];
if ([Utility isEmptyString:ccName]) {
cell.ccViewHeight.constant = 0;
}
else {
cell.lblCCName.text = ccName;
}
NSString *custName = [self jointNameFromArray:evidence.arrCustodian];
if ([Utility isEmptyString:custName]) {
cell.custViewHeight.constant = 0;
}
else {
cell.lblCustName.text = custName;
}
[cell layoutIfNeeded];
If you're targeting iOS 9+, I'd suggesting using UIStackView, as it does exactly what you want just by setting the .hidden property of your view to true.
https://www.raizlabs.com/dev/2016/04/uistackview/
If you are unable to use UIStackView, you will need to set one of the vertical padding constraints to a lower priority than the height constraint. You will also need to set .clipsToBounds to true.
Set constraint programatically and write logic as u want it is good idea and dont forgot to set layoutifnedded.

How to set constraints programmatically for a varying number of UIView

I'm new to auto layout constraints. In my application there are 3 buttons in one line. Now I want to set the constraints programatically as shown in the image below:
If there is one, then the button show in center horizontally.
If two buttons are enable, then second line shown in same image as three image.
My approach is to setting all buttons centerX to superview's centerX at the beginning.
IBOutletCollection(UIButton) NSArray *allButtons;
//3 buttons' references
IBOutletCollection(NSLayoutConstraint) NSArray *allButtonCenterConstraints;
//buttons' centerX constraints' references
and then if in viewDidAppear:
int t = arc4random() %3;// one of the 3 cases you will need
if (t == 0) {//if you want all 3 buttons appears
//all buttons shown
NSLayoutConstraint *c1 = allButtonCenterConstraints[0];//first button's centerX reference.
NSLayoutConstraint *c2 = allButtonCenterConstraints[2];//third button's centerX reference.
c1.constant = -self.view.frame.size.width*0.25;//push left
c2.constant = +self.view.frame.size.width*0.25;//push right
}
else if (t == 1)
{
//two buttons shown;
[allButtons[0] setHidden:YES];// close the one you dont need
NSLayoutConstraint *c1 = allButtonCenterConstraints[1];
NSLayoutConstraint *c2 = allButtonCenterConstraints[2];
c1.constant = -self.view.frame.size.width*0.125;
c2.constant = +self.view.frame.size.width*0.125;
}
else
{
//close 2 buttons
[allButtons[0] setHidden:YES];
[allButtons[1] setHidden:YES];
}
[self.view layoutIfNeeded];
if you need something depends on the width of buttons, you can set the constants according to the width of buttons.
Make constraint outlet after that you set constraint pragmatically
like as :
#IBOutlet var mainViewWidthConstant: NSLayoutConstraint!
if (self.view.frame.size.width == 320){
mainViewWidthConstant.constant = 320
}
else if (self.view.frame.size.width == 375)
{
mainViewWidthConstant.constant = 375
}
else{
mainViewWidthConstant.constant = 414
}

How can I apply multiplier to all my constraints created on nib files?

I've pretty much finished my project, only to realize fonts and spaces in between look weird in iPhone 6Plus.
I've created all my constraints to drag and drop on XIB files. My colleague added these codes on a global function so I can apply the multipliers:
- (float)constraintScale {
if (IS_STANDARD_IPHONE_6_PLUS) {
return 1.29;
}
return 1.0;}
- (float)textScale {
if (IS_STANDARD_IPHONE_6_PLUS)
{
return 1.16;
}
return 1.0; }
My problem is now having to drag each (more than 100) constraints into my code and applying each with these multiplier and scale. Is there a more convenient way to add multipliers to my XIB files? I've thought about subclassing but that will probably take the same amount of time?
self.view.constraints
will give you all the constraints on the view.
for(NSLayoutConstraint *c in self.view.constraints)
{
c.multiplier = [self constraintScale];
}
probably work. Hope it helps.
edit:
sorry for readonly issue. another way to get rid of this problem may be;
NSMutableArray *constraintsNew = [NSMutableArray new];
for (NSLayoutConstraint *c in self.view.constraints)
{
NSLayoutConstraint *newC = [NSLayoutConstraint constraintWithItem:c.firstItem
attribute:c.firstAttribute
relatedBy:c.relation
toItem:c.secondItem
attribute:c.secondAttribute
multiplier:[self constraintScale]
constant:c.constant];
[constraintsNew addObject:newC];
}
[self.view removeConstraints:self.view.constraints];
[self.view addConstraints:constraintsNew];

how to set vertical space constraint in Auto layout with baseline?

there are two labels in view. my designer will offer me markman as following
However, I can only set the constraint in Xcode like following
I mean, the designer need me to align the two labels with baseline, however, I can set the space of two labels with frame. Is there a way to set space of two labels with baseline instead of label's frame using Autolayout?
thanks #Arkadiusz, #Fogmeister, I have implemented a NSLayoutConstraint category to do it.
#implementation NSLayoutConstraint (MarkmanSpace)
- (void)updateSpaceBetweenTopView:(UIView *)topView bottomView:(UIView *)bottomView {
UIFont *topViewFont = [self fontFromView:topView];
UIFont *bottomViewFont = [self fontFromView:bottomView];
if(topViewFont && bottomViewFont) {
CGFloat padding = fabs(topViewFont.descender) + fabs([self lowercaseGlyphTopBaselineHeightForFont:bottomViewFont]);
self.constant -= padding;
}
}
- (UIFont *)fontFromView:(UIView *)view {
UIFont *viewFont = nil;
if([view isKindOfClass:[UILabel class]]) {
viewFont = ((UILabel *)view).font;
}
return viewFont;
}
- (CGFloat)lowercaseGlyphTopBaselineHeightForFont:(UIFont *)font {
return font.ascender - font.xHeight;
}
#end
but there's a problem ,which is the font.xHeight is lower the actual height for Chinese Character,
Your designer wants you to use a baseline and an x-height, see http://en.wikipedia.org/wiki/Baseline_(typography). A format option for the x-height isn't available in Auto Layout (see NSLayoutFormatOptions), so I don't think it's possible.

Resources