Strange three20's TTTableViewController frame properties - iphone

I am adding a TTTableViewController into an existing UIViewController, one strange thing I found is that the frame properties of the initialized TTTableViewController are wired, e.g. in a iOS layout.
I have:
UIStatusBar
UINavigationController
UIViewController
UITabBar
In order to set the TTTableViewController fill in all the remaining space I need to set the height to 460 instead of 367. (367 = 480-20-44-49)
e.g.
self.tableViewController.view.frame = CGRectMake(0, 0, 320, 460.0f);
instead of
self.tableViewController.view.frame = CGRectMake(0, 0, 320, 367.0f);
Why is it so?
*Edit for clarification: I mean TTTableViewController on the top of TTViewController (using [self.view addSubview:self.tableViewController.view];), and I need to set the self.tableViewController.view.frame = CGRectMake(0, 0, 320, 460.0f); instead of 367

It depends on when you are setting the frame, I think. I'm pretty sure when you set the frame in viewDidLoad, for example, you'll be setting it before the status bar and other things are taken into account. There might be other cases like this. If you set it to 320:460, it'll be resized to take into account the status bar and other stuff afterwards, making it fill in the rest of the screen. If you set it to 320:367 because you've already taken into account that stuff, it'll get resized again and squished (basically scaled down twice), making it only fill part of the screen. If you're using viewDidLoad you could try sticking it in another method (maybe viewWillAppear?) or just keep using 320:460.
It'd be nice to know when you set the frame, exactly. Also keep in mind that I could be way off. My mind's feeling a little fuzzy right now.

As per my understanding, only the size of your status bar is deducted i.e. 480-20 = 460. actually status bar is 22 pts but its approx.
Its just like when you add a viewcontroller to your navigation controller or your tab bar controller the size is auto rendered. So same is the case here, the three20 automatically adjusts the size of the view and if you try to set it to something smaller then that it behaves differently.
Its a nice question though. Happy Coding. Cheers!!

I wouldn't add a view of a different view controller into the main view of your current view.
You should present the TTTableViewController using the controller's present / dismiss functions. if you don't want to include the slide up effect, so the users won't see that it's a "different screen", use the boolean flag when you present the controller.
[self presentModalViewController:vc animated:NO];
Alternatively, use a TTTableView without the controller:
tableView = [[TTTableView alloc] initWithFrame:CGRectMake(0, kScrollViewHeight + kSignupLabelHeight, 320, kTableViewHeight) style:UITableViewStyleGrouped];
[tableView setBackgroundColor:[UIColor clearColor]];
tableView.delegate = self;
tableView.dataSource = [TTSectionedDataSource dataSourceWithObjects:
#"",
[TTTableTextItem itemWithText:#"Sign Up" URL:#"tt://signupController"],
nil];
[self.view addSubview:tableView];

Like a this code .
if use iOS6 , get current device.
if();
-(void)viewdidLoad{
TTTableViewController *tt = [TTTableViewController new];
CGRect frame = [UIScreen mainScreen].bounds;
tt.frame = frame;
tt.delegate = self;
[self.view addsubview:tt];
}

Related

Flipping between a standard and grouped style table via segmented control

I'm looking to use a UISegmentedControl to flip between two tables on the same view. One table (the default when the view is loaded) is grouped. The second table should be plain (non-grouped). The tableView is set to Grouped in the xib.
Right now I'm doing a reloadData when flipping between the two sets of data. So of course both "views" are in the grouped style. I know I can't change a tableView's style once set, but I'm looking to mimic that intent.
The best idea I've had is to create a second (plain) tableView in code, then flip between the two as needed. I could either show/hide (keeping them both in memory) or add and remove from the superView.
Both views may result in the user clicking through to additional pushed viewControllers. I want to retain consistency with the chain, including the ability to popToRootViewController from viewControllers further down the chain (the view in question is two or three steps down from the root).
Before I tear up my code with this idea I figure I'd check around to see what others thought.
Thanks!
UPDATE: Getting there. When I want to display the plain table view I have this:
PlainViewController *plainViewController = [[PlainViewController alloc] init];
UIView *plainTableView = plainViewController.view;
plainTableView.frame = CGRectMake(320, 0, 320, 480);
[self.view addSubview:plainViewController.view];
which loads and displays fine. But when PlainViewController's didSelectRowAtIndexPath fires, it fails to load the next view. didSelectRowAtIndexPath is firing (it's logged), but pushViewController does nothing; the next view's viewDidLoad never fires.
I suspect there's a discontinuity in the NavigationController but not quite sure where to fix that.
You can create a separate UIViewController and add its table to the 'main' view.
PlainTableViewController *ctr = [[PlainTableViewController alloc] init];
[self.view addSubview:ctr.view];
[ctr release];
You can also easily animate this process:
PlainTableViewController *ctr = [[PlainTableViewController alloc] init];
UIView *plainTableView = ctr.view;
plainTableView.frame = CGRectMake(320, 0, 320, 480);
[self.view addSubview:plainTableView];
[UIView animateWithDuration:0.3
animations:^{
groupedTableView.frame = CGRectMake(-320, 0, 320, 480);
plainTableView.frame = CGRectMake(0, 0, 320, 480);}
completion:^(BOOL finished){[groupedTableView removeFromSuperView];}
];
[ctr release];
Notice: You don't add it as view controller in your navigation controller so all calls of popToRootViewController should lead to your 'main' viewController. And that's what you want.
Assign your tableViewStyle in something like - (void) refreshTable; then when you tap your segmentedControl, also call [myTable reloadData];
You can use two different views that have table with both the style but with the same data and you can flip that two views on the segment selected index change event
OR
You have to remove the table from its superview and then realloc table with the selected style and add it again to that view.
I think that adding/removing the table views from the view containing the segmented control should work seamlessly.
In order to keep consistency, I think the approach is storing somewhere the information as to which view is selected through the segmented control, so that when you are displaying that view you know which is the current style. This info will be also stored in NSUserDefaults, so that it is persistent across runs.
Simple, every time that the segmented control changes, re-create the UITableView.
Upside: saves you from writing too much code, memory.
Downside: uses a bit more CPU.
But you'd be redrawing the view with reloadData anyway, so it is a very minimal CPU drain to simply recreate the view.
-(void)segmentedControlChanged{
CGRect originalFrame = tableView.frame;
UITableViewStyle tableStyle;
if(selectedSegmentIndex == 0){
tableStyle = UITableViewStylePlain;
} else {
tableStyle = UITableViewStyleGrouped;
}
[tableView removeFromSubview];
tableView = [[UITableView alloc] initWithFrame:originalFrame style:tableStyle];
tableView.delegate = self;
tableView.dataSource = self;
[self.view addSubview:tableView];
}
Note: code written by memory without checking in Xcode.

Why does UIScrollView always scrolls to bottom?

I have seen this question being addressed several times here at SO, e.g Problem with UIScrollView Content Offset, but I´m still not able to solve it.
My iphone app is basically a tab bar controller with navigation bar. I have a tableview controller made programmatically and a DetailViewController that slides in when I tap a cell in my tableview controller.
The DetailViewController is made in IB and has the following hierarchy:
top view => UIScrollView => UIView => UIImage and a UITextField.
My goal is to be able to scroll the image and text field and this works well. The problem is that my UIScrollView always gets positioned at the bottom instead at the top.
After recommendations her at SO, I have made my UIScrollView same size as the top view and instead made the UIView with the max height (1500) of my variable contents.
In ViewDidLoad I set the contentSize for the UIScrollView (as this is not accessible from IB):
- (void)viewDidLoad {
[scrollView setContentSize:CGSizeMake(320, 1500)];
[scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
NSLog(#"viewDidLoad: contentOffset y: %f",[scrollView contentOffset].y);
}
Specifically setting the contentOffset, I would expect my scrollView to always end up at the top. Instead it always go to the bottom. It looks to me that there is some autoscrolling beyond my control taking place after this method.
My read back of the contentOffset looks OK. It looks to me that there may be some timing related issues as the scrolling result may vary whether animation is YES or NO.
A ugly workaround I have found is by using this delegate method:
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrView {
NSLog(#"Prog. scrolling ended");
[scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
}
This brings my scrollview to top, but makes it bounce down and up like a yo-yo
Another clue might be that although my instance variables for the IBOutlet are set before I push the view controller, the first time comes up with empty image and textfield:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (!detailViewController) {
detailViewController = [[DayDetailViewController alloc] init];
}
// Pass dictionary for the selected event to next controller
NSDictionary *dict = [eventsDay objectAtIndex:[indexPath row]];
// This method sets values for the image and textfield outlets
[detailViewController setEventDictionary:dict];
// Push it onto the top of the navigation controller´s stack.
[[self navigationController] pushViewController:detailViewController animated:NO];
}
If I set animation to YES, and switch the order of the IBOutlet setting and pushViewController, I can avoid the emptiness upon initialization. Why?
Any help with these matters are highly appreciated, as this is really driving me nuts!
Inspired of Ponchotg´s description of a programmatically approach, I decided to skip interface builder. The result was in some way disappointing: The same problem, with the scrollview ending up in unpredictable positions (mostly at bottom), persisted.
However, I noticed that the scroll offset error was much smaller. I think this is related to the now dynamic (and generally smaller) value of ContentOffset. After some blind experimenting I ended up setting
[textView setScrollEnabled:YES];
This was previously set to NO, as the UITextView is placed inside the scrollview, which should take care of the scrolling. (In my initial question, I have erroneously said it was a UITextField, that was wrong)
With this change my problem disappeared, I was simply not able to get into the situation with scrollview appearing at bottom anymore in the simulator! (At my 3G device I have seen a slight offset appear very seldom, but this is easily fixed with scrollViewDidEndScrollingAnimation delegate described previously ).
I consider this as solved now, but would appreciate if anyone understand why this little detail messes up things?
OK! i have a question before i can give a correct answer.
Why are you using a UIView inside the Scrollview?
You can always only put your UIImageView and UITextField inside the UIScrollView without the UIView
and set the contentSize dynamically depending on the size of the text.
to give you an example how i do it:
int contSize = 0;
imageView.frame = CGRectMake(10, 0, 300, 190);
imageView.image = [UIImage imageNamed:#"yourimage"];
contSize = 190 //or add extra space if you dont want your image and your text to be so close
[textField setScrollEnabled:NO];
textField.font = [UIFont systemFontOfSize:15];
textField.textColor = [UIColor grayColor];
textField.text = #"YOUR TEXT";
[textField setEditable:NO];
textField.frame = CGRectMake(5, contSize, 310, 34);
CGRect frameText = textField.frame;
frameText.size.height = textField.contentSize.height;
textField.frame = frameText;
contSize += (textField.contentSize.height);
[scrollView setScrollEnabled:YES];
[scrolView setContentSize:CGSizeMake(320, contSize)];
In the above example I first create an int to keep track of the ysize of my view then Give settings and the image to my UIImageView and add that number to my int then i give settings and text to my UITextField and then i calculate the size of my text depending on how long is my text and the size of my font, then add that to my int and finally assign the contentSize of my ScrollView to match my int.
That way the size of your scrollview will always match your view, and the scrollView will always be at top.
But if you don't want to do all this, you can allways just:
[scrollView setContentOffset:CGPointMake(0, 0) animated:NO];
at the end of the code where you set your image and your text, and the NOto avoid the bouncing.
Hope this helps.

Navigation bar and top margin

I have an issue with a UINavigationBar and its y-offset. The bar is displayed without a UINavigationController as superview, yet that should not matter. In the viewController where the navigation bar appears the setup looks like this:
// Add Basic View
CGRect viewFrame = [[UIScreen mainScreen] applicationFrame];
UIView *myView = [[UIView alloc] initWithFrame:viewFrame];
myView.backgroundColor = [UIColor greenColor];
self.view = myView;
[myView release];
UINavigationBar *myBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)];
.... add some Stuff to the bar...
[self.view addSubview:myBar];
[myBar release];
As I add the navigationBar as a chield View to self.view I assumed that origin.y = 0 would mean that the bar should get directly displayed below the status bar. This works as expected if I start the app on my iPad, rotate it once (or more) and then drill down to the view that is described above. In this case the UINavigationBar is displayed properly. Yet if I start my app and directly drill down to the controller described above (without rotating the device before this particular controller appears) the navigation bar slides 20 points below the status bar. But as soon as I rotate the device then, the bar is fine again. I have checked the viewFrame.origin.y value and it is 20 points in both situations, hence I do not understand why in one case the bar just seems to ignore the origin.y value of its superview but does not in the other.
I am really confused about this, has anybody else ever experienced such an issue?
Thanks a lot for your help!
Ps. I have also tried it with a UIToolbar, the problem is the same.
Yes. My solution is to set the "Full screen on launch" flag to on in Interface Builder for the window in the MainWindow-iPad.xib file. Then design your views as if the 20 pixel status bar were always displayed, so in my root view, I have a toolbar that is positioned 20px below the top of the screen in the content view.

Learning the basics of UIScrollView

I've been having a very hard time finding good examples of UIScrollView. Even Apple's UIScrollView Suite I find a bit lacking.
I'm looking for a tutorial or example set that shows me how to create something similar to the iPhone Safari tab scrolling, when you zoom out from one browser window and can flick to others.
But I'm having a hard time just getting any old view showing within a scroll view. I have a view set up with an image in it, but when I add it to the scroll view, I only get a black rectangle, no matter what I put in the view I add.
Any links or code snippets would be great!
Here is a scroll view guide from Apple
The basic steps are:
Create a UIScrollView and a content view you want to put inside (in your case a UIImageView).
Make the content view a subview of the scroll view.
Set the content size of the scrollview to the frame size of the content view. This is a very important step that people often omit.
Put the scroll view in a window somewhere.
As for the paging behavior, check out UIScrollView’s pagingEnabled property. If you need to scroll by less than a whole page you’ll need to play tricks with clipsToBounds, sort of the reverse of what is happening in this StackOverflow question.
UIScrollView *scrollview = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
NSInteger viewcount= 4;
for (int i = 0; i <viewcount; i++)
{
CGFloat y = i * self.view.frame.size.height;
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, y,self.view.frame.size.width, self .view.frame.size.height)];
view.backgroundColor = [UIColor greenColor];
[scrollview addSubview:view];
[view release];
}
scrollview.contentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height *viewcount);
For more information Create UIScrollView programmatically
New 3/26/2013
I stumbled on an easier way to do this without code (contentSize)
https://stackoverflow.com/a/15649607/1705353

iPhone Modal View Smaller that the screen

I'm trying to do something that shouldn't be that complicated, but I can't figure it out.
I have a UIViewController displaying a UITableView. I want to present a context menu when the user press on a row. I want this to be a semi-transparent view with labels and buttons.
I could use an AlertView, but I want full control on the format of the labels and buttons and will like to use Interface Builder.
So I created my small view 250x290, set the alpha to .75 and create a view controller with the outlets to handle the different user events.
Now I want to present it.
If I use presentModalViewController two (undesired) things happen
1) the view covers all of the screen (but the status bar).
2) It is semi-transparent, but what I see "behind" it its not the parent view but the applications root view.
Ive tried adding it as a subview, but nothing happens, so Im not doing something right:
RestaurantContextVC* modalViewController = [[[RestaurantContextVC alloc] initWithNibName:#"RestaurantContextView" bundle:nil] autorelease];
[self.view addSubview:modalViewController.view];
Is it possible to do what I want?
Thanks in advance.
Gonso
I'm coding similar thing. My approach include.....
Not using dismissModalViewControllerAnimated and presentModalViewController:animated.
Design a customized full sized view in IB. In its viewDidLoad message body, set the background color to clearColor, so that space on the view not covered by controllers are transparent.
I put a UIImageView under the controllers of the floating view. The UIImageView contains a photoshoped image, which has rounded corners and the background is set to transparent. This image view serves as the container.
I uses CoreAnimation to present/dismiss the floating view in the modal view style: (the FloatingViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor clearColor]];
[UIView beginAnimations:nil context:nil];
[self.view setFrame:CGRectMake(0, 480, 320, 480)];
[UIView setAnimationDuration:0.75f];
[self.view setFrame:CGRectMake(0, 0, 320, 480)];
[UIView commitAnimations];
}
wangii
Thats pretty much the solution I found.
I load the view with loadNibNamed and then just add it on top with addSubView, like this:
//Show a view on top of current view with a wait indicator. This prevents all user interactions.
-(void) showWaitView{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WaitView" owner:self options:nil];
#ifdef __IPHONE_2_1
waitView = [ nibViews objectAtIndex: 0];
#else
waitView = [ nibViews objectAtIndex: 1];
#endif
CGFloat x = self.view.center.x - (waitView.frame.size.width / 2);
CGFloat y = self.view.center.y - (waitView.frame.size.height / 2);
[waitView setFrame:CGRectMake(x,y,waitView.bounds.size.width,waitView.bounds.size.height)];
[self.view addSubview:waitView];
}
Could you elaborate on points 3 and 4?
What I did to give the view the round rect aspect is put it inside a round rect button.
This code will actually allow you to have a small floating view, but if the view is smaller that its parent, the user could interact with the visible part of the parent.
In the end I create my view with the same size, but kept the code just in case.
Gonso
I would strongly consider using a navigation controller to slide in your subview instead of overlaying it. This is the expected model and any small benefit you may think you'll get by doing it your own way will be greatly offset by the principle of (least) surprise.
If you really really have to do it this way, I believe the trick is to add the first table view as a subview of a transparent "holding" view that the view controller maintains. Then add your new sub view as another subview of that.
Again, if you really want to do this, instead of adding a transparent "holding" view, since this pop-up is essentially modal, I would make it a subview directly of the window.
You might want to put in a transparent black shield behind it to prevent touches on the background and focus input on the popup.
But seriously, consider either popping a controller on the stack or using that alert view. Unless you've hired a $$ designer, it's probably not going to look appropriate on the iPhone.
What I did was create a UIViewController on top of my UINavigation controller in my app delegate and made it a property of a singleton object for convenience:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- create root navigation controller
self.window.rootViewController = self.navigationController;
//--- create view controller for popups:
popupViewController = [[BaseViewController alloc] init];
popupViewController.view.backgroundColor = [UIColor clearColor];
popupViewController.view.hidden = true; //for rendering optimisation
[self.window addSubview:popupViewController.view];
[AppState sharedInstance].popupViewController = self.popupViewController;
//--- make all visible:
[self.window makeKeyAndVisible];
return YES;
}
At any point in my app, I can then call e.g.
MyViewController * myVC = [[UIViewController alloc] init];
//... set up viewcontroller and its view...
// add the view of the created view controller to the popup view:
[AppState sharedInstance].popupViewController.view.hidden = false;
[[AppState sharedInstance].popupViewController.view addSubview:myVC.view];
The BaseViewController used on the top just inherits from UIViewController and sets up a full-screen view:
//----- in BaseViewController implementation
- (void)loadView {
//------- create root view:
CGRect frame = [[AppState sharedInstance] getScreenFrame];
rootView = [[VCView alloc] initWithFrame:frame];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
}