UIView group opacity in single view heirachy - ios

I am writing a UI library in which i would like to be able to have the alpha of the UIViews as if the UIViewGroupOpacity info.plist ket was set. See following links:
Make UIView and subviews translucent as one layer, not individually
iOS controlling UIView alpha behaviour for subviews
But, as I am writing a lib, I dont want the projects to have to set this global key, and effect the behaviour of the main project.
Is there any other way of achieving this? I dont want to cycle through all the subviews and set alpha of each subview, as the project that includes my lib might have this key enabled...

Yes there is, you can set shouldRasterize of the view's layer.
containerView.layer.shouldRasterize = YES;
// Not setting rasterizationScale, will cause blurry images on retina displays:
containerView.layer.rasterizationScale = [[UIScreen mainScreen] scale];
This will have the same effect as UIViewGroupOpacity but only for containerView and it's subviews.

For iOS 7.0 and later:
Since iOS 7.0, this can be controlled for each CALayer individually through the allowsGroupOpacity property. By setting it to YES, you get the same behavior as if UIViewGroupOpacity was set, but just for that particular layer.

Related

UILabel redraws the view. How to avoid this behavior?

I have a viewController with three subviews.
Also I use AutoLayout and size classes.
These views are animated and change location and size.
After the animations I update a label but the whole view is redrawn so each view is in their initial position and size. This shouldn't happen.
As in Apple developer reference that says:
"The default content mode of the UILabel class is
UIViewContentModeRedraw. This mode causes the view to redraw its
contents every time its bounding rectangle changes. You can change
this mode by modifying the inherited contentMode property of the
class."
It doesn't seem clear to me how to modify the -contentMode- in order to update that label and leave the view -as is-. Can anyone give me a clue?
Thanks in advance.
It sounds like you may be using autolayout to lay out your view (e.g., via constraints in IB), but then you're manipulating your views' frames directly for your animations - is this the case? If so, you should instead be animating the constant values of your constraints, or possibly the transforms of your subviews.
If you manipulate frames directly in a view which uses autolayout, your changes will be over-written the next time the system lays out your view (e.g. after a label's text changes).
You have 3 options to overcome your issue -
Stop using AutoLayout in your Storyboard/Xib.
Not a great solution
Animate changes to the transform property of your subviews. e.g. myView.transform = CGAffineTransformMakeScale(2.0,2.0);
Useful for presentation and dismissal animations, but mixing AutoLayout and transforms has some issues pre-iOS 8.0
Add IBOutlets for the constraints you need to change in your animations. Animate changes to the constant values of those constraints.
Most robust approach but can lead to a lot of properties and code for complex animations
Try contentMode = UIViewContentModeCenter. This should prevent the redraw you're seeing. Although that may not be the contentMode you want.
Option 2 is to use CATextLayer to draw the label. It will resize very nicely with animation, but it's a lot more work to set up.

UITableViewCell AccessoryView not displayed properly

I have a UITableviewCell with a UISwitch in the accessoryView and it is displayed properly at runtime, but as soon as I rotate the device, the switch isn't anymore at the right place.
I could use constraints and not use the accessoryView, but I thought I could use this field..
Does anyone have an idea ?
Thanks !
EDIT :
here is the configuration in Interface Builder :
EDIT 2 :
I used a Custom cell :
with initialization on the default properties :
self.textLabel.font = [UIFont preferredDynamicFontForTextStyle:UIFontTextStyleBody fontSize:16.0];
self.backgroundColor = self.contentView.backgroundColor;
self.textLabel.text = item.title;
self.selectedImage = item.imageSelected;
self.unselectedImage = item.image;
self.imageView.image = self.unselectedImage;
// if I do it programmatically
self.accessoryView = [[UISwitch alloc] init];
I don't set constraints at all, because I use the default properties..
I'm just having the same issue as you.
Here are my observations:
If you set the accessoryView by code, it's always OK, whether the cell is selected or not, on both iOS 7 and iOS 8.
If you set the accessoryView by IB, the accessoryView moves to the top left corner after the first selection of the cell, and only on iOS 8.
I don't know if the issue is from IB or the way cells layout the accessory view on iOS 8 but so far the workaround is simple: always set the accessoryView by code to avoid unexpected behavior.
Looks like if you create the accessoryView and the editingAccessoryView in code and set it, everything works great!
If you try to set them in the UIStoryBoard, the UITableViewCell will remove the constraints after they fade away the first time, and it won't add them back. It's a shame, but hopefully they change it in a later version.
Hope that Helps!
If you are using autolayout (probably), you should setup constraints first.
For anyone finding this issue currently, I think I have an explanation of what might be happening here:
This is an issue with the translatesAutoresizingMaskIntoConstraints property on the view. From Apple's documentation, particularly relevant bits in bold:
If this property’s value is YES, the system creates a set of
constraints that duplicate the behavior specified by the view’s
autoresizing mask. This also lets you modify the view’s size and
location using the view’s frame, bounds, or center properties,
allowing you to create a static, frame-based layout within Auto
Layout. Note that the autoresizing mask constraints fully specify the
view’s size and position; therefore, you cannot add additional
constraints to modify this size or position without introducing
conflicts. If you want to use Auto Layout to dynamically calculate the
size and position of your view, you must set this property to NO, and
then provide a non ambiguous, nonconflicting set of constraints for
the view. By default, the property is set to YES for any view you
programmatically create. If you add views in Interface Builder, the
system automatically sets this property to NO.
So, when I came across this issue it was when I was changing the tableview from the contents being a stack view with label and switch into being a label with a UISwitch as the accessory view. The switch was being programmatically created earlier on view load for the container view the table was sitting in and the translatesAutoresizingMaskIntoConstraints was being set to NO - essentially the same situation that happens when you create the switch in interface builder. The code I was changing then tried to set up constraints but the switch would always bug out and be floating over to the wrong side when you rotated the screen. The solution for me was changing the translatesAutoresizingMaskIntoConstraints property to YES and letting the constraints be created by the view itself. It looks to me like this is a conflicting issue between what this property does on most views and what the accessory view itself tries to handle and if a programmer tries to go in and set things up themselves there seems to not be a good indication of what has gone wrong here. Hope this helps someone in the future!
I can say with some certainty that your constraints are not properly set up. But without seeing the storyboard's constraints, there's no way to say what is broken for sure.

GLKViewController orientation animation

I have a layout, where the screen is divided into 2 parts: UIView at the top and GLKViewController at the bottom. The problem is that when the screen orientation changes the
GLKViewController part is rotated and gradually stretched out until the animation finishes, at which point a new unstretched frame will be drawn.
Is there a way to disable the automatic rotation animation for the GLKViewController, so I could animate it manually by manipulating the modelview-projection matrix?
If you're just looking to deal with the stretching effect, you don't need to replace the whole orientation/rotation system. Depending on how much you're making use of device orientation in you're app's UI, you're likely to cause yourself more maintenance headaches by reimplementing orientation and rotation. (If you need to do more than just work around the stretching effect, the other answers are still good.)
Your view is drawing itself during the rotation animation, so all you need to do is ensure that it's drawing itself in a way that matches its intermediate size during the animation. The rotation animation is handled by Core Animation, so you use its presentationLayer method to access the transitory state during the animation. For example:
CALayer *presentationLayer = [self.view.layer presentationLayer];
CGSize layerSize = presentationLayer.bounds.size;
float aspect = fabsf(layerSize.width / layerSize.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(
GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f);
Run this snippet as part of your update/draw loop — note this might mean moving calculation of your projection matrix into the update/draw loop if it isn't there already. (You might also want to make sure this snippet only runs every frame if an orientation change is in progress.)
To see the animation in progress and make sure it's working right, use the "Toggle Slow Animations" command in the iOS Simulator.
(Credit where due: code is from this answer.)
GLKViewController is a subclass of UIViewController. UIViewController has a method willAnimateRotationToInterfaceOrientation:duration: You can override it and configure the animation of view.
You can find more details in the documentation: UIViewController Class Reference under the Responding to View Rotation Events section.
Depending on what you want to achieve, you would do different things to disable automatic rotation.
If you do not need rotation at all, disable it at project level. In Xcode, use the navigator to select the project (top item of the file list) and visit the "General" section of the target app. For device orientations, untick everything but "Portrait":
If you need to disable it for some "screens" and not for others, you need to disable rotation at the UIViewController level. UIViewController was initially designed to take up the whole screen area, so if you are using a GLKViewController that manages GLKView that covers the screen just partially, it is most probably inside another view controller. You need to subclass the parent view controller and add these methods:
// From iOS 6
- (BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
// iOS 5
- (BOOL)shouldRotateToOrientation:(UIInterfaceOrientation)orientation
{
return NO;
}
These will prevent the parent view controller from being rotated.
Finally, if you need to allow rotation of some elements on the screen, but not others, then you will have to determine which method to use:
Allow the screen to rotate while rotating back those elements you want to remain static. I would not recommend it.
Block the screen from rotating as mentioned above, and manually rotating those elements that you want rotated by setting their transform property. This is what I would go for.

iOS controlling UIView alpha behaviour for subviews

In my example, I have 3 views: one red view containing two white views. I change the red container view's alpha to 0.3 and this happens (look at the image, the current result).
By seeing this, I can only assume (tell me if I'm wrong) that setting a view's alpha will also set all of its subviews' alphas. My question is : is there a way to simply tell the red view to act as a whole so that setting its alpha would give something that looks like the wanted result (in the image)?
This is what it looks like without any alpha :
To elaborate on Mark's answer: If you set UIViewGroupOpacity in the Info.plist, it will change the behavior for all views in your app, if you are interested in only fixing the rendering of this particular view, you could also use this snippet:
redContainerView.layer.shouldRasterize = YES;
// No setting rasterizationScale, will cause blurry images on retina.
redContainerView.layer.rasterizationScale = [[UIScreen mainScreen] scale];
The iOS alpha property is inherited by its subviews. If we are setting alpha 0.3 for red view then both subview will have the alpha = 0.3. There is no way to stop subviews from inheriting their alpha value from their superview.
The solution might be to set the colour of the red view with an alpha of 0.3. The color property will not be inherited by its subview.
[redView setBackgroundColor:[UIColor colorWithHue:238.0f/255.0f saturation:24.0f/255.0f brightness:24.0f/255.0f alpha:0.3]];
Check out the possible UIKit keys for Info.plist, specifically UIViewGroupOpacity.
UIViewGroupOpacity (Boolean - iOS) specifies whether Core Animation
sublayers inherit the opacity of their superlayer.
Info.plist UIKit Keys

changing subviews when rotating on ios

I need a view controller(on ipad) to share two modes, one in portrait and one in landscape. Actually, I pretty much want to mimic the functionality of UISplitViewController, but I want to be able to use not as the top level view controller. HIG guidelines aside, I have a general problem that I think anyone who is switching views between orientations will run into.
1) To provide for smooth transitions between views, I would like to call my view changes( and animations) inside willRotateToInterfaceOrientation:duration instead of didRotateToInterfaceOrientation method. The problem is, at this stage, the view frame and bounds have not yet changed to their new ones, so you end up having to set the frame manually, like:
subview.frame = CGRectMake(0,0,320,768);
instead of something nicer, maybe:
subview.frame = CGRectMake(0,0,320, self.view.frame.height);
2) Furthermore, even if you try such shenanigans, if the view autoresizes its subviews, you still get nowhere with it. If you disable the autoresizing of subviews, then stuff like this does not even work anymore:
subview.frame = self.view.frame; //because then you'd have to always specify the exact rect.
Does anyone have any insight to offer on this?
Thanks!
Try using willAnimateRotationToInterfaceOrientation:duration: instead.
Also, if you have your autoresizing masks set up correctly on the subviews, you shouldn't need to care whether the main view has been adjusted yet or not. Just size the subviews to fit appropriately inside the main view as it is sized now and it will just work.
If you don't (or can't) have your autoresizing masks set up correctly, you should then already have code to handle size changes in the main view's layoutSubviews method. So again, you shouldn't have to care much whether the main view has been resized yet or not.

Resources