I'm programatically adding a UIDatePicker control to a view. I want the DatePicker to appear docked to the bottom of the screen, in the standard way...
I'm setting the frame for the DatePicker and need to be aware of the different screen sizes for 3.5-inch iPhones and 4-inch iPhones.
The following code is producing the desired result, but I have a couple of questions...
// In ViewDidLoad
CGRect defaultFrame = CGRectMake(0,0,0,0);
_datePicker = [[UIDatePicker alloc] initWithFrame:defaultFrame];
CGRect bounds = [self.view bounds];
int datePickerHeight = _datePicker.bounds.size.height;
int navBarHeight = 44;
CGRect datePickerFrame = CGRectMake(0, bounds.size.height - (datePickerHeight + navBarHeight), 0, 0);
[_datePicker setFrame:datePickerFrame];
// In method responding to user tap
[self.view addSubview:_datePicker];
Q1. Is there a more elegant way to do this? Something other than, creating the DatePicker with a frame, checking its height, then setting its frame...
Q2. The view is a UITableView, sitting inside a UINavigationController. When I get the bounds of self.view, the size includes the whole view, including the 44 for the navbar. Yet, when I add the DatePicker with addSubview, if I don't include the offset for the navBar, it's off the bottom by 44...
Why does addSubview work within the smaller bounds when [self.view bounds] returns the full bounds?
Cheers,
Gavin
After looking into this some more, I've realised my original question was flawed. It wasn't clear where I was adding the UIDatePicker as a sub view. I've updated the question.
I now have two answers:
1) Position and add the UIDatePicker in ViewDidLoad. Use Autoresizing to deal with the view size change. Then make it visisible in response to the user tapping a control:
- (void)viewDidLoad
{
[super viewDidLoad];
_tableView = (UITableView*)self.view;
_tableView.backgroundColor = [UIColor colorWithRed:0.941 green:0.941 blue:0.913 alpha:1.000];
_tableView.backgroundView = nil;
_datePicker = [[UIDatePicker alloc] init];
CGRect bounds = [self.view bounds];
int datePickerHeight = _datePicker.frame.size.height;
_datePicker.frame = CGRectMake(0, bounds.size.height - (datePickerHeight), _datePicker.frame.size.width, _datePicker.frame.size.height);
_datePicker.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
_datePicker.isHidden = YES;
[self.view addSubview:_datePicker];
[_datePicker addTarget:self action:#selector(datePickerChanged:) forControlEvents:UIControlEventValueChanged];
}
2) Just set the frame for the UIDatePicker as required, not in ViewDidLoad:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.row) {
case RowDate:
{
CGRect bounds = [self.view bounds];
int datePickerHeight = _datePicker.frame.size.height;
_datePicker.frame = CGRectMake(0, bounds.size.height - (datePickerHeight), _datePicker.frame.size.width, _datePicker.frame.size.height);
[self.view addSubview:_datePicker];
break;
}
default:
break;
}
}
Thanks,
Gavin
The problem is that navigation bar pushes all the view downwards, after view did load initialized.
autoresizing mask may help.
For UIDatePicker, you don't need to specify its size. Because most of the time you will want it as wide as the screen and its height is fixed. But you need still to put it in the correct position. That is, you need to compute the correct position for it, set its frame.
Because most of the time you won't want your UIDatePicker to overlap your navBar. So Apple will let the addSubview work as if the bounds is "smaller".
Related
I'm dealing with a tricky bug on my app. I have a chat view controller where I deal with keyboard showing and hiding. But the weird part is when putting the app on background.
This is the view controller before going to background (tapping the home button)
And this is the app when returning from background
If I put the app on background using the home button, the black screen only appears for a second, but if I block the phone and then unlock, the black screen keeps there.
This is how I deal with the keyboard
- (void)keyboardWillShowWithRect:(CGRect)rect
{
//We adjust inverted insets because tableview will be inverted
CGFloat displacementDistance = rect.size.height - self.composeBarView.bounds.size.height;
self.tableView.contentInset = UIEdgeInsetsMake(self.composeBarView.bounds.size.height, 0, displacementDistance, 0);
[self.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y - displacementDistance, self.view.frame.size.width, self.view.frame.size.height)];
}
- (void)keyboardWillDismissWithRect:(CGRect)rect
{
//We adjust inverted insets because tableview will be inverted
CGFloat displacementDistance = rect.size.height - self.composeBarView.bounds.size.height;
self.tableView.contentInset = UIEdgeInsetsMake(self.composeBarView.bounds.size.height, 0, 0, 0);
[self.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y + displacementDistance, self.view.frame.size.width, self.view.frame.size.height)];
}
The tableView used to display the messages is inverted in order to display the first message at the bottom. Here's how I do it:
- (void)setupTableView
{
self.tableView.delegate = self;
self.tableView.dataSource = self;
//We adjust inverted insets because tableview will be inverted
self.tableView.contentInset = UIEdgeInsetsMake(self.composeBarView.bounds.size.height, 0, 0, 0);
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 500.0;
self.tableView.sectionFooterHeight = 30.0;
self.tableView.showsVerticalScrollIndicator = NO;
[self.tableView registerNib:[UINib nibWithNibName:#"MessagesDateHeader" bundle:nil] forHeaderFooterViewReuseIdentifier:#"MessagesDateHeader"];
// Table View is inverted so we display last messages at the bottom instead of top
self.tableView.transform = CGAffineTransformMakeRotation(-M_PI);
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapOnTableView)];
tap.numberOfTapsRequired = 1;
[self.tableView addGestureRecognizer:tap];
}
Also, I have self.definesPresentationContext = YES; set on viewDidLoad
Anyone can give me a hint of what's happening?
Thanks
I used to have a similar problem. Issue was the resizing of the tableView when keyboard appears/disappears based on its frame.
What I did was set a constraint between the bottom of the tableView and the superView and then change its constant with the appearance/disappearance of the keyboard.
Instead of relying on keyboardWillShowWithRect: and keyboardWillDismissWithRect: you can use listen to UIKeyboardWillChangeFrameNotification and handle it like follows
- (void) keyboardWillChangeFrame:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrameEnd = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrameEnd = [self.view convertRect:keyboardFrameEnd toView:nil];
[UIView animateWithDuration:[userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]
delay:0.f
options:[userInfo[UIKeyboardAnimationCurveUserInfoKey] intValue] << 16
animations:^{
self.bottomConstraint.constant = CGRectGetHeight(self.view.frame) - keyboardFrameEnd.origin.y;
[self.view layoutIfNeeded];
} completion:nil];
}
[self.view layoutIfNeeded] is used so that view shows the whole animation
I am building a tutorial that uses custom subclasses of UIViewController to supply views for the tutorial. I would like to have a subview with instructions appear and then slowly fade out. I am trying to do it this way: a wrapper page controller class, custom UIViewController classes added as child view controllers, and an animation added to the custom UIViewController class's view.
Here is an example of how I add a child view controller to a tutorial page view:
else if(index==2)
{
FollowTutorViewController *next1 = [[FollowTutorViewController alloc] init];
next1.view.userInteractionEnabled = NO;
next1.typeDisplay = 1;
next1.index = index;
[page addChildViewController:next1];
next1.view.frame = page.view.frame;
[page.view addSubview:next1.view];
}
And here is an example of how I do the animation within the subclassed UIViewController:
- (void)messageTutorial
{
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
UILabel *directionsLabel = [[UILabel alloc] initWithFrame:CGRectMake(width/2, height/2, 100, 100)];
directionsLabel.backgroundColor = ORANGE_COLOR;
directionsLabel.textColor = BACKGROUND_COLOR;
directionsLabel.text = #"Follower pages show who you're following and who's following you\n\nYou can also see a list of most-controversial posters when you look for New People \n\nBuzz and Judge to gain followers";
directionsLabel.font = [UIFont systemFontOfSize:SMALL_FONT_SIZE];
[directionsLabel setLineBreakMode: UILineBreakModeWordWrap];
[directionsLabel setNumberOfLines:0];
CGSize constraint = CGSizeMake(width*.8 , 200000.0f);
CGSize size = [directionsLabel.text sizeWithFont:[UIFont systemFontOfSize:SMALL_FONT_SIZE] constrainedToSize:constraint];
directionsLabel.alpha = .8;
CGRect f = directionsLabel.frame;
f.size = size;
directionsLabel.frame = f;
directionsLabel.center = self.view.center;
[self.tableView addSubview:directionsLabel];
[UIView animateWithDuration:15 animations:^{
directionsLabel.alpha = 0;
} completion:^(BOOL finished) {
[directionsLabel removeFromSuperview];
}];
}
I would like to make the label disappear slowly starting when the user sees that view, but now when I run this code, regardless of where I call it, the view is gone by the time the user looks at the page. I have tried running this code in viewWillAppear, viewDidAppear, viewDidLayoutSubviews of the child custom UIViewController. Any other suggestions?
I have a scenario in mind, which I tried to implement but to no avail- Put a scrolling view with a text box, all of that within page controller.
Here's what I've done- UIPageViewController->ViewController with data ->UIView ->UIScrollView -> UITextView.
What it does? Well, other than scrolling a bit (not to show all of the text) and showing a weird white bar at the bottom, nothing.
Useful snippets of code- basically I linked my height constraint, and then in code:
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
//CGSize sizeThatShouldFitTheContent = [_textView sizeThatFits:_textView.frame.size];
self.theScrollView.frame = CGRectMake(self.theScrollView.frame.origin.x, self.theScrollView.frame.origin.y, 320, 2000);//[_theScrollView sizeThatFits:_theScrollView.frame.size];
_heightOfText.constant = 2000; //sizeThatShouldFitTheContent.height;
//_textView.frame = CGRectMake(_textView.frame.origin.x, _textView.frame.origin.y, sizeThatShouldFitTheContent.width, sizeThatShouldFitTheContent.height);
_textView.scrollEnabled = NO;
NSLog(NSStringFromCGRect(self.theScrollView.frame));
self.theScrollView.scrollEnabled = YES;
}
But nope.
Thanks in advance
This solved my issue- apparently I should call a selector...
-(void)viewDidLoad
{
// Change the size of page view controller
self.pageViewController.view.frame = CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height + 40);
[self performSelector:#selector(adjustScrollViewContentSize) withObject:nil afterDelay:0.1];
}
-(void)adjustScrollViewContentSize
{
_scrollView.contentSize = CGSizeMake(4000, 4000);
}
Hi here is what I'm trying to do.
I've got a Table View with transparent cell's background.
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"trans.png"]];
And I added to the ViewDidLoad a background image.
- (void)viewDidLoad {
bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
bgView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"homelights.png"]];
[super viewDidLoad];
self.title = #"";
}
So this works fine, I've got a tableView with transparent cells and a background image. Now the plan is when I scroll the TableView down, the image should react and move up.
Any idea how to trigger the scrolldown? Or where to start?
I enabled scrollViewDidScroll;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat scrollOffset = scrollView.contentOffset.y;
Works like a charm, I can read the scroll position, but when I'm trying to animate UIView, by linking the scrollView.contentOffset.y to the UIView "y" position;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat scrollOffset = scrollView.contentOffset.y;
[UIView beginAnimations:#"UIViewAnimationCurveEaseInOut" context:nil];
bgView.center = CGPointMake(bgView.center.x, bgView.center.y+scrollOffset);
[UIView commitAnimations];
}
So I tried to make the UIView bigger to the original size of the image 640x832 to be able to navigate on it, but doesn't seems to work.
bgView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 640, 832)];
bgView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"homelights.png"]];
Any idea why this is happening?
Your table view is a subclass of UIScrollView and your delegate for the table view is also (automatically) a delegate of the scroll view. Implement the scrollViewDidScroll: delegate method and use it to monitor how the table view has changed and move your background view.
I have a UIView which is located offscreen and I'm animating the frame so that the view slides in offscreen from the bottom and is visible. I'd like to simultaneously animate the alpha property of a UILabel on the view as well so it fades in. Unfortunately it appears I can't do the alpha animation because the view is offscreen and doesn't appear to take hold. It looks something like this:
nextCell.titleLabel.alpha = 0;
[UIView animateWithDuration:collapsedAnimationDuration animations:^{
CGRect newFrame = lastCell.frame;
newFrame.origin = CGPointMake(lastCell.frame.origin.x , lastCell.frame.origin.y + THREAD_CELL_HEIGHT);
nextCell.frame = newFrame;
nextCell.titleLabel.alpha = 1;
}];
Is it not possible to start animating the alpha of the subview because it's offscreen? If I position the view on screen and then try the animation it looks great but that's not the effect I'm going for. Thanks for your help.
Is this code executed in cellForRowAtIndexPath? If so, try moving it to tableView:willDisplayCell:forRowAtIndexPath:. The table view resets various properties of the cell before displaying it.
From the AppDelegate didFinishLaunching method:
self.myView = [[MyView alloc] initWithFrame:CGRectMake(320, 480, 400, 400)];
self.myView.titleLabel.text = #"test text";
self.myView.titleLabel.alpha = 0;
[UIView animateWithDuration:10.0 animations:^{
CGRect newFrame = self.myView.frame;
newFrame.origin = CGPointMake(0 , 0);
self.myView.frame = newFrame;
self.myView.titleLabel.alpha = 1;
}];
[self.viewController.view addSubview:self.myView];
MyView is just this:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self addSubview:self.titleLabel];
}
return self;
}
- (UILabel *)titleLabel
{
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
}
return _titleLabel;
}
I did no important changes to the code you presented and it worked fine. So assuming you're not doing what Tim mentioned (it won't work if you're doing it), we need more details to help you out.