I have a project the same menu as this but with submenu that the same slide animation. I create a xib file name it as SecondMenu.xib. The file owner is UIViewController and name it as SecondMenuController. If you check the project REFrosted (check the link), there is DEMOMenuViewController(UIViewController). In method didSelectRowAtIndexPath:. I changed it in my code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < 1; i++) {
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
SecondMenuController *controller = [self.viewControllers objectAtIndex:0];
if ((NSNull *)controller == [NSNull null]) {
controller = [[SecondMenuController alloc] initWithNibName:#"SecondMenu" bundle:nil];
[self.viewControllers replaceObjectAtIndex:0 withObject:controller];
}
controller.view.frame = CGRectMake(-self.view.frame.size.width, 0,self.view.frame.size.width,self.view.frame.size.height);
[self.view addSubview:controller.view];
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
controller.view.frame = CGRectMake(0, 0,self.view.frame.size.width,self.view.frame.size.height);
} completion:^(BOOL finished) {
NSLog(#"Done!");
}];
//[self hideMenu];
}
That code work for me when connecting to submenu(which is SecondMenuController). In my SecondMenuController, I add a button (this will connect to DEMOHomeViewController, and the storyboard identifier is homeController). here's the code of my button
- (IBAction)buttonConnect:(id)sender {
DEMONavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"contentController"];
DEMOHomeViewController *homeViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homeController"];
navigationController.viewControllers = #[homeViewController];
self.frostedViewController.contentViewController = navigationController;
[self.frostedViewController hideMenuViewController];
}
I get this code in REFrosted before I changed it into my code in didSelectRowAtIndexPath: (as what I have mention above in first code). Why is it I get this error
'NSInvalidArgumentException', reason: '*** -[__NSArrayM replaceObjectAtIndex:withObject:]: object cannot be nil'
I import all files. As what I have understood, when you import a files that connect to the file owner. This means I have control for every method or function that inside of that file when you instantiate it into my SecondMenuController(this only based on my experienced, just rectify me if I'm wrong. I just want to know how the system work). Please see my code. Did I miss something here? Hoping for you advised on how can i fix this or explanation why I get this error.
DEMONavigationController *navigationController = [self.storyboard instantiateViewControllerWithIdentifier:#"contentController"];
DEMOHomeViewController *homeViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"homeController"];
navigationController.viewControllers = #[homeViewController];
2 days of searching an answer for this. I finally get it.
The reason why the DEMOHomeViewController is nil when I click the button because SecondMenuController didn't connect to DEMOHomeViewController even I instantiate it to SecondMenuController class. I learn now. Here's the code
first code that i do. I add this SecondMenuController.h
#property (nonatomic, weak) UIStoryboard * myself;
#property (nonatomic, strong) REFrostedViewController * ref;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil myStoryBoard:(UIStoryboard *) xStoryBoard ref: (id) xRef;
In SecondMenuController.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil myStoryBoard:(UIStoryboard *) xStoryBoard ref: (id) xRef
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.myself = xStoryBoard;
self.ref = xRef;
}
return self;
}
In my button
DEMONavigationController *navigationController = [self.myself instantiateViewControllerWithIdentifier:#"contentController"];
DEMOSecondViewController *secondViewController = [self.myself instantiateViewControllerWithIdentifier:#"homeController"];
navigationController.viewControllers = #[secondViewController];
self.ref.frostedViewController.contentViewController = navigationController;
[self.ref.frostedViewController hideMenuViewController];
Now it's working. All you need to do is to call the initWithNibName: method to the DEMOMenuViewController.
The console log means you are attempting to insert nil object in NSArray, which is not allowed. If for some reason you need to add empty object use (NSArray *)[NSNull null] instead.
Related
I have 2 view controllers. viewController1 has a UILabel called nameLabel which I want to set in a different class, viewController2.
I try calling this code from viewController2.
Content1ViewController *viewController1 = [Content1ViewController new];
viewController1.nameLabel.text = #"HELLO";
NSLog(#"%#",viewController1.nameLabel);
However, the viewController1 nameLabel doesn't change when I call the code? Also the NSLog returns "null"?? Can someone tell me why this is happening and also how I can change the nameLabel from a different class? Thanks!
Controls are not initialized when you manually create the instance. This is done on a later stage of view controller life cycle.
If I'm not mistaken, first event where you will see controls initialized is viewDidLoad
Something you can do is adding a NSString property named (let's say) nameLabelText and do nameLabel.text = nameLabelText; on viewDidLoad
You need to take the same instance of viewController which was on the window. If you are pushing viewController2 into navigation controller stack then get instance of viewController1 from navigation stack and then try to set the label.
If you are using present modal then use presentingViewController instance of viewController2 in order to access instance of viewController1 like
viewController1 *controller = [viewController2 presentingViewController];
[controller.nameLabel setText:#"WhatEver"];
Where have you set the property for namelabel?
Did you use Interface Builder (XIB or Storyboard) or have you done it all in code?
If the you did it in InterfaceBuilder than
[Content1ViewController new]
is the wrong approach.
For XIB-Files you should use
initWithNibName:(NSString *)nibName
bundle:(NSBundle *)nibBundle
and if your ViewController has been defined in a storyboard you should use
instantiateViewControllerWithIdentifier:(NSString *)identifier
from UIStoryboard.
But as Claudio Redi already said the earliest point where you will be able to access the UILabel will be in viewDidLoad
If everything in code, you should put
[self setNameLabel:[[UILabel alloc] initWithFrame:CGRectMake(50.0f,50.0f,100.0f,100.0f)];
in the init-Method of your ViewController.
//
// ViewController.m
// UIPageViewControllerDemo
//
// Created by YunYi1118 on 15/5/20.
// Copyright (c) 2015年 Xiaoyi. All rights reserved.
//
#import "ViewController.h"
#import "MoreViewController.h"
#interface ViewController () <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
#property (strong, nonatomic) UIPageViewController *pageController;
#property (strong, nonatomic) NSArray *pageContent;
#end
#implementation ViewController
#synthesize pageContent = _pageContent;
#synthesize pageController = _pageController;
- (void)viewDidLoad {
[super viewDidLoad];
[self createContentPages];
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin] forKey:UIPageViewControllerOptionSpineLocationKey];
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:options];
_pageController.dataSource = self;
_pageController.view.frame = self.view.bounds;
MoreViewController *initialViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];
[self.pageController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:_pageController];
[self.view addSubview:_pageController.view];
}
- (MoreViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (self.pageContent.count == 0 || index >= self.pageContent.count) {
return nil;
}
MoreViewController *dataViewController = [[MoreViewController alloc] init];
dataViewController.dataObject = [self.pageContent objectAtIndex:index];
return dataViewController;
}
- (void)createContentPages
{
NSMutableArray *pageStrings = [NSMutableArray array];
for (int i = 1; i < 11; i++) {
NSString *contentString = [[NSString alloc] initWithFormat:#"Chapter%d, This is the page %d of content displayed using UIPageViewController ",i,i];
[pageStrings addObject:contentString];
}
self.pageContent = [NSArray arrayWithArray:pageStrings];
}
#pragma mark - delegate
- (NSUInteger)indexOfViewController:(MoreViewController *)viewController
{
return [self.pageContent indexOfObject:viewController.dataObject];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(MoreViewController *)viewController];
if (index == 0 || index == NSNotFound) {
return nil;
}
index--;
return [self viewControllerAtIndex:index];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = [self indexOfViewController:(MoreViewController *)viewController];
if (index == NSNotFound) {
return nil;
}
index++;
if (index == self.pageContent.count) {
return nil;
}
return [self viewControllerAtIndex:index];
}
#end
i need to pass data between to Views. I really can't figure out why is not working. Variable selectedRow in SecondViewController is always empty.
FirstViewController.m
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
SecondViewController *newView = [[SecondViewController alloc] init];
newView.selectedRow = [tablelist objectAtIndex:indexPath.row];
newView.title = [self.tableView cellForRowAtIndexPath:indexPath].textLabel.text;
[self.navigationController pushViewController:newView animated:YES];
}
SecondViewController.h
#interface SecondViewController : UIViewController
{
NSString *title,*selectedRow;
}
#property(nonatomic,retain) NSString *title;
#property (nonatomic,retain) NSString *selectedRow;
#end
SecondViewController.m
#synthesize title,selectedRow;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
textView = [[UITextView alloc]initWithFrame:CGRectMake(0, 64, 320, 370)];
textView.text = selectedRow;
textView.editable = FALSE;
[self.view addSubview:textView.text];
}
return self; }
I'm struggling for hours. I really cant figure why.
Any help?
Thank you
The problem is that you are looking at the selectedRow value before you set the property. You can't set the selectedRow property until after the call to init completes.
Since your use of the property is to setup the view controller's view, you should move that code to viewDidLoad where it belongs. As a rule, you should not access self.view in the init method of a view controller.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
textView = [[UITextView alloc]initWithFrame:CGRectMake(0, 64, 320, 370)];
textView.text = selectedRow;
textView.editable = FALSE;
[self.view addSubview:textView.text];
}
I used savestrings to pass data to the next viewcontroller. The only thing you need to do is import the viewcontroller from where the data comes in the viewcontroller.
You can use this in the first viewcontroller
//1e savestring
NSString *saveString1 = emailfield.text;
NSUserDefaults *defaults1 = [NSUserDefaults standardUserDefaults];
[defaults1 setObject:saveString1 forKey:#"saveString1"];
[defaults1 synchronize];
ANd this in the viewcontroller where you need to load data
//First load string
NSUserDefaults *defaults1 = [NSUserDefaults standardUserDefaults];
NSString *loadString1 = [defaults1 objectForKey:#"saveString1"];
[emailfield setText:loadString1];
if you are using storyboard to get a viewcontroller ,you can try this.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIStoryboard *storyboard = [ UIStoryboard storyboardWithName:#"Main" bundle:nil ];
SecondViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"test" ];
vc.title = #"something";
[self.navigationController pushViewController:detailView animated:YES];
}
I have a view controller implementing UIPageViewControllerDataSource delegate, and it contains a UIPageViewController.
My issue is that, after 3 hours of tutorials and reading, I still don't understand why the UIPageController reacts to swipe by moving it's content, but it doesn't change the page if the scroll is enough. It's always stuck on the first page.
So this is my .h file
#import <UIKit/UIKit.h>
#interface BreathePageViewController : UIViewController <UIPageViewControllerDataSource>
#property (strong, nonatomic) UIPageViewController *pageController;
#end
And this is my .m file
#import "BreathePageViewController.h"
#import "FirstViewController.h"
#import "SecondViewController.h.h"
#interface BreathePageViewController () {
NSArray *pageViewControllerScreens;
FirstViewController *firstViewController;
SecondViewController *secondViewController;
int pageIndex;
}
#end
#implementation BreathePageViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
pageIndex = 0;
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];
[self.pageController setViewControllers:#[firstViewController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];
[self.view addSubview:self.pageController.view];
[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
if (pageIndex == 0) {
return nil;
}
pageIndex--;
return [self viewControllerAtIndex:pageIndex];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
if (pageIndex == 2) {
return nil;
}
pageIndex++;
return [self viewControllerAtIndex:pageIndex];
}
- (UIViewController *)viewControllerAtIndex:(NSUInteger)index {
UIViewController *vc;
if (pageIndex == 0 ) {
vc = [[FirstViewController alloc] init];
}
else if (pageIndex == 1) {
vc = [[SecondViewController alloc] init];
}
return vc;
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController {
return 2;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController {
return 0;
}
#end
Now the strange stuff is that the pageViewController:viewControllerAfterViewController is never called.
Can someone help me out?
Thank you
After a night of sleep, I am going to to reply my own question.
I don't know why (any comment appreciated), but the problem was not on that view controller, but it was in the one I was creating it in.
In the parent view controller I was building the UIPageViewController like that:
BreathePageViewController *pageController = [[BreathePageViewController alloc] init];
[pageController.view setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:pageController.view];
And now I tried to move the declaration of pageViewController as an iVar. And it works.
Hope to help someone else
I took the Photo Scroller example from Apple's website and tried to implement my own Album by copying the code. Now, the UIScrollView is not visible. How do I make it appear?
The only code change that I made was in creating the UIPageViewController. In my case, it's a UIViewController that is opening it and not the AppDelegate.
#implementation BasePhotoViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nil bundle:nibBundleOrNil];
if (self) {
// kick things off by making the first page
PhotoViewController *pageZero = [PhotoViewController photoViewControllerForPageIndex:0];
if (pageZero != nil)
{
// assign the first page to the pageViewController (our rootViewController)
//UIPageViewController *pageViewController = (UIPageViewController *) [[UIApplication sharedApplication] keyWindow].rootViewController;
UIPageViewController *pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:0 navigationOrientation:0 options:nil];
//UIPageViewController *pageViewController = (UIPageViewController *)self.parentViewController;
pageViewController.dataSource = self;
[pageViewController setViewControllers:#[pageZero]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
}
}
return self;
}
You are not adding the pageViewController's view as a subview of BasePhotoViewController's view. Your BasePhotoViewController class should look something like this. Note the code in viewDidLoad.
BasePhotoViewController.h:
#interface BasePhotoViewController : UIViewController <UIPageViewControllerDataSource>
#property (nonatomic, strong) UIPageViewController * pageViewController;
#end
BasePhotoViewController.m:
#import "BasePhotoViewController.h"
#import "PhotoViewController.h"
#implementation BasePhotoViewController
#synthesize pageViewController;
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
PhotoViewController *pageZero = [PhotoViewController photoViewControllerForPageIndex:0];
if (pageZero != nil)
{
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:0
navigationOrientation:0
options:nil];
self.pageViewController.dataSource = self;
[self.pageViewController setViewControllers:#[pageZero]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
}
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
# pragma mark - UIPageViewControllerDataSource
- (UIViewController *)pageViewController:(UIPageViewController *)pvc viewControllerBeforeViewController:(PhotoViewController *)vc
{
NSUInteger index = vc.pageIndex;
return [PhotoViewController photoViewControllerForPageIndex:(index - 1)];
}
- (UIViewController *)pageViewController:(UIPageViewController *)pvc viewControllerAfterViewController:(PhotoViewController *)vc
{
NSUInteger index = vc.pageIndex;
return [PhotoViewController photoViewControllerForPageIndex:(index + 1)];
}
#end
Note: I've initialised the UIPageViewController in initWithCoder: because Apple's Photo Scroller code uses a storyboard. I removed the UIPageViewController from the storyboard and created a BasePhotoViewController in its place. If you are not loading BasePhotoViewController from a storyboard, you should move the code in initWithCoder: to the appropriate initialiser.
EDIT: See this sample project on github.
I've followed several guides on passing data between view controllers, but they seem to involve setting the secondViewController's property in any equivalent of a -(void)goToNextView method. I'm trying to figure out how to assign a value to a property in a 2nd view controller from the first while using a tab bar controller created in the app delegate.
Current setup:
AppDelegate.m
#synthesize window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
...
UITabBarController *tbc = [[UITabBarController alloc] init];
FirstViewController *vc1 = [[FirstViewController alloc] init];
SecondViewController *vc2 = [[SecondViewController alloc] init];
[tbc setViewControllers: [NSArray arrayWithObjects: vc1, vc2, nil]];
[[self window] setRootViewController:tbc];
[self.window makeKeyAndVisible];
return YES;
}
...
#end
FirstViewController.h
#import "SecondViewController.h"
#interface FirstViewController : UIViewController
{
SecondViewController *vc2;
IBOutlet UISlider *sizeSlider;
}
#property (nonatomic, retain) SecondViewController *vc2;
#property (strong, nonatomic) UISlider *mySlider;
-(IBAction) mySliderAction:(id)sender;
#end
FirstViewController.m
#import "FirstViewController.h"
#import "SecondViewController.h"
#implementation FirstViewController
#synthesize vc2, mySlider;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
UITabBarItem *tbi = [self tabBarItem];
[tbi setTitle:#"xib"];
}
return self;
}
...
- (IBAction) mySliderAction:(id)sender
{
NSString *str = [[NSString alloc] initWithFormat:#"%3.2f", mySlider.value];
if(self.vc2 == nil)
{
SecondViewController *viewTwo = [[SecondViewController alloc] initWithNibName:nil bundle:nil];
self.vc2 = viewTwo;
}
vc2.sliderValueString = str;
}
...
#end
SecondViewController.h
#import <UIKit/UIKit.h>
#import "myView.h"
#interface SecondViewController : UIViewController
{
NSString *sliderValueString;
}
#property (copy) NSString *sliderValueString;
#end
SecondViewController.m
#import "SecondViewController.h"
#implementation SecondViewController
#synthesize sliderValueString;
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
UITabBarItem *tbi = [self tabBarItem];
[tbi setTitle:#"View 2"];
}
return self;
}
-(void) loadView
{
CGRect frame = [[UIScreen mainScreen] bounds];
myView *view = [[myView alloc] initWithFrame:frame];
printf("Slider Value: %s", [sliderValueString UTF8String]);
[self setView: view];
}
...
#end
myView.h and .m are likely irrelevant to the question, but I've got the .h subclassing UIView, and the .m creating the view with -(id)initWithFrame:(CGRect)frame
I'm guessing that I'm assigning the 2nd VC's property in the wrong place (mySliderAction), given that the printf() in the 2nd VC is blank, so my question is: Where should the property be assigned, or what am I doing wrong that is preventing the 2nd VC from allowing it's sliderValueString to not be (or remain) assigned?
Thanks!
You are fine setting the property in mySliderAction, the trouble is that you are setting it on the wrong object.
In the App Delegate you create a viewController for tabControllers' item 2:
SecondViewController *vc2 = [[SecondViewController alloc] init];
In vc1's mySliderAction you also create an instance of vc2:
SecondViewController *viewTwo = [[SecondViewController alloc] initWithNibName:nil bundle:nil];
This is not the instance that you navigate to via the second tab on the tabController, but it is the one who's sliderValueString property you are setting.
Instead of making a new instance here, you need to refer to the already-existing instance:
SecondViewController *viewTwo = [self.tabBarController.viewControllers objectAtIndex:1]
Then you should be ok.