UITableView not interacting after modyifing the bounds on a UIModalPresentationCustom - uitableview

Basically, I am trying to present a custom modal view with a specific size and position since I was able to manage this through popover on iOS 7. But on iOS it seems they change things.
I manage to achieve this with the following:
Presentation:
UpViewController *upViewController = [[UpViewController alloc] init];
upViewController.delegate = self;
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:upViewController];
navigationController.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController:navigationController animated:YES completion:nil];
In UpViewController.m:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
self.navigationController.view.superview.bounds = CGRectMake(-350, -30, 320, screenSize.height-(44+20));
}
My problem is after my modal view has been presented. We cannot interact with the UITableView that has been added to the view. Anyone has encounter this problem?

Seems I found my own solution
instead of setting the bounds, I set the navigationController.view.frame:
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
self.navigationController.view.frame = CGRectMake(screenSize.width-320, 44, 320, screenSize.height-(44));
}

Related

How to switch between tabs and directly show pushed view controller?

This is my code:
VideoDetailViewController *VideoDetailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"VideoDetailViewController"];
UINavigationController * navigationController = (UINavigationController *) [[self tabBarController] selectedViewController];
[self.tabBarController setSelectedIndex:0];
[navigationController pushViewController:VideoDetailVC animated:YES];
I am writing this code in tabbar's second index. I want to directly go to the Video Detail screen, which is child view controller of tabbar's zero index. Everything works fine but what I see is: it shows a parent view controller which is tabbar's zero index. After a second it pushes to the Video Detail screen. I just don't want to show the zero index. What is the solution? Help will be appreciated.
EDITED:
#import "TWPhotoCollectionViewCell.h"
#implementation TWPhotoCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.imageView = [[UIImageView alloc] initWithFrame:self.bounds];
self.imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.imageView.layer.borderColor = [UIColor blueColor].CGColor;
[self.contentView addSubview:self.imageView];
}
return self;
}
- (void)setSelected:(BOOL)selected {
[super setSelected:selected];
self.imageView.layer.borderWidth = selected ? 2 : 0;
}
#end
Replace below line
[navigationController pushViewController:VideoDetailVC animated:YES];
With
[navigationController pushViewController:VideoDetailVC animated:NO];
It may solve your issue.

UINavigationBar with UISegmentedControl partially covers childViews

I have read many other threads on this and the Apple docs, but haven't found a solution yet for my particular problem.
My app uses a UITabBarController as the rootViewController, and in one of the tabs I have a UISegmentedControl in the navigationBar to switch between three child UITableViewControllers.
(In the real app two of the childVCs are a custom UIViewController, I'm just using three UITableViewControllers for the sample app).
The segmentedControl setup and the switching all works fine. The thing that goes wrong is that only the first UITableViewController is shown correctly. For the second and third one, part of the first cell is hidden under the navigationBar. When I click through all three, the first one is still ok.
I have made a little sample app to show what's going on, using very bright colors for demonstration purposes: https://www.dropbox.com/s/7pfutvn5jba6rva/SegmentedControlVC.zip?dl=0
Here is also some code (I'm not using storyboards):
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
FirstViewController *fvc = [[FirstViewController alloc] init];
UINavigationController *firstNavigationController = [[UINavigationController alloc] initWithRootViewController: fvc];
SecondViewController *svc = [[SecondViewController alloc] init];
UINavigationController *secondNavigationController = [[UINavigationController alloc] initWithRootViewController: svc];
// Initialize tab bar controller, add tabs controllers
UITabBarController *tabBarController = [[UITabBarController alloc] init];
tabBarController.viewControllers = #[firstNavigationController, secondNavigationController];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
// FirstViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.title = #"One";
self.view.backgroundColor = [UIColor orangeColor];
UITableViewController *vc1 = [[UITableViewController alloc] init];
UITableViewController *vc2 = [[UITableViewController alloc] init];
UITableViewController *vc3 = [[UITableViewController alloc] init];
vc1.view.backgroundColor = [UIColor redColor];
vc2.view.backgroundColor = [UIColor blueColor];
vc3.view.backgroundColor = [UIColor greenColor];
self.viewControllers = #[vc1, vc2, vc3];
self.segmentTitles = #[#"Red", #"Blue", #"Green"];
self.segmentedControl = [[UISegmentedControl alloc] initWithItems: self.segmentTitles];
[self.segmentedControl addTarget: self
action: #selector(segmentClicked:)
forControlEvents: UIControlEventValueChanged];
self.navigationItem.titleView = self.segmentedControl;
self.segmentedControl.selectedSegmentIndex = 0;
// set the first child vc:
UIViewController *vc = self.viewControllers[0];
[self addChildViewController: vc];
vc.view.frame = self.view.bounds;
[self.view addSubview: vc.view];
self.currentVC = vc;
}
- (void)segmentClicked:(id)sender
{
if (sender == self.segmentedControl)
{
NSUInteger index = self.segmentedControl.selectedSegmentIndex;
[self loadViewController: self.viewControllers[index]];
}
}
- (void)loadViewController:(UIViewController *)vc
{
[self addChildViewController: vc];
[self transitionFromViewController: self.currentVC
toViewController: vc
duration: 1.0
options: UIViewAnimationOptionTransitionFlipFromBottom
animations: ^{
[self.currentVC.view removeFromSuperview];
vc.view.frame = self.view.bounds;
[self.view addSubview: vc.view];
} completion: ^(BOOL finished) {
[vc didMoveToParentViewController: self];
[self.currentVC removeFromParentViewController];
self.currentVC = vc;
}
];
}
So obviously my question is, why does this happen, and what can I do to fix it?
Edit: adding screenshots.
EDIT: Based on the answer below I changed the code in the animation block to:
[self.currentVC.view removeFromSuperview];
if ([vc.view isKindOfClass: [UIScrollView class]])
{
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(self.topLayoutGuide.length, 0, self.bottomLayoutGuide.length, 0);
[UIView performWithoutAnimation: ^{
vc.view.frame = self.view.bounds;
((UIScrollView *)vc.view).contentInset = edgeInsets;
((UIScrollView *)vc.view).scrollIndicatorInsets = edgeInsets;
}];
}
else
{
vc.view.frame = self.view.bounds;
}
[self.view addSubview: vc.view];
Now it works. I'm going to try this with a custom UIViewController as well.
The issue is that you do not set the correct content inset to each table view. The system attempts to do it for you, but I guess your setup is too complex for it, and it only does it for the first tableview that is loaded in viewDidLoad. In your loadViewController: method, when replacing the currently displayed view, make sure to set both the contentInset and scrollIndicatorInsets to the values of the previous view. I think the system will manage to set the correct insets later, in case you rotate to landscape. Try it. If it doesn't, you will need to do it on your own in viewDidLayoutSubviews.

Programmatically create a UINavigationController with a UICollectionViewController embed in a container

What I'd like to do is create a navigation controller based on a layout. Therefor I created a method renderLayout in my UIStoryboard class.
renderLayout:
UINavigationController *navigationController = [[UINavigationController alloc] init];
NSMutableArray *viewControllers = [[NSMutableArray alloc] init];
/* navigation bar styling */
// navigationController.toolbar.translucent = false;
// navigationController.title = #"TEST";
UIViewController *controller = [[UIViewController alloc] init];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
scrollView.contentSize = CGSizeMake(1024, /* some calculated value here */);
/* controller styling */
//controller.title = [page valueForKey:#"title"];
NSArray *items = /* ... */
int containerHeight = (1 + ([items count] / 3)) * 29;
MultiSelectionViewController *multiSelection = [[MultiSelectionViewController alloc] initWithItems:[data valueForKey:#"items"]];
multiSelection.view.frame = CGRectMake(20, 65, 984, containerHeight);
/* for debugging */
//multiSelection.view.backgroundColor = [UIColor redColor];
[scrollView addSubview:multiSelection.view];
[controller addChildViewController:multiSelection];
[multiSelection didMoveToParentViewController:controller];
[view addSubview:scrollView];
[controller.view addSubview:view];
[viewControllers addObject:controller];
[navigationController pushViewController:controller animated:true];
return navigationController;
The MultiSelection is a UICollectionViewController<UICollectionViewDataSource> kind of class that creates a collection of items which can be selected.
MultiSelectionViewController init method:
- (id)initWithItems:(NSArray*)items
{
self = [super init];
if (self) {
self.items = items;
UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init];
self.view = [[UICollectionView alloc] initWithFrame:CGRectMake(20, 0, 984, 29) collectionViewLayout:layout];
self.collectionView.dataSource = self;
}
return self;
}
After a uses pushes a button on that current UIViewController the method gets called. It should then construct the navigation controller which I return and later load with
[self presentViewController:renderedNavController animated:true completion:^(){}];
So far everything is working quite well. The one problem I have is that It displays all kinds of labels and buttons and stuff that I can directly push onto the scrollView but it fails to load the MultiSelection elements. It displays a red rectangle in the size of the view frame but viewDidLoad never gets called inside the MultiSelectionViewController. Can someone help me with this? I've already done a lot of google research but couldn't find any solution yet. I hope I explained my problem thoroughly, if not, ask away :)
Okay I finally found the answer. In order to have the viewDidLoad method called I have to provide a layout and init the view controller with it.
Hence self = [super init]; is wrong, the correct way to do it is self = [super initWithCollectionViewLayout:layout
On a side note, I had to use UICollectionViewFlowLayout (instead of UICollectionViewLayout) and use the method setItemSize to provide the correct size of the cells.

UISearchBar animation issue

I have a UIViewController in which I want to show a tableview with the serchBar.
//viewDidLoad
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0,
0,
SCREEN_WIDTH(),
SCREEN_HEIGHT())
style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
[self.view addSubview:_tableView];
// adding uisearch bar
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
_tableView.tableHeaderView = searchBar;
//
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
The issue happens when I click inside the uisearch bar so that the animation starts and it looks like it has a 20px unwanted offset.
In your Storyboard, select the problematic controller, look at the Attributes tab and try to edit these settings:
Under Top Bars
Under Opaque Bars
I've solved a similar problem by unflagging these settings.
I found what is causing this issue. Seems that the animation gets messed up when you set navigationBar.translucent to NO. If you make your navigationBar translucent, everything should work fine, but this is definitely not an ideal solution. I'm going to try and find a workaround.
UITableView *_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0,
64,
self.view.frame.size.width,
self.view.frame.size.height)
style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
[self.view addSubview:_tableView];
// adding uisearch bar
UISearchBar* searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
_tableView.tableHeaderView = searchBar;
//
UISearchDisplayController* searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
and I just embade my controller with UINavigationcontroller and its working quite well..
You can cancel animation by subclassing UISearchDisplayController and adding this:
- (void)setActive:(BOOL)visible animated:(BOOL)animated
{
if(self.active == visible) return;
[self.searchContentsController.navigationController setNavigationBarHidden:YES animated:NO];
[super setActive:visible animated:animated];
[self.searchContentsController.navigationController setNavigationBarHidden:NO animated:NO];
if (visible) {
[self.searchBar becomeFirstResponder];
} else {
[self.searchBar resignFirstResponder];
}
}
codyko gave me an idea. It was the because the navigation bar was no translucent. So I set it to translucent on this view controller and rest when I left with the following code:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.navigationController.navigationBar.translucent = NO;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.navigationBar.translucent = YES;
}
Now that left the navigation bar on this controller slightly faded, so I added a UIView the same color as my navbar just behind it to make it look opaque. I know it isn't perfect but it works nicely.
As a reminder for anyone with similar issues. I needed to add this line that fixed things:
self.edgesForExtendedLayout = UIRectEdgeNone;
Why are you creating the searchBar programmatically instead of in the StoryBoard ?
I'm currently using searchBar, added in storyboard and it's work fine ( I have to change the contentOffset )
I have applied your code,, It works fine for me,, Just hide you navigation bar and start the search bar from y = 20, instead of y = 0;

top bar in UIPageViewController

I based my application with the default "Page-based App" in Xcode. I'm trying to display a navigation bar on top of my DataViewController in my storyboard without using a navigation controller. The problem is, when I swipe left and right, the navigation bar seems to go along with the gesture. How can I make it "not-movable"?
Things I tried:
Putting the navigation bar in my RootViewController instead. When I simulate the app, the navigation bar is not visible.
EDIT:
RootViewController.m
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Configure the page view controller and add it as a child view controller.
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageViewController.delegate = self;
GJDataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
self.pageViewController.dataSource = self.modelController;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
CGRect pageViewRect = self.view.bounds;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
pageViewRect = CGRectInset(pageViewRect, 40.0, 40.0);
}
self.pageViewController.view.frame = pageViewRect;
[self.pageViewController didMoveToParentViewController:self];
// Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
}
DataViewController.m
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.dataLabel.text = [self.dataObject description];
}
Follow these steps:
Use your 2nd trial: "Putting the navigation bar in my RootViewController instead." to make navigation-bar immovable with every age transition.
Now in RootViewController.m, change the line CGRect pageViewRect = self.view.bounds; to CGRect pageViewRect = CGRectMake(self.view.bounds.origin.x, 64, self.view.bounds.size.width, self.view.bounds.size.height-64); .Where 64=20(status-bar height) + 44(navigation-bar) height.
2nd step will adjust frame of your DataViewController's view below added navigation-bar in RootViewController and thus navigation-bar will not hide.
Hope, it helps you....Happy Coding! :-)

Resources