Overlapping UITextView border - iphone

I need to support Landscape and Portrait orientation for my app, and I venture into using a single UIView.
When I first simulate the application, it displays the result without a problem. However, when I change orientation to landscape, the problem occurs.
The first time I run the application in portrait:
When I change the orientation to landscape:
Notice the lower left corner, near the Information tab.
When I change back the orientation to portrait:
It gets worse.
The code I am using,
- (void)updateLayoutForNewOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
CGFloat height = CGRectGetHeight([[UIScreen mainScreen] bounds]);
CGFloat width = CGRectGetWidth([[UIScreen mainScreen] bounds]);
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
NSLog(#"Portrait");
[self iPhoneUserInterfacePortrait:width height:height];
}
else if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) {
NSLog(#"Landscape");
[self iPhoneUserInterfaceLandscape:width height:height];
}
}
- (void)iPhoneUserInterfacePortrait:(CGFloat)width height:(CGFloat)height
{
UITextView *descriptionTextView = [[UITextView alloc] initWithFrame:CGRectMake(0.0, 0.0, width - 100.0, height - 300.0)];
[self makeBorder:descriptionTextView];
[self setInformation:descriptionTextView];
[self.view addSubview:descriptionTextView];
}
- (void)iPhoneUserInterfaceLandscape:(CGFloat)width height:(CGFloat)height
{
UITextView *descriptionTextView = [[UITextView alloc] initWithFrame:CGRectMake(0.0, 0.0, width + 230, height - 368.0)];
[self makeBorder:descriptionTextView];
[self setInformation:descriptionTextView];
[self.view addSubview:descriptionTextView];
}
- (void)makeBorder:(UITextView *)descriptionTextView
{
descriptionTextView.layer.borderWidth = 3.0;
descriptionTextView.layer.cornerRadius = 15.0;
descriptionTextView.layer.borderColor = [[UIColor grayColor] CGColor];
[self.view addSubview:descriptionTextView];
}

You're adding several views while you want just one.
[self.view addSubview:descriptionTextView];
To get rid of this line, you could add field descriptionTextView to your ViewController subclass and change frame of that text view without adding/removing it from self.view.
Also you should try to play with AutoResizing masks to see if you can get needed results without actually changing frame manually.
And you should be careful with that constants: try your app on both 3.5 and 4.0 inch simulator devices.

Related

What piece am I missing to set an iOS background?

I have:
[A series of if statements sets backgroundImage to be something like [UIImageNamed #"Background-Default-Landscape"]; am I right to assume that the device will look for the file named Background-Default-Landscape#2x.png if it is a Retina display?]
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:backgroundImage];
CGRect containerRect = CGRectZero;
containerRect.size = [backgroundImage size];
UIView *containerView = [[UIView alloc] initWithFrame:containerRect];
[containerView addSubview:backgroundView];
Right now my app is launching and once it loads displays a white screen, not a background specified (none of the backgrounds are solid white).
What am I missing to draw a background before I start putting things on the background, further below in the code?
Thanks,
--EDIT--
As far as code, I have:
- (void) renderScreen
{
int height = [[UIScreen mainScreen] bounds].size.height;
int width = [[UIScreen mainScreen] bounds].size.width;
UIImage *backgroundImage = nil;
if (height == 2048 || height == 2008 || height == 1024 || height == 1004 || height == 984)
{
backgroundImage = [UIImage imageNamed:#"Background-Default-Portrait"];
}
// Other such statements cut.
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:backgroundImage];
CGRect containerRect = CGRectZero;
containerRect.size = [backgroundImage size];
UIView *containerView = [[UIView alloc] initWithFrame:containerRect];
[containerView addSubview:backgroundView];
[self.view addSubview:containerView];
Then below that are statements to draw on the background.
This method is called when the initial view loads and when it is rotated:
- (void)viewDidLoad
{
[super viewDidLoad];
[self renderScreen];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(renderScreen) name:UIDeviceOrientationDidChangeNotification object:nil];
}
The behavior is that the correct initial screen loads, then it fades to white and nothing interesting seems to happen after that.
--EDIT--
At the top, I have:
int height = [[UIScreen mainScreen] bounds].size.height;
int width = [[UIScreen mainScreen] bounds].size.width;
When I NSLog the height and width, for a retina device in landscape mode, I get a height of 1024 and a width of 768. The image displayed is a rotation of the portrait image; if I turn the device on the side the image neatly fills the whole screen but as-is the background displays a horizontally squeezed image.
Should I do anything different to be correctly obtaining height and width? I would expect for a device in landscape orientation the height would be 768 and the width would be 1024.
Every time renderScreen fires it will add two more subviews to the current controller's main view:
[containerView addSubview:backgroundView];
[self.view addSubview:containerView];
And this fires every time you rotate the device.
At least have the containerView as a property and replace it.
#property (nonatomic, strong) UIView *containerView;
and inside - (void) renderScreen
[self.containerView removeFromSuperview];
self.containerView = [[UIView alloc] initWithFrame:containerRect];
[self.view addSubview:self.containerView];
You could also add:
[self.view setNeedsDisplay];
So that the view is redrawn.

Is it possible to add fixed content to a UIScrollView?

I want to create a subclass of UITableView or UIScrollView that will have some shading at the top when the content offset is > 0 to indicate that the content is scrollable. (See image attached)
The way I'm implementing it right now is using the UIViewController that is the delegate of the tableView. I simply have a GradientView on top of the tableView, and I intercept scrollViewDidScroll: to animate the visibility of that top gradient.
My problem with this implementation is that it's not "clean". I want my UIViewControllers to take care of logic, and not to deal with applying gradients and stuff. I wish I could just drop a subclass of UITableView that will do that for me.
The challenge for me is that I can't figure out how the tableView could add to itself a fixed content on top of the scrollable content.
Another question is what method/s of UIScrollView should I override to intercept the scrolling event. Obviously I don't want the tableView to be the delegate of itself...
Any ideas?
Thanks!
Ok, so I found the solution on Apple's WWDC 2011 Session 104 video - Advanced Scroll View Techniques.
There is a whole section in this video about "Stationary Views" inside a scroll view.
According to Apple, the way to go here is to override layoutSubviews and put there all the code to position whatever you want - wherever you want.
I tried it and it's actually pretty easy and it's working as expected.
So for example if I would like a shadowed header on top of the table when the content is being scrolled, this is the code I should write:
-(void) layoutSubviews
{
[super layoutSubviews];
[self positionTopShadow];
}
-(void) positionTopShadow
{
CGFloat yOffset = self.contentOffset.y;
// I'm doing some limiting so that the maximum height of the shadow view will be 40 pixels
yOffset = MIN(yOffset, 40);
yOffset = MAX(0, yOffset);
CGRect frame = self.topShadowView.frame;
// The origin should be exactly like the content offset so it would look like
// the shadow is at the top of the table (when it's actually just part of the content)
frame.origin = CGPointMake(0, self.contentOffset.y);
frame.size.height = yOffset;
frame.size.width = self.frame.size.width;
self.topShadowView.frame = frame;
if (self.topShadowView.superview == nil)
{
[self addSubview:self.topShadowView];
}
[self bringSubviewToFront:self.topShadowView];
}
I've managed to figure out a much simpler way of doing this then what Avraham did.
I use the fact that the UIScrollView calls scrollViewDidScroll: ever pixel the scrolling changes to set the object at the location of the offset. Below is my full code to keep a gray bar at the top of the scrollview as you move around:
- (void)viewDidLoad {
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(5.0, 50.0, self.bounds.size.width - 15.0, self.bounds.size.height - 60.0)];
[scrollView setBackgroundColor:[UIColor colorWithRed:251.0/255.0 green:251.0/255.0 blue:251.0/255.0 alpha:1.0]];
[scrollView setContentSize:CGSizeMake(scrollView.frame.size.width + 500, 1000.0)];
[scrollView setDelegate:self];
[self addSubview:scrollView];
UIView* header = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, scrollView.contentSize.width, 40.0)];
[header setTag:100];
[header setBackgroundColor:[UIColor darkGrayColor]];
[scrollView addSubview:header];
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
UIView* header = [self viewWithTag:100];
[header setFrame:CGRectMake(0.0, scrollView.contentOffset.y, header.bounds.size.width, header.bounds.size.height)];
}
You could try using viewForHeaderInSection method of tableView for the shaded view(and also heightForHeaderInSection)... Make the shaded portion as a header.That way there is a fixed content on top of the scrollable content.
#define kImageOriginHight 300
- (void)scrollViewDidScroll:(UIScrollView *)scrollView1{
CGFloat yOffset = scrollView1.contentOffset.y;
// NSLog(#" y offset := %f", yOffset);
//zoom images and hide upper view while scrooling to down position
if (yOffset < 0) {//-kImageOriginHight
CGRect f = imgV.frame;
f.origin.y = yOffset;
f.size.height = -yOffset + kImageOriginHight;
imgV.frame = f;
//viewTableUpperView.alpha = 1.5 - (yOffset/-kImageOriginHight);
//viewTableUpperView.userInteractionEnabled = NO;
if(yOffset+0.5 == -kImageOriginHight){
[UIView animateWithDuration:0.1 animations:^{
//viewTableUpperView.alpha = 1.0;
}];
//viewTableUpperView.userInteractionEnabled = YES;
}
}
}

UIScrollView scrolls too far after initial zoom

My UIScrollView is populated with a large content and then zoomed with "setZoomScale:animated", such that it fits into the scrollview frame. The view appears properly zoomed out, and properly positioned in the scrollview. However, the user is able to scroll outside the content (the scrollview background color shows). It seems that he can scroll as far as the original content size (as if the content was not zoomed out).
Strangely enough, after the user zooms manually the first time, everything works fine, and the scrollview is constrained to the content size again.
I have searched the web, and gone through the Apple docs and examples, but could not find anyone having the same problem. The Apple examples seem to show the same thing as I do.
My content is a custom view that is derived from UIView, and whose CALayer is replaced by a CATiledLayer (as in the Apple examples). The drawing I do myself in drawLayer:inContext:.
Here is a code snippet from MyScrollViewController:
- (void)loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
scrollView.maximumZoomScale=1.0;
scrollView.delegate = self;
... setup theContentView ...
scrollView.contentSize = theContentView.bounds.size;
scrollView.contentInset = UIEdgeInsetsMake(20, 20, 20, 20);
[scrollView addSubview:theContentView];
self.view = scrollView;
double zoomScale = 0.5; // hardcoded for testing
[scrollView setZoomScale:zoomScale animated:NO];
NSLog(#"zoomscale=%g contentview=%# contentsize=%#", zoomScale, NSStringFromCGSize(theContentView.bounds.size), NSStringFromCGSize(scrollView.contentSize));
[scrollView release];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return theContentView;
}
The NSLog statement shows a correctly set contentSize and zoomscale...
Hoping there is something obvious that I am missing...
I ended up setting the zoomScale on the next runloop (instead of directly from loadView), which worked:
- (void) loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
scrollView.maximumZoomScale=1.0;
scrollView.delegate = self;
... setup theContentView ...
scrollView.contentSize = theContentView.bounds.size;
scrollView.contentInset = UIEdgeInsetsMake(20, 20, 20, 20);
[scrollView addSubview:theContentView];
self.view = scrollView;
// set the zoom level on the next runloop invocation
[self performSelector:#selector(initialZoom) withObject:nil afterDelay:0];
[scrollView release];
}
- (void) initialZoom {
double zoomScale = 0.5; // hardcoded for testing
UIScrollView *scrollView = (UIScrollView *) self.view;
[scrollView setZoomScale:zoomScale animated:NO];
}
Another possibility, suggested by Maverick1st, is to set the zoomscale from viewDidAppear, rather than LoadView. That approach has the added advantage that it shows an animation of the zooming, indicating to the user that the view starts in a zoomed state.
- (void) viewDidAppear {
double zoomScale = 0.5; // hardcoded for testing
UIScrollView *scrollView = (UIScrollView *) self.view;
[scrollView setZoomScale:zoomScale animated:YES];
}
I'm new at this but you could try clipping subviews. I created a scroll view with interface builder, and there in the attributes inspector was a checkbox clip subviews, so i think you should do something like that, only programmatically. Hope that helps.

How to layout a view whose frame depends of the orientation

Consider a view that has to aligned to the bottom of the screen and whose width must fill the screen.
Additionally, the height of the view depends of the orientation. In portrait orientation the height of the view must be 200px and in landscape the height must be 100px.
What is the best way to do this without assuming anything about the parent view size (ie: don't know if it has status bar or not, maybe there's a tab bar, etc.)?
This is my current code, that doesn't work:
- (void)viewDidLoad
{
[super viewDidLoad];
myView = [[UIView alloc] init];
CGSize myViewSize = CGSizeMake(self.view.frame.size.width, 200);
// Align bottom
float myViewY = self.view.frame.origin.y + self.view.frame.size.height - myViewSize.height;
myView.frame = CGRectMake(0, myViewY, myViewSize.width, myViewSize.height);
myView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
}
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
BOOL landscape = UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
myView.frame = CGRectMake(myView.frame.origin.x, myView.frame.origin.y, myView.frame.size.width, landscape ? 100 : 200);
}
Try using UIWindow object as given below:
[[UIApplication sharedApplication] keyWindow].screen.applicationFrame.size.height
Apple documentation for applicationFrame reads as below:
This property contains the screen bounds minus the area occupied by the status bar, if it is visible. Using this property is the recommended way to retrieve your application’s initial window size. The rectangle is specified in points

Landscape Screen shifted to right in 20px

I am trying to rotate the screen on the iphone. For example,
First state of screen : is "Portrait".
Then it calls "Landscape" screen. Following code is used for making Landscape screen :
- (void)viewDidLoad {
[super viewDidLoad];
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;
CGFloat angle = 90 * M_PI / 180;
self.view.transform = CGAffineTransformMakeRotation(angle);
self.view.center = [nRangeAppDelegate sharedAppDelegate].window.center;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 480, 44)];
label.textAlignment = UITextAlignmentLeft;
label.numberOfLines = 2;
label.text = #"12345\n67890";
[self.view addSubview:label];
}
But above code shifted to right in 20 px.
How to make 480x320 view at point 0,0 no shifting ?
Thanks in advance.
Update:
My application navigation based application. All UIViewControllers have one UIView. When call the the view I am trying rotate, this code is used : MyRoratedController *myCtrlr = [[MyRoratedController alloc] initWithNibName:#"MyRoratedController" bundle:nil]; [navigationController pushViewController: myCtrlr animated:NO]; I've changed CGPointMake(160.0, 240.0) by many different values. But no changes.
Your window is shifted because the appDelegate window is not centred on the screen as it is drawn below the status bar
Try this:
self.view.center = CGPointMake(160.0, 240.0);
You must make sure you are adding this view to a normal viewcontroller (ie not a nav controller or similar). The easiest way to do this is to add it to the main application window, like so:
MyRoratedController *myCtrlr = [[MyRoratedController alloc]
initWithNibName:#"MyRoratedController" bundle:nil];
[window addSubview:[myCtrlr view]];
Note that if your app has a status bar shown you will have to change the 160 above to 150