Im building an app that calls different subviews and lay them over in a main view controller (http://imgur.com/p6l9Oac)
when ever the user clicks a button on the bottom part of the screen (lets call it sliding menu!) the view behind it will disappear and new one will show up.
one of the subviews is Settings , which it have some switches to enable/disable some of the buttons.
in the SettingsViewController.
Ive set a protocol:
#protocol SettingsViewControllerDelegate <NSObject>
#required
-(void)hideCountdownView;
-(void)showCountdownView;
#end
and the interface contains:
#interface SettingsHubViewController : UIViewController
#property (weak, nonatomic) IBOutlet UISwitch *enableCountdown;
#property (nonatomic, assign) id <SettingsViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UIView *mainView;
#end
and in the Implementation:
- (IBAction)switchAction:(id)sender {
if (!self.enableCountdown.on) {
NSLog(#"The view is Hidden");
[_delegate hideCountdownView];
} else if (self.enableCountdown.on) {
NSLog(#"The view is Shown");
[_delegate showCountdownView];
}
}
You can see i used _delegate to use the show and hide functions, I used NSLog to make sure that Im calling the functions correctly.
in the MainViewController
#import "SettingsHubViewController.h"
#interface MainViewController () <SettingsViewControllerDelegate>
#property (nonatomic, strong) SettingsHubViewController * settingsViewController;
and the Implementation
#implementation MainViewController
-(void)showCountdownView {
self.slidingMainMenuViewController.countdownView.hidden = NO;
NSLog(#"Showing Countdown");
}
-(void)hideCountdownView {
self.slidingMainMenuViewController.countdownView.hidden = YES;
NSLog(#"Hiding Countdown");
}
-(void)viewDidLoad {
[super viewDidLoad];
self.settingsViewController.delegate = self;
self.slidingMainMenuViewController.delegate = self;
}
the problem is that the NSLogs above is not being called at all, can any one help me ?
Thanks
UPDATE: Since i have more that 20 different views that needs to be called, i created this method
- (UIView *) getPresentedMenu:(NSString *) menuIdentifer withMenuTag:(int) menuTag withAViewController:(UIViewController*) menuViewController andMenuDelegate:(id) menuDelegate {
menuViewController = [[UIStoryboard storyboardWithName:#"Main" bundle:Nil] instantiateViewControllerWithIdentifier:menuIdentifer];
menuViewController.view.tag = menuTag;
if (self.viewBeingCalledBySwipe == NO) {
menuViewController.view.frame = CGRectMake(0, menuViewController.view.frame.size.height, menuViewController.view.frame.size.width, menuViewController.view.frame.size.height);
} else if (self.isItRightSwipe == YES) {
menuViewController.view.frame = CGRectMake(-menuViewController.view.frame.size.width, 0, menuViewController.view.frame.size.width, menuViewController.view.frame.size.height);
} else if (self.isItRightSwipe == NO) {
menuViewController.view.frame = CGRectMake(menuViewController.view.frame.size.width, 0, menuViewController.view.frame.size.width, menuViewController.view.frame.size.height);
}
[self.view addSubview:menuViewController.view];
[self addChildViewController:menuViewController];
[menuViewController didMoveToParentViewController:self];
UIView *view = menuViewController.view;
return view;
}
So when ever i need a certain view controller, i just call this function
self.childView = [self getPresentedMenu:#"Settings" withMenuTag:SETTINGS_TAG withAViewController:self.settingsViewController andMenuDelegate:self.settingsViewController.delegate];
but this method is not assigning the delegate
If settingsViewController is an IBOutlet, you have to add IBOutlet to it's declaration and connect it using the storyboard or xib.
#property (nonatomic, strong) IBOutlet SettingsHubViewController * settingsViewController;
But if it's not an Outlet, you have to allocate it before set it's delegate.
self.settingsViewController = [[SettingsHubViewController alloc] init];
self.settingsViewController.delegate = self;
You should also verify that your - (IBAction)switchAction:(id)sender IBAction is correctly connected in the storyboard or xib.
When you initialize your SettingsViewController, make sure that you do it using the storyboard method and not [[SettingsViewController alloc] init].
First set a Storyboard ID directly in your storyboard view:
Then use this to settingsViewController:
self.settingsViewController = [[UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:NULL] instantiateViewControllerWithIdentifier:#"SettingsViewController"];
Replace MainStoryboard_iPhone with your Storyboard name.
Hope that helps!
// If u are using View controller
SettingsHubViewController *settingVC = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"yourStoryBoardIdentifier"];
settingVC.delegate = self;
// If u are using XIB load the NIB after that set the settingVC.delegate = self;
its working for me (I created OverLay view and loaded in main class)
Related
I'm trying to use an xib file (which is just a simple view) on my viewcontroller more than once. I can add it on my viewcontroller more than once and interact with both of them. The question is, how can i distinguish between these views to know which one i'm clicking?
For example, when i tap on my firstview, i want to print "apples" and when i tap on second view i wan to print "oranges"
Below you can see my code and here is github repo for you to play with my code: https://github.com/TimurAykutYildirim/demoView/tree/multiple-instance
ViewController.h
#import <UIKit/UIKit.h>
#import "Mini.h"
#interface ViewController : UIViewController <SelectionProtocol>
#property (weak, nonatomic) IBOutlet Mini *miniView;
#property (weak, nonatomic) IBOutlet Mini *miniView2;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.miniView.delegate = self;
self.miniView2.delegate = self;
}
-(void) isClicked {
NSLog(#"apples");
NSString * storyboardName = #"Main";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
UIViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Mini.h
#import <UIKit/UIKit.h>
#protocol SelectionProtocol;
#interface Mini : UIView
#property (nonatomic, weak) id<SelectionProtocol> delegate;
- (IBAction)btnClick:(id)sender;
#end
#protocol SelectionProtocol <NSObject>
#required
-(void) isClicked;
#end
Mini.m
#import "Mini.h"
#implementation Mini
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self load];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self load];
}
return self;
}
- (void)load {
UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:#"Mini" owner:self options:nil] firstObject];
[self addSubview:view];
view.frame = self.bounds;
}
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClicked];
}
}
#end
There are lots of ways that you could do that. Here are two:
Add a sender parameter to your -[ViewController isClicked] method, and change Mini so that calls to -isClicked pass in a pointer to self. Then the code in -isClicked can compare that to each of the instances of Mini that it knows about, i.e. self.miniView and self.miniView2, to see if either of those is the one that sent the message.
Add a property to Mini that lets you distinguish between the two, e.g. name. You can configure that property in -viewDidLoad, like self.miniView.name = #"apples", or you can even do it in the .xib file using "user defined runtime attributes." Then you can have Mini pass it's name property as a parameter to methods that need to know which instance of Mini is the caller. (Or, combine 1 & 2 and pass a reference to self so that ViewController can examine the name parameter or anything else it wants.)
I'm assuming from your question that somewhere in the Mini xib there is an outlet with the text "apples" (or whichever fruit for that particular xib).
In that case, you can just change your protocol to:
- (void)isClickedFromView:(Mini *)mini
In the delegate (ViewController.m) change the btnClick action to:
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClickedFromView:self];
}
}
Add an outlet like fruitLabel to your Mini class.
#property (weak, nonatomic) IBOutlet UILabel *fruitLabel
Now when the delegate gets the call you can call:
NSLog(#"Fruit: %#", mini.fruitLabel.text);
===== Additional answer for if the data (in this case fruits) is available in code =====
If you have the data programmatically already (like an array of fruits), It might be easier to just put the Mini classes in an array ordered the same way.
So if miniViewArray contains an array of your Mini classes,
and fruitArray contains an array of NSStrings of fruits you can do:
At the time you set the miniView's delegate you can add them to the array..something like:
NSArray *fruitArray = #[ #"apples", #"oranges" ];
NSArray *miniViewArray = #[ miniView, miniView2 ];
Then in the delegate call you can do (Using the same protocol change as above):
- (void)isClickedFromView:(Mini *)mini {
NSInteger fruitIndex = [miniViewArray indexOfObject:mini];
NSString fruitName = fruitArray[fruitIndex];
NSLog(#"Fruit: %#", fruitName);
}
I use ENUM for distinguish between the view type.
typedef NS_ENUM(NSUInteger, <#MyViewType#>) {
<#MyViewTypeDefault#>,
<#MyViewTypeA#>,
<#MyViewTypeB#>,
};
#property (nonatomic, assign) MyEnum viewType;
Protocol:
-(void) isClickedForViewType:(MyEnum)viewType;
Common approach for calling delegate method is to include caller as a param. In your case it should be something like this:
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClickedOnMiniView:self];
}
}
Basically, this convention was created to cover such cases - one object is a delegate of many similar ones.
Check apple docs for examples of this convention:
https://developer.apple.com/documentation/uikit/uitableviewdelegate
I have a VC of custom type LVSBBSettingsViewController for user settings. The VC is presented by a main menu in LVSMainViewController. The main VC sets the values of the controls in the settings VC programatically. However, when the settings view appears, the controls all revert to the values assigned to them in the storyboard.
I am using delegation to close the settings view and to pass data from the settings VC back to the main VC when it closes. But I don't think that's what's causing the problem since the same thing happens even if I remove that.
What's causing this? I have a feeling I'm missing something really simple here...
LVSBBSettingsViewController.h:
#import <UIKit/UIKit.h>
#class LVSBBSettingsViewController;
#pragma mark LVSBBSettingsViewController Delegate
#protocol LVSBBSettingsViewControllerDelegate <NSObject>
- (void)settingsViewControllerDidCancel:(LVSBBSettingsViewController *)controller;
- (void)settingsViewControllerDidSave:(LVSBBSettingsViewController *)controller;
#end
#pragma mark LVSBBSettingsViewController
#interface LVSBBSettingsViewController : UITableViewController
#property (nonatomic, weak) id <LVSBBSettingsViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UISwitch *showBranchVarLabelsSwitch;
#property (weak, nonatomic) IBOutlet UISwitch *useAnimationSwitch;
#property (weak, nonatomic) IBOutlet UISwitch *showAllNodesSwitch;
#property (weak, nonatomic) IBOutlet UILabel *tempLabel;
- (IBAction)cancel:(id)sender;
- (IBAction)done:(id)sender;
#end
LVSBBSettingsViewController.m:
#import "LVSBBSettingsViewController.h"
#interface LVSBBSettingsViewController ()
#end
#implementation LVSBBSettingsViewController
// ... Xcode-generated stuff ...
- (IBAction)cancel:(id)sender
{
[self.delegate settingsViewControllerDidCancel:self];
}
- (IBAction)done:(id)sender
{
[self.delegate settingsViewControllerDidSave:self];
}
#end
LVSBBMainViewController.h:
#import <UIKit/UIKit.h>
#import "LVSBBSettingsViewController.h"
#interface LVSMainViewController : UIViewController <LVSBBSettingsViewControllerDelegate>
#end
LVSBBMainViewController.m:
#import "LVSMainViewController.h"
#import "LVSBBMasterViewController.h"
#interface LVSMainViewController ()
#end
#implementation LVSMainViewController
{
LVSBBMasterViewController *bbmvc;
}
// ...
- (void)viewDidLoad
{
[super viewDidLoad];
// Get main storyboard
UIStoryboard *st = [UIStoryboard storyboardWithName:[[NSBundle mainBundle].infoDictionary objectForKey:#"UIMainStoryboardFile"] bundle:[NSBundle mainBundle]];
// Instantiate bbmvc
bbmvc = [st instantiateViewControllerWithIdentifier:#"BBMasterViewControllerStoryboard"];
// Initialize settings
bbmvc.showBranchVarLabels = YES;
bbmvc.useAnimation = YES;
bbmvc.showAllNodes = NO;
}
...
#pragma mark LVSBBSettingsViewController Delegate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowSettings"])
{
// Get pointer to settings VC
UINavigationController *navigationController = segue.destinationViewController;
LVSBBSettingsViewController *settingsViewController = [navigationController viewControllers][0];
// Set delegate
settingsViewController.delegate = self;
// Populate settings VC
// (same problem occurs if I replace right-hand sides of next 3 lines with NO;)
settingsViewController.showBranchVarLabelsSwitch.on = bbmvc.showBranchVarLabels;
settingsViewController.useAnimationSwitch.on = bbmvc.useAnimation;
settingsViewController.showAllNodesSwitch.on = bbmvc.showAllNodes;
settingsViewController.tempLabel.text = #"HELLO";
}
}
- (void)settingsViewControllerDidCancel:(LVSBBSettingsViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)settingsViewControllerDidSave:(LVSBBSettingsViewController *)controller
{
// Set settings in bbmvc
bbmvc.showBranchVarLabels = controller.showBranchVarLabelsSwitch.on;
bbmvc.useAnimation = controller.useAnimationSwitch.on;
bbmvc.showAllNodes = controller.showAllNodesSwitch.on;
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
UPDATE: As a workaround, I added properties in LVSBBSettingsViewController that match the properties in LVSMainViewController. In prepareForSegue:sender:, I set those properties instead of setting the controls directly. Then in viewDidLoad in LVSBBSettingsViewController, I set the control values based on the properties. This seems to work. Still not sure why I can't set the control values directly, though.
hi i am working on an app and all was going good till now.... i am stuck at this point..
here is my storyboard snapshot..
in the DemoTableViewController when i clock on "filters" button .. Brands TableViewController is opened modally .
after user select multiple rows in Brands TableViewController ,, he then clicks on done button and view controller is dismissed by this code:
-(IBAction)DonePressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
I am storing the user selections in NSMutableSet and it is to be Used in DemoTableViewController so that i can reload the table rows according to the selection but i dont know how to send NSMutableSet to DemoTableViewController and then reload the table according to selection..
what is the right way to dismiss modal view and reload the parent DemoTableViewController
i know i am not doing it correctly but can anyone help me in doing it...
here is some code ---
DemoTVC.h
#import <UIKit/UIKit.h>
#import "MyModelClass.h"
#import "brandsTableViewController.h"
#interface demoTableViewController : UITableViewController<UITableViewDataSource,UITableViewDelegate,MyModelProtocol,FilterProtocol>
#property (strong, nonatomic) IBOutlet UITableView *demoTable;
- (IBAction)FilterPressed:(id)sender;
#end
DemoTVC.m--
the method which performs segue--
- (IBAction)FilterPressed:(id)sender {
[self performSegueWithIdentifier:#"FilterPressed" sender:self];
}
the delegate method which is called to get the values from BrandsTVC--
-(void)GetTheSet:(NSMutableSet *)MySet{
[self dismissViewControllerAnimated:YES completion:nil];
}
viewdidLoad--
- (void)viewDidLoad
{
[super viewDidLoad];
productArray = [[NSMutableArray alloc] init];
homeModel = [[MyModelClass alloc] init];
// Set this view controller object as the delegate for the home model object
homeModel.delegate = self;
// Call the download items method of the home model object
[homeModel downloadItemswithurl:#"url is written here"];
}
BrandsTVC.h---
#import <UIKit/UIKit.h>
#import "MyModelClass.h"
#protocol FilterProtocol <NSObject>
-(void)GetTheSet:(NSMutableSet *) MySet;
#end
#interface brandsTableViewController : UITableViewController<UITableViewDataSource,UITableViewDelegate,MyModelProtocol>
#property (strong, nonatomic) IBOutlet UITableView *RetailerList;
#property (strong,nonatomic) NSMutableSet *selectStates;
#property (nonatomic, weak) id<FilterProtocol> delegate;
- (IBAction) DonePressed:(id)sender;
#end
BrandsTVC.m---
#interface brandsTableViewController ()
{
MyModelClass *myModelClass;
NSMutableArray *BrandsArray;
brandsTableViewController *Delegate;
}
#end
viewDidLoad----
- (void)viewDidLoad
{
[super viewDidLoad];
BrandsArray = [[NSMutableArray alloc] init];
self.selectStates=[NSMutableSet new];
myModelClass = [[MyModelClass alloc] init];
// Set this view controller object as the delegate for the home model object
myModelClass.delegate = self;
// Call the download items method of the home model object
[myModelClass downloadItemswithurl:#"url to get json"];
self.tableView.allowsMultipleSelection = YES;
}
Done Button is called ---
- (IBAction)DonePressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
id<FilterProtocol> strongDelegate = self.delegate;
if ([strongDelegate respondsToSelector:#selector(GetTheSet:)]) {
[strongDelegate GetTheSet:self.selectStates];
}
}
#end
You can keep those elements in NSUserDefaults, and whenever DemoTableViewController appears, you send [tableView reloadData]. Do this in the viewDidAppear: method.
You can also use Core Data, but there are a lot of details to explain how to do that.
After a lot of trial and error i come to know that in BrandsTVC--
- (IBAction)DonePressed:(id)sender {
NSLog(#"done pressed %#",self.delegate);
[delegate GetTheSet:self.selectStates];
}
self.delegate is giving me NULL.
delegate is not setup and i cant figure out how to do that.. someone help me..
this answer solved my problem :D
custom viewcontroller delegate not working
today i have this problem:
Im doing so the title of the First cell of a tableview is show in my "Home" view controller.
This is what i have dun at the moment, but the label in homeviewcontroller is not getting the title text of the first cell.
Reminders.h:
NSMutableString *primerevento;
#property (strong, nonatomic) NSMutableString *primerevento;
Reminders.m:
#synthesize primerevento;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
....
UITableViewCell *cell = (UITableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
if(indexPath.row == 1){
cell.textLabel.text = primerevento;
}
HomeViewController.h
#property (weak, nonatomic) IBOutlet UILabel *promediototal;
HomeViewController.m
#synthesize promediototal
- (void)viewDidLoad
{
[super viewDidLoad];
....
Reminders *viewsiguiente = [[Reminders alloc] initWithNibName:nil bundle:nil];
promediototal.text = viewsiguiente.primerevento;
[self presentViewController:viewsiguiente animated:YES completion:NULL];
my label on homeviewcontroller appears in blanc, i dont know what im doing wrong, im really trying.
Thanks for your time guys
In the homeViewController don't forget to import the .h of the remindersViewController.
#import "reminders.h"
#property (weak, nonatomic) IBOutlet UILabel *promediototal;
Then in the homeViewController.m where you want your text - promediototal.text = reminders.primerevento.text;
#synthesize promediototal
- (void)viewDidLoad
{
[super viewDidLoad];
....
Reminders *viewsiguiente = [[Reminders alloc] initWithNibName:nil bundle:nil];
promediototal.text = reminders.primerevento.text;
[self presentViewController:viewsiguiente animated:YES completion:NULL];
We have probably still too view code to understand what you are up to.
There is one thing though:
Reminders *viewsiguiente = [[Reminders alloc] initWithNibName:nil bundle:nil];// 1)
promediototal.text = viewsiguiente.primerevento; // 2)
[self presentViewController:viewsiguiente animated:YES completion:NULL]; // 3)
1) What is the purpose of that? Firstyou create a brand new view controller object. As you do not create it from nib (which is fine) it cannot have any poperties set. so viewsiguiente.primerevento is nil at that point of time.
2) Now you take this nil value and assign it to promeditional.text which becomes nil to? What is the sense in that? Did you mean?
viewsiguiente.primerevento = promediototal.text;
3) And now you present the newly created view controller viewsiguiente. You did not make any changes to it. That may be fine if you implementation of Reminders takes care of the views. But you did not set any value from external.
I have two view controllers called DataPass and ViewController. I want to pass a data from DataPass to ViewController. I have a label in ViewController and I have a UIButton in my ViewController which will dismiss itself and while dismissing will pass a data to label in DataPass.
I could not do it. Please help. Here is my code:
ViewController.h
#interface ViewController : UIViewController
- (IBAction)sayfaGec:(id)sender;
#property (retain, nonatomic) IBOutlet UILabel *label;
+(ViewController *)hop;
ViewController.m
+(ViewController *)hop{
static ViewController *myInstance = nil;
if (myInstance == nil){
myInstance = [[[self class]alloc]init];
myInstance.label.text = #"test";
}
return myInstance;
}
DataPass.h
- (IBAction)sayfaKapat:(id)sender;
DataPass.m
- (IBAction)sayfaKapat:(id)sender {
[ViewController hop].label.text = #"ddsg";
[self dismissModalViewControllerAnimated:YES];
}
ViewController.h
- (void)setLabelData:(NSString:)aString;
ViewController.m
- (void)setLabelData:(NSString:)aString{
[yourLabel setText:aString];
}
In your DataPass you say:
ViewController *vContr = [[ViewController aloc]init];
[vContr setLabelData: #"asta la vista baby!"];
[self.view addSubview:vContr.view];