I have a problematic UIView called as MyListViewController. It is at the first tab in my application.
I am adding a subview in the viewDidLoad section( there are some constraints defined on the subview ). When I run the application the subview doesn't fit to it's root view well. However, if I navigate to an another tab and then re-click to the first tab, the application fits the subview properly.
Also I can't use [self.detailView setTranslatesAutoresizingMaskIntoConstraints:YES]; because it causes heaps of constraint conflicts.
Anyway, I wrote my custom method, patchSubViewPadding, in order to fit the subView to it's rootView which is defined as detailView. As I stated above if I navigate to one of the other tabs and then navigate to the the first tab the application shows up the subview properly.
#interface MyListViewController ()
#property (strong, nonatomic) CustomViewController *customViewController;
#end
#implementation MyListViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
CustomViewController *viewController = (CustomViewController *)[storyboard instantiateViewControllerWithIdentifier:#"customView"];
self.customViewController = viewController;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
[self.detailView addSubview:self.customViewController.view];
}
-(void)viewDidAppear:(BOOL)animated
{
[CommonUtilities patchSubViewPadding:self.detailView subView:self.customViewController.view padding:0];
}
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[CommonUtilities patchSubViewPadding:self.detailView subView:self.customViewController.view padding:0];
}
#end
And this is my custom method:
+(CGRect)patchSubViewPadding:(UIView *)superView subView:(UIView *)subView padding:(float)padding
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
if (padding == 0)
padding = -4.0f;
CGRect frame = superView.bounds;
frame.origin.y = 0;
frame.size.width = frame.size.width - padding;
subView.frame = frame;
return subView.frame;
}
return subView.frame;
}
I tried the following properties either in viewDidLoad or viewWillLayoutSubviews but it didn't work.
[self.detailView setNeedsLayout];
[self.detailView setNeedsUpdateConstraints];
Any ideas? What am I doing wrong?
Move the method call for patchSubViewPadding: out of viewDidAppear and to the bottom of viewDidLoad.
Related
I need to make UIScrollView with UIViewController's inside, every UIViewController has UITableview.
When I tap to UITableview, all UITableViewCells are disappear.
How I initialise UIViewController inside UIScrollView?
- (void)viewDidLoad {
[super viewDidLoad];
int width = 0;
for (int i = 0; i < [self.dishes count]; i++) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Order" bundle:nil];
OneDayDishViewController *myVC = (OneDayDishViewController *)[storyboard instantiateViewControllerWithIdentifier:#"OneDayDishViewController"];
myVC.dishes = self.dishes[i];
myVC.menuTitle = self.menuTitle;
[self.scrollView addSubview:myVC.view];
width += 375;
}
self.scrollView.contentSize = CGSizeMake(width,0);
}
After looking for in google & stack overflow I found this solution
So I done something like this
in my .h file (where I have UIScrollView):
#property (strong, nonatomic) OneDayDishViewController *myVC;
Than in my .m file:
- (void)viewDidLoad {
[super viewDidLoad];
int width = 0;
for (int i = 0; i < [self.dishes count]; i++) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Order" bundle:nil];
self.myVC = [storyboard instantiateViewControllerWithIdentifier:#"OneDayDishViewController"];
self.myVC.dishes = self.dishes[i];
self.myVC.menuTitle = self.menuTitle;
[self.scrollView addSubview:self.myVC.view];
width += 375;
}
self.scrollView.contentSize = CGSizeMake(width,0);
}
Now I also have N elements in my scrollview, if I tap the last element - it's work fine, if I tap any other element - the problem is the same.
So what I do wrong or how I can create (strong) proper dynamically and is it good practice?
Thanks
UPDT:
Thank you #CZ54
my solution is add childviewcontroller so do
[self addChildViewController:myVC];
[self.scrollView addSubview:myVC.view];
[myVC didMoveToParentViewController:self];
instead of
[self.scrollView addSubview:myVC.view];
Adding the subview is not enough.
You have to add the entire controller using
addChildViewController: ( from https://developer.apple.com/documentation/uikit/uiviewcontroller/1621394-addchildviewcontroller )
And you have to retain every instance you are adding.
Change
#property (strong, nonatomic) OneDayDishViewController *myVC;
into
#property (strong, nonatomic) NSMutableArray<OneDayDishViewController*> *myVC;
I have a method that sets the height of a UIScrollView and a UITextView based on the content of the UITextView. I realized that both, textview and scrollview are correctly resized when the method is call from viewWillAppear but not from viewDidLoad (the textview is not correctly resized from viewDidLoad but the scrollview it is).
//In View1
-(void) setHeight
{
NSLog(#"Set height");
CGRect frame = descriptionTextView.frame;
frame.size.height = descriptionTextView.contentSize.height;
descriptionTextView.frame = frame;
if([[UIScreen mainScreen] bounds].size.height == 568) //iPhone 4inch
{
totalHeight = 380+frame.size.height;
[self.mainScrollView setContentSize:CGSizeMake(320,totalHeight)];
}
else{
totalHeight = 250+frame.size.height;
[self.mainScrollView setContentSize:CGSizeMake(320,totalHeight)];
}
}
The problem is that I created a custom tab bar menu with a UIView that's a placeholder. The first time I perform the segue it works, but not if I go back and press the tab again, since viewWillAppear and viewDidAppear are not called. why are not called? How can I force them to be called?
//In CustomTabBarController
-(void) selectTab{
self.isLoaded = YES;
self.activity_indicator.hidden = YES;
if ([self.selectedButton isEqualToString: #"view1"])
[self performSegueWithIdentifier:#"sg_view1" sender:self];
else if ([self.selectedButton isEqualToString:#"view2" ])
[self performSegueWithIdentifier:#"sg_view2" sender:self];
else if ([self.selectedButton isEqualToString:#"view3" ])
[self performSegueWithIdentifier:#"sg_view3" sender:self];
}
The best approach would be to using the tab bar delegate method instead of viewWillAppear:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if ([viewController isKindOfClass:someClass]){
//do your stuff
}
}
I did search first, and have seen somewhat similar issues, but no definitive answer on how to solve it. The problem I'm having with my app is that I have 3 navigation controllers that are added as child view controllers into a UIScrollView. I originally wanted to use a UIPageViewController instead, but that turned out to be a mess since you can't disable the bouncing on it. For each navigation controller, a tableView controller is embedded within it in storyboard.
viewDidAppear and viewDidDisappear are not called for my child view controllers because their views are added as subviews to the paging scroll view at runtime, and I kindof believe is why I can't figure out how to solve this.
I need each of my table view controller's to respond to the user touching the status bar for the respective tableView to scroll to the top.
Here is the code I'm using for the setup of the app's main scroll view and adding the 3 view controller's views to it:
I appreciate any help offered!
MainViewController.h
#import <UIKit/UIKit.h>
#interface MainViewController : UIViewController <UIScrollViewDelegate>
#end
MainViewController.m:
#import "MainViewController.h"
#interface MainViewController ()
#property (nonatomic, strong) UINavigationController *settings;
#property (nonatomic, strong) UINavigationController *hehTwo;
#property (nonatomic, strong) UINavigationController *hehOne;
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#end
#implementation MainViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.settings = [self.storyboard instantiateViewControllerWithIdentifier:#"Settings"];
self.hehTwo = [self.storyboard instantiateViewControllerWithIdentifier:#"HehTwo"];
self.hehOne = [self.storyboard instantiateViewControllerWithIdentifier:#"HehOne"];
[self addChildViewController:self.settings];
[self addChildViewController:self.hehTwo];
[self addChildViewController:self.hehOne];
CGRect hehTwoFrame = self.hehTwo.view.frame;
hehTwoFrame.origin.x = 320;
self.hehTwo.view.frame = hehTwoFrame;
CGRect hehOneFrame = self.hehOne.view.frame;
hehOneFrame.origin.x = 640;
self.hehOne.view.frame = hehOneFrame;
[self.scrollView addSubview:self.settings.view];
[self.scrollView addSubview:self.hehTwo.view];
[self.scrollView addSubview:self.hehOne.view];
// Setting offset so the farthest right controller is heh One
[self.scrollView setContentOffset:CGPointMake(640,0)];
self.scrollView.delegate = self;
self.scrollView.bounces = NO;
self.scrollView.delaysContentTouches = NO;
self.scrollView.contentSize = CGSizeMake(960, self.view.frame.size.height);
self.scrollView.scrollsToTop = NO;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat width = scrollView.frame.size.width;
NSInteger page = (scrollView.contentOffset.x + (0.5f * width)) / width;
if (page == 0)
{
// Settings
}
else if (page == 1)
{
// Heh Two
}
else
{
// Heh One
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
First of all, you need to set self.tableView.scrollsToTop = NO; on HehTwo and HehOne (if these two are initially hidden) in their viewDidLoad implementation.
And now, update this method:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGFloat width = scrollView.frame.size.width;
NSInteger page = (scrollView.contentOffset.x + (0.5f * width)) / width;
if (page == 0)
{
// Settings
((UITableViewController *)self.settings.viewControllers[0]).tableView.scrollsToTop = YES;
((UITableViewController *)self.hehOne.viewControllers[0]).tableView.scrollsToTop = NO;
((UITableViewController *)self.hehTwo.viewControllers[0]).tableView.scrollsToTop = NO;
}
else if (page == 1)
{
// Heh Two
((UITableViewController *)self.settings.viewControllers[0]).tableView.scrollsToTop = NO;
((UITableViewController *)self.hehOne.viewControllers[0]).tableView.scrollsToTop = NO;
((UITableViewController *)self.hehTwo.viewControllers[0]).tableView.scrollsToTop = YES;
}
else
{
// Heh One
((UITableViewController *)self.settings.viewControllers[0]).tableView.scrollsToTop = NO;
((UITableViewController *)self.hehOne.viewControllers[0]).tableView.scrollsToTop = YES;
((UITableViewController *)self.hehTwo.viewControllers[0]).tableView.scrollsToTop = NO;
}
}
That's all! This should fix it.
I have added another viewcontroller (freeform viewController) on the top of mainviewcontroller, but somehow, it is not showing full size. Please look at my code which is below and screenshots.
- (void)viewDidLoad
{
[super viewDidLoad];
firstSurveyViewController = [[FirstSurveyViewController alloc] initWithNibName:#"FirstSurveyViewController" bundle:[NSBundle mainBundle]];
CGRect rect = [firstSurveyViewController.view frame];
rect.origin.x = 10;
rect.origin.y = 20;
[firstSurveyViewController.view setFrame:rect];
[self.view addSubview:firstSurveyViewController.view];
}
Try setting with width and height in addition to the origin, manually. What is the initial value of rect after CGRect rect = [firstSurveyViewController.view frame];?
Also, in general, you don't really want to add viewControllers to other viewControllers unless you're using a UINavigationController or UIViewController Containment to manage them. Seems like in this case firstSurveyViewController should just be firstSurveyView, a subclass of UIView.
I am following Apress Beginning IOS 6 for school where we were asked by the professor to implement a custom algorithm that switches between 3 views (blue, yellow, and green) with each press of the "Next View" button in the toolbar. My approach was to add the subviews at different indices and shuffle the views in the hierarchy in BIDSwitchViewController.m:
#import "BIDSwitchViewController.h"
#import "BIDYellowViewController.h"
#import "BIDBlueViewController.h"
#import "BIDGreenViewController.h"
NSInteger count;
#interface BIDSwitchViewController ()
#end
#implementation BIDSwitchViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.yellowViewController = [[BIDYellowViewController alloc] initWithNibName:#"YellowView"
bundle:nil];
self.greenViewController = [[BIDGreenViewController alloc] initWithNibName:#"GreenView"
bundle:nil];
self.blueViewController = [[BIDBlueViewController alloc] initWithNibName:#"BlueView"
bundle:nil];
//the topmost view is the last one in the stack (2)
[self.view insertSubview:self.blueViewController.view atIndex:2];
[self.view insertSubview:self.yellowViewController.view atIndex:1];
[self.view insertSubview:self.greenViewController.view atIndex:0];
[self.view setBackgroundColor:self.blueViewController.view.backgroundColor];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.blueViewController = [[BIDBlueViewController alloc]
initWithNibName:#"BlueView" bundle:nil];
[self.view insertSubview:self.blueViewController.view atIndex:0];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.;
self.blueViewController = nil;
self.yellowViewController = nil;
self.greenViewController = nil;
}
- (IBAction)switchViews:(id)sender
{
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:
UIViewAnimationTransitionFlipFromRight
forView:self.view cache:YES];
switch (count)
{
case 0:
[self.view sendSubviewToBack:self.blueViewController.view];
[self.view bringSubviewToFront:self.yellowViewController.view];
[self.view setBackgroundColor:self.yellowViewController.view.backgroundColor];
break;
case 1:
[self.view sendSubviewToBack:self.yellowViewController.view];
[self.view bringSubviewToFront:self.greenViewController.view];
[self.view setBackgroundColor:self.greenViewController.view.backgroundColor];
break;
case 2:
[self.view sendSubviewToBack:self.greenViewController.view];
[self.view bringSubviewToFront:self.blueViewController.view];
[self.view setBackgroundColor:self.blueViewController.view.backgroundColor];
break;
}
if (++count >= 3)
{
count = 0;
}
[UIView commitAnimations];
}
#end
Here is the code in BIDAppDelegate.m where the root view controller is added as an instance of BIDSwitchViewController:
#implementation BIDAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.switchViewController = [[BIDSwitchViewController alloc]
initWithNibName:#"SwitchView" bundle:nil];
UIView *switchView = self.switchViewController.view;
CGRect switchViewFrame = switchView.frame;
switchViewFrame.origin.y += [UIApplication
sharedApplication].statusBarFrame.size.height;
switchView.frame = switchViewFrame;
//[self.window addSubview:switchView];
self.window.rootViewController = self.switchViewController;
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
And the BIDSwitchViewController.h header file:
#import <UIKit/UIKit.h>
#class BIDSwitchViewController;
#interface BIDAppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) BIDSwitchViewController *switchViewController;
#end
The app and logic all work as the views switch from blue -> yellow -> green and back to blue again. As shown in the first picture (BIDBlueViewController's BlueView.xib subview of the BIDSubViewController) each of the views overlap the toolbar slightly. I have double and triple checked all of my simulated metrics in IB with the help of one of my classmates:
Am I using poor practice in shuffling the topmost views through the view hierarchy array, instead of removing each view via the book's method of "removeFromParentViewController()," or is their another, hidden cause for the sub views not properly sitting inside / behind the parent view?
Your UIToolbar control's Z-Order on it's view is behind your blue view controller. Since you created it in IB, you can add an IBOutlet for it, and bring it's view to top of the parent view. You're loading the ViewControllers as subviews, so they are all subviews of self.view of your parent.
In your header definition:
IBOutlet *toolBar UIToolbar;
Bring to front in your implementation.
[self.view bringSubviewToFront:toolBar];
You can do this after you've added the blue controller subview or at the end of all your case statements.