iPhone - UIView orientation changes - iphone

I got a subclass of UIView in which I draw an NSString object using the drawInRect:withFont:lineBreakMode:alignment: method.
Unfortunately, when I change the orientation of the device, say from portrait into landscape mode, the text doesn't get redrawn correctly but gets scaled and distorted against my intentions.
How can I solve this problem?

This is part of how Core Animation animates transitions. A snapshot of your view is taken, and then stretched/moved into the new location.
There are a number of ways you can tackle this. First of all you can try this:
self.contentMode = UIViewContentModeRedraw;
This might be all you need and will tell Core Animation to redraw your contents instead of using a snapshot. If you still have issues, you can try defining a "stretchable" region that is stretched instead of your text.
For example, if you know you have a vertical slice of your view where there is never any text, you can define the content stretch to be in that little section and any stretching only occurs there, hopefully keeping your text intact.
CGFloat stretchStartX = 25.0f;
CGFloat stretchEndX = 30.0f;
/* Content stretch values are from 0.0-1.0, as a percentage of the view bounds */
self.contentStretch = CGRectMake(stretchStartX / self.bounds.size.width,
0.0f,
stretchEndX / self.bounds.size.width,
1.0f);
This causes the your view to be stretched only between x values 25 and 30.

Related

Rotating one view and leaving another unmoved in Swift / Xcode for iOS on orientation change

I'm writing an iPhone app. For most of the scenes on our storyboard the default behaviour when the phone changes from portrait orientation to landscape orientation is fine. I.e. this is the layout change we want:
But for one of the scenes in the storyboard I do not want this layout change. Instead I want this:
I am confused about how to achieve the latter layout change. I can use size classes to ensure that the 100 pixels gap for the toolbar swaps from the bottom (portrait) to the right (landscape), but I cannot work out how to constrain the toolbar correctly, especially because the rotation needs to happen in code, e.g.
switch (UIDevice.currentDevice().orientation) {
case UIDeviceOrientation.LandscapeLeft, UIDeviceOrientation.LandscapeRight:
self.toolbarView.transform = CGAffineTransformMakeRotation(CGFloat(3 * M_PI_2))
// Reposition toolbarView into space to the right of the content's view
break
default:
self.toolbarView.transform = CGAffineTransformIdentity
break
}
What should I do to reposition the toolbar correctly?
Layout constraints constrain the frame of the given view. Let's see what the documentation says about the interplay between the frame and transform properties:
WARNING
If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
...
if the transform property contains a non-identity transform, the value of the frame property is undefined and should not be modified. In that case, you can reposition the view using the center property and adjust the size using the bounds property instead.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instp/UIView/frame
So you cannot do this via AutoLayout. You have to use center and bounds, and do positioning from code:
-(void)layoutSubviews {
[super layoutSubviews];
self.toolbarView.center = ...
self.toolbarView.bounds = ...
}

UISegmentedControl width in SplitView

I have several UISegmentedControls in different view controllers. On the iPad, when the device changes orientation I realign the segments inside these so they line up with the UITableViews underneath. The problem I'm seeing is that although the resizing mask is set for the UISegmentedControl and without any of my code, it resizes to fill the right width, once I try to change the width of the segments within, the segmented will either not stretch all the way to the end, or they'll be too big and go over.
This only seems to happen when the controls are in a split view.
- (void)orientationChanged:(NSNotification *)notification {
[self setHeaderWidths];
}
-(void)setHeaderWidths{
int totalWidth = self.segSorter.bounds.size.width;
int areaWidth = 100,
priceWidth = 100;
int padding = 35;
[self.segSorter setWidth:totalWidth -padding- areaWidth-priceWidth forSegmentAtIndex:0];
[self.segSorter setWidth:areaWidth forSegmentAtIndex:1];
[self.segSorter setWidth:priceWidth forSegmentAtIndex:2];
}
Initially I thought it might be caused by the animation, but even adding a delay so the rotation animation has totally finished has no effect in the width. Are controls within a splitview given a false width or something?
One option you might consider is simply re-creating the control or removing then re-adding the segments.
Without knowing what's going on inside the control, as you change the widths, what if the first one you're resizing (say increasing width) would cause the sum of the widths to exceed the bounds? Without knowing how that logic is working inside, when I've run across things like this that seem like a fight for a simple change, it's easier to roll a method that just recreates the control as you need it (or removes and re-adds the elements) in the specified sizes.

UILabel animated resize side effect

I came across issue where I need to animate height change of UILabel frame or its enclosing view's frame. Label is multiline.
The issue is that given with large text which does not fit initially into label(say it takes 3 lines), then animating the label's height to increase, immediately changing 3 line to 4 and then animating the frame increase.
Opposite effect is when the text fully fit into label(4 lines), then I animate height decrease, 4 lines are instantly becoming 3 and only then I see animating frame size decrease.
This is of course not good for an eye.
What I expect is something like keep the label's origin.y intact and then as frame is increasing the more text is revealing from the bottom. The ellipsis may convert to missing word instantly, that is not a problem.
If you want to keep origin.y intact then you have to animate it with CoreAnimation stuff.
Set once:
label.layer.anchorPoint = CGPointMake(0,0); //I believe 0,0 is the upper left or it was 0,1?
after doing this you just need to change the size accordinly:
I would measure the text first (With NSString's methods)and see if the label needs to be resized.
In case it needs to then adjust label.numberOfLines and label.layer.bounds = CGRectMake(label.layer.bounds.origin.x,label.layer.bounds.origin.y,
label.layer.size.width, newHeight);
This should work
I hope this helps.

UILabel inside custom UITableViewCell not drawing at the correct size

I have a custom table cell which contains a number of UILabels. At runtime, I am adjusting the height of the labels to fit their contents using sizeWithFont:constrainedToSize:lineBreakMode: and repositioning them accordingly. The last label in the cell contains a large amount of text, causing it to wrap, and I'm having a very odd problem. Although the sizeWithFont call returns the correct size, and I'm setting the UILabel's frame to that height, it draws a couple of lines short. This screenshot illustrates what I'm talking about:
In this example, the height of the full block of text should be 90 (as checked in Interface Builder), and that's what returns from sizeWithFont. It's also the height that the UILabel's frame is set to, which I have verified by logging and also by stopping execution and inspecting the value. However, as you can see, it's clearly not drawing the full 90 pixels high, although it's correctly allocating the space for it (the thin black line above 'Edited' is the table cell border). I'm completely perplexed. If anyone can offer some insight as to why it's behaving this way, I would be very grateful.
At last, a solution!
Turns out that the cell does layout twice -- once during heightForRowAtIndexPath, which is where I tweak all the heights of the subviews and the cell, and later during some untraceable transaction originating in __CFRunLoopDoObservers. How did I trace this? I added a layoutSubviews override to my custom table view cell class so I could breakpoint it.
During the second pass, the last UILabel subview was getting set to a shorter height than I set it to, probably in accordance with some arcane autoresizing rules. (Yes, I tried tweaking all of those settings first, with no success.) As it turns out, merely doing nothing in layoutSubviews disabled this framework behavior, allowing me to completely control how my views draw.
With iOS 8 it doesn't work anymore like this. Implementing layoutSubviews alone doesn't do the trick, because the layout of subviews have already changed when the method is called.
I have found 2 solutions:
adding NSLayoutConstraint to layout the subviews programmatically
implementing subview's layoutSubviews and change the frame
An example für solution 2:
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect frame = self.frame;
frame.size.height = 39.f;
self.frame = frame;
}
I've fought with similar problems. It was to do with other properties being set in previous incarnations of the cell. To find it / prove it I changed the reuseidentifer for the offending cell to make sure it was a unique cell.

Drop shadows on iPhone Clock App "Alarm" tab

In the clock app that comes with the iPhone there is a tab view for setting alarms. In that view each of the UITableViewCell instances have a drop shadow around them. Does anyone know how to achieve the same effect?
Also it looks like the tab bar at the very bottom has a drop shadow above it as well.
Ideas on how to achieve the same look would be greatly appreciated.
I was wondering how to do that and it just occurred to me to use the UITableView's footer view:
myTableView.tableFooterView = myCustomViewWithDropShadowImage;
I would guess that there is an extra cell which contains just a background image which is the transparent dropshadow. If it's not a cell (because that might create scrolling weirdness) it is probably an extra view positioned below the bottom cell of the UITableview which - again - simply contains an image of the drop shadow.
Like Thomas said, create a 100% width image (say, 320 x 40px on non-retina devices) and create 4 instances of UIImageView with it. The first, at the top of your main view. The second, at the bottom, and in addition do this:
UIImageView* bottomShadow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"BottmShadow.png"]]
bottomShadow.transform = CGAffineTransformMakeScale(1, -1);
(Flip it vertically)
Then, do the same with the other two, but place them as subviews of the Table View. One of them just outside the first row:
CGRect tableTopShadowFrame = tableTopShadow.frame;
tableTopShadowFrame.origin.y = -(tableTopShadowFrame.size.height);
[tableTopShadow setFrame:tableTopShadowFrame];
and the other just below the last row (you need to know the height of all the rows together. If your rows are all the same height, it is row height times number of rows).
Finally, you need to set the table's backgroundColor property to transparent
tableView.backgroundColor = [UIColor clearColor];
and possibly set some darkish gray for your main view's background color.