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.
Related
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];
}
I have added a subview over my UITableView using:
TransparentViewController *tvc =
[[TransparentViewController alloc]
initWithNibName:#"TransparentViewController" bundle:nil];
[self.view addSubview:tvc.view];
My Nib has a UIImageView in it that has some text and a transparent background.
When I load the detailView for the table for the first time I show the subview that gives a brief explanation of the information that you can see below the text. Works really well.
What i would like to do is alter the alpha of the underlying table so that it is dimmer but not affect the alpha of the overlay subview. If i use:
[self.view setAlpha:(CGFloat)];
It dims the overlay as well. I seem to be having a mental block.
Changing the alpha affects the subviews as well. Your tvc.view is a subview of self.view, so it is naturally going to be affected.
Why don't you try this: put another view in tvc.view and send this view to this view to the back.
(UIView*) back = [[UIView alloc] initWithFrame:CGFrameMake(...)];
back.backgroundColor = [UIColor grayColor]; // choose a color that you like;
back.alpha = 0.5; // whatever works for you
[tvc.view addSubview:back];
[tvc.view sendSubviewToBack:back];
Set the size and alpha of this new view to something you like. The table view will show through it to a limited extent, which may accomplish what you are trying to do.
Since this is part of our tvc view, it will appear when you show that view and go away when you hide that view.
I don't know if this is possible, but what I would like to do is add a subview several times to the view. I have tried something like this:
[self.view addSubview: newView];
newView.center = CGPointMake(160, 100);
[self.view addSubview: newView];
newView.center = CGPointMake(160, 200);
[self.view addSubview: newView];
All this does is move newView around, without adding new ones. Any ideas?
I also tried this:
[self.view addSubview:newView];
UIView *anotherView = newView;
anotherView.center = CGPointMake(160, 100)
[self.view addSubview:anotherView];
Edit
Here's a solution I've learned with time
Another way to solve the problem would be to make a separate nib containing the view and add instances of the nib several times. A good template to work off to implement this solution is to do it the same way a custom UITableViewCell is used in the cellForRowAtIndexPath method.
A view can only be contained in a single parent view's hierarchy. As soon as you add it to a new one, it is removed from the previous one. In this case, it is being removed and added back to the same view's hierarchy. You would need to make a copy of the sub-view to have it appear multiple times.
#Jonah pointed me to the following explanation he has provided:
Question on View Controller's view
I currently have a MainViewController whose main(root?) view contains an Add Record UIButton which is linked to the following method and is dependent on which page the user currently has open in a UIScrollView:
#pragma mark -
#pragma mark Add View Support
- (void)add:(id)sender {
MyAddViewController *controller = nil;
// Dynamically creating and allocating the Add View Controller
if (dataViewPageControl.currentPage == 0) {
controller = [[MyAddViewController alloc] initWithAddType:1];
} else {
controller = [[MyAddViewController alloc] initWithAddType:0];
}
controller.delegate = self;
// Slide the new AddViewController's view in from the right
CGPoint initialCenter = self.view.center;
controller.view.center = CGPointMake(initialCenter.x + 320, initialCenter.y);
[self.view addSubview:controller.view];
[UIView beginAnimations:#"animation" context:NULL];
[UIView setAnimationDuration:0.35];
[UIView setAnimationDelegate:controller];
controller.view.center = CGPointMake(initialCenter.x, initialCenter.y);
[UIView commitAnimations];
}
MainViewController is the delegate for MyAddViewController (which has a CANCEL/CLOSE button) and when it's close button is clicked the MyAddViewController is released through the close method in this MainViewController.
As you can see, MyAddViewController's view becomes a subview to the current root view (Bad? Bad? Bad?) MyAddViewController's view is as a FULL size view (320x416) but whose top half is transparent, so that MainViewController's view can be seen below on the top half and the bottom half contains the AddViewController's view and textfields etc....
Based on what I've read from #Jonah just now at the link at the very top this sort of design is BAD, BAD, BAD. I've noticed when testing in Instruments that MyAddViewController sometimes lags when sliding in. I'll click the ADD button to fire this method and there is sometimes a 3 - 5 sec delay before the view slides in. Some sort of delay in all the memory management and cleanup?
I'm assuming I can fix this by making a single MyAddViewController property inside MainViewController which will then prevent me from allocating and deallocating on every button click. That should make things smoother? My only worry is that this will cause a greater memory imprint at application load time. Instruments seems to show a 516KB allocation every time I click ADD for the first time in my app.
Any recommendations though on design and how to improve this? The reason I like having separate view controllers with nested views like this is because MyAddViewController has a lot of logic with Core Data and such and MainViewController is pretty busy enough already as it is. Trying to keep things modular.
Thanks in advance!
I think the best solution is to redesign your MyAddViewController to be a controller which inherits from NSObject rather than UIViewController. The MyAddViewController can then manage a subview of your MainViewController allowing you to keep your controller logic nicely encapsulated without abusing UIViewController.
I've tried to describe why nesting the views of multiple custom UIViewController classes is problematic here: http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/
An add view controller is the perfect type of thing to use modal presentation for. Instead of handling all that animation yourself, just present it like this:
[self presentModalViewController:controller animated:YES];
Edit
Then I would recommend making MyAddViewController a property on MainViewController and write a custom accessor. Additionally I would defer setting it's addType (if possible) until the point at which you will actually animate the view in. This way we can just have one generic MyAddViewController hanging around, instead of having to create a new one every time the detailPageControl changes.
- (MyAddViewController *)addViewController
{
if (!addViewController)
addViewController = [[MyAddViewController alloc] init];
}
Then before you animate it...
self.addViewController.addType = 0;
if (dataViewPageControl.currentPage == 0) {
self.addViewController.addType = 1;
}
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;
}