So I have a scroll view with 3 other views as subviews. Using the scroll view to page between the 3 subviews which have their controllers stored in an array.
- (void)viewDidLoad
{
[self loadData];
self.uiScrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
[self.uiScrollView setPagingEnabled:YES];
[self.uiScrollView setContentSize:CGSizeMake(self.view.frame.size.width*3, self.view.frame.size.height)];
self.uiScrollView.delegate = self;
[self.view addSubview:self.uiScrollView];
self.uiPhotoCollectionViewControllers = [[NSMutableArray alloc] init];
int numberOfOtherViews = 3;
int subViewOffset = 0;
for (int i = 0; i < numberOfCollectionViews; i++)
{
UIViewController* uiViewController = [[UIPhotoCollectionViewController alloc] initWithData:dataArray];
// add offset to the view of the controller
[self.uiScrollView addSubview:uiViewController.view];
[self.uiViewControllers addObject:uiViewController];
uiViewController.view.frame = CGRectOffset(uiViewController.view.frame,subViewOffset,0.0f);
subViewOffset = subViewOffset + uiViewController.view.frame.size.width;
}
If I later wanted to switch the view in the middle of the scroll view with the first one, how would I go about this without removing all teh scroll views subviews and replacing them.
Not possible without replacing, but you can do this by swaping the objects in self.uiViewControllers array like this
[scrollView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
and then after swaping the objects you can add subview by traversing the whole array.
Related
In the parent view controller, I add a child view controller when the view loads:
// In the parent
- (void) viewDidLoad
{
[super viewDidLoad];
self.tokenFieldViewController = [[TokenFieldViewController alloc] init];
[self addChildViewController:self.tokenFieldViewController];
[self.view addSubview:self.tokenFieldViewController.view];
self.tokenFieldViewController.view.frame = self.view.bounds;
[self.tokenFieldViewController didMoveToParentViewController:self];
}
In essence, this child view controller has a text field in its view:
// In the child
- (void) loadView
{
self.view = [[UIView alloc] init];
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(0,0,99,99)];
[self.view addSubview:self.textField];
}
When coded like this, the text field is not tappable. That is, tapping it does not put a blinking cursor into it. However, when I add the child view controller in the parent's viewDidAppear, the text field starts working. I would like to know how to fix it for viewDidLoad, because I need the lifecycle of the child to match that of the parent.
I would suspect that your only problem with the tapping is this line:
self.tokenFieldViewController.view.frame = self.view.bounds;
Between viewDidLoad and viewDidAppear there are a number of things going on. The biggest of which is the measurement of views and subviews. This happens between viewWillLoadSubviews and viewDidLoadSubviews. You should not count on valid sizes until after viewDidLoadSubviews.
Override viewDidLoadSubviews and put your line in it, like this:
- (void)viewDidLayoutSubviews {
self.tokenFieldViewController.view.frame = self.view.bounds;
}
Using SDK 6.1, Xcode 4.6.1, I make a new project Master-Detail iOS App, ARC, no storyboards.
Then in the DetailViewController, in the viewDidLoad I add two UITableViews contained in UIViewControllers and make sure the second one is hidden like this:
- (void)viewDidLoad
{
[super viewDidLoad];
UIViewController *lViewController1 = [[UIViewController alloc] init];
UITableView *lTableView1 = [[UITableView alloc] initWithFrame: self.view.frame];
lTableView1.scrollsToTop = YES;
[lViewController1.view addSubview: lTableView1];
lTableView1.dataSource = self;
[self.view addSubview: lViewController1.view];
[self addChildViewController: lViewController1];
UIViewController *lViewController2 = [[UIViewController alloc] init];
UITableView *lTableView2 = [[UITableView alloc] initWithFrame: self.view.frame];
lTableView2.scrollsToTop = YES;
[lViewController2.view addSubview: lTableView2];
lTableView2.dataSource = self;
[self.view addSubview: lViewController2.view];
[self addChildViewController: lViewController2];
// now hide the view in view controller 2
lViewController2.view.hidden = YES;
}
(I make sure the DetailViewController is a datasource that returns 100 rows of UITableViewCells with the textLabel.text set to #"hello")
The presence of the second view controller makes that scrollsToTop (tapping on the status bar) does not work anymore. If I do not use UIViewController containment and just add two UITableViews and set the second one to be hidden, scrollsToTop does work.
What am I doing wrong?
scrollsToTop only works on a single visible view. From the documentation:
This gesture works on a single visible scroll view; if there are multiple scroll views (for example, a date picker) with this property set, or if the delegate returns NO in scrollViewShouldScrollToTop:, UIScrollView ignores the request. After the scroll view scrolls to the top of the content view, it sends the delegate a scrollViewDidScrollToTop: message.
You could try calling [tableView setContentOffset:CGPointZero animated:YES] on each of your table (or scroll) views manually instead. To do this, implement the scrollViewShouldScrollToTop: method in the UIScrollViewDelegate protocol:
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
[lTableView1 setContentOffset:CGPointZero animated:YES];
[lTableView2 setContentOffset:CGPointZero animated:YES];
return NO;
}
You can only set 1 ScrollView per ViewController with property .scrollsToTop = YES.
If you set 2 scrollview.scrollsTopTop = YES, it will simply stop functioning.
ie: your sample project (DetailViewController.m) update following lines,
line48: lTableView1.scrollsToTop = YES;
line56: lTableView2.scrollsToTop = NO;
then, scrollsToTop works correctly. If there are more than 1 scrollview you wish to concurrently setScrollsToTop, keep digging around. good luck!
I am currently experimenting with your project. When
lViewController2.view.hidden = YES;
is replaced with
lTableView2.hidden = YES;
then the scrolling works, even with controller containment.
I tried to insert a view between the controller's view and the table and then hide this view, but the table was not scrolling.
I tried to hide the controller by experimenting with shouldAutomaticallyForwardAppearanceMethods but the table was not scrolling.
Result: From my experiments, only one scroll view must be visible in the view hierarchy and the hidden property of the parent views is not checked out. hidden must be set to NO on all other scroll views, not their parent views.
After testing several options and various hits and try I finally settled to one final solution, i.e. setBounds: of scrollView (that is tableView in your case) and it works good. You'll have to put extra effort for animation although.
CGRect frame = scrollView.frame;
frame.origin.x = 0;
frame.origin.y = 0;
[scrollView setBounds:frame];
By the way in your case, try returning YES to
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView;
Although if not defined, assumes YES.
I have used this and now it works fine.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIViewController *lViewController1 = [[UIViewController alloc] init];
UITableView *lTableView1 = [[UITableView alloc] initWithFrame: self.view.frame];
lTableView1.scrollsToTop = YES;
[lViewController1.view addSubview: lTableView1];
lTableView1.dataSource = self;
[self.view addSubview: lViewController1.view];
[self addChildViewController: lViewController1];
lTableView1.tag=1;
UIViewController *lViewController2 = [[UIViewController alloc] init];
UITableView *lTableView2 = [[UITableView alloc] initWithFrame: self.view.frame];
lTableView2.scrollsToTop = NO;
[lViewController2.view addSubview: lTableView2];
lTableView2.dataSource = self;
[self.view addSubview: lViewController2.view];
[self addChildViewController: lViewController2];
lTableView2.tag=2;
// now hide the view in view controller 2
lViewController2.view.hidden = YES;
}
- (NSUInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSUInteger)section {
return 50;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * const kCellIdentifier = #"MyCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:#"hello %d %d",indexPath.row, tableView.tag];
return cell;
}
I am dealing with a project where I have a horizontal paged table view and it is connected to a sqlite3 database. I have tried many methods to create the horizontal tableview and worked nice but when I pass the objects to table view to be displayed on the cells, the app crashes.
I got error that I googled and found that table view is accessing an object from the array that did not exist.
for(int i = 0; i < colors.count; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UITableView *tableView = [[UITableView alloc] initWithFrame:frame];
self.tableView = tableView;
tableView.dataSource = self;
[self.scrollView addSubview:tableView];
And in - (void)scrollViewDidScroll:(UIScrollView *)sender I am passing the array's objects to a particular page when the page is scrolled.
Are there any other good methods to do this?
i think you should use NRGridView, because as per my understanding you have multiple tableview in scrollview, that why tableview delegate method going to confused & your app gone crashed.
I have created one scroll view and on the scroll view, i am adding two alternate custom UIView s
#interface FirstView : UIView
#interface SecondView : UIView
my array is
NSMutableArray *classArray= [[NSMutableArray alloc] initWithObjects:firstView, secondView, firstView, secondView, nil];
i am adding views on scroll view in following manner
for ( i = 0 ; i< 5; i++)
{
UIView *userView = [classArray objectAtIndex:i];
CGRect rect;
rect.size.height = KVIEWHGT;
float userViewWidth = userView.frame.size.width;
rect.size.width = userViewWidth;
userView.frame = rect;
[scrollView addSubview:userView];
[userView release];
}
UIView *userView = nil;
NSArray *userSubViews = [scrollView subviews];
CGFloat x = 0;
float userViewWidth = 0.0f;
for (userView in userSubViews) {
if ([userView isKindOfClass:[UIView class]]){
CGRect frame = userView.frame;
frame.origin = CGPointMake(x, 0);
userView.frame = frame;
userViewWidth = userView.frame.size.width;
x += userViewWidth + 10;
}
}
[scrollView setContentSize:CGSizeMake(x, scrollView.frame.size.height)];
I am getting output as only two custom views on scroll view but i want there should be 5 custom view on the scroll views
Please help me to solve this problem.
You are adding firstView ,secondView multiple time in the array, even though you add it multiple time it refers to the same instance
NSMutableArray *classArray= [[NSMutableArray alloc] initWithObjects:firstView, secondView, firstView, secondView, nil];
A UIView can only have one parent view. You are using the same UIView instance multiple times. If you add a UIView to another, it is removed from the one it is currently attached to.
Read UIView Class Reference, addSubview:
Views can have only one superview. If view already has a superview and that view is not the receiver, this method removes the previous superview before making the receiver its new superview.
even if it works, you would have duplicated views in your scrollview. you have to create x instances of you views and add them to your scrollview.
thats kind of creepy code anyway.
1.
UIView *userView = [classArray objectAtIndex:i];
[userView release];
you dont have to release this, because its not yours!
2: why iterating twice over the views? makes no sense, better
NSMutableArray* myArray = [[NSMutableArray alloc] initWithObjects: firstView,secView,thirdView,fourthView];
for (UIView* view in myArray)
{
//set the Frame and add it the the ScrollView
}
I want to remove the current subview (added atIndex:0) from my root view controller and insert a new view in its place.
Currently I can only find code snippets that show unloading a view using removeFromSuperView, which requires you to know what viewcontrollers view is currently loaded.
The only other thing to note is that I have another subview inserted in the rootcontrollers view which i dont want unloaded. So code that removes any and all subviews is not adequate
Thanks,
m
if(self.firstscr == nil)
{
firstscreen *f = [[firstscreen alloc] initWithNibName:#"firstscreenview" bundle:nil];
self.firstscr = f;
[f release];
}
///This is my attempt at getting to the currently loaded view :P
[[self.view subviews atIndex:0].view removeFromSuperView];
[self.view insertSubview:firstscr.view atIndex:0];
Here is how to do it (i figured it out, Yey me!)
if(self.firstscr == nil)
{
firstscreen *f = [[firstscreen alloc] initWithNibName:#"firstscreenview" bundle:nil];
self.firstscr = f;
[f release];
}
//Remove whatever view is currently loaded at index 0, this index is only to be used by "page" views
UIView *v = [self.view.subviews objectAtIndex:0];
[v removeFromSuperview];
[self.view insertSubview:firstscr.view atIndex:0];