I'm having some problems setting a custom NSView for my NSPopupButton menu items. Here is what I've got so far:
#interface ViewController ()
#property (weak) IBOutlet NSPopUpButton *popupButton;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
for(int i = 0; i < 25; i++) {
NSMenuItem *menuItem = [[NSMenuItem alloc ] initWithTitle:[NSString stringWithFormat:#"%d", i] action:#selector(itemClicked:) keyEquivalent:#""];
MenuView *menuView = [[MenuView alloc] initWithFrame:CGRectMake(0, 0, 184, 50)];
menuView.displayText.stringValue = #"This is a test";
[menuItem setView:menuView];
[self.popupButton.menu addItem:menuItem];
}
}
- (void)itemClicked:(id)sender {
}
#end
//My custom view
#implementation MenuView
- (id)initWithFrame:(NSRect)frameRect {
NSString* nibName = NSStringFromClass([self class]);
self = [super initWithFrame:frameRect];
if (self) {
if ([[NSBundle mainBundle] loadNibNamed:nibName
owner:self
topLevelObjects:nil]) {
[self configureView];
}
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self configureView];
}
- (void)configureView {
[self setWantsLayer:YES];
self.layer.backgroundColor = [NSColor blueColor].CGColor;
}
#end
//Here is what my xib MenuView looks like
And here is the problem:
This seems like it should be a fairly straight forward task but I'm not sure what is happening to my view and why my label on the view seems to disappear and is not showing any text for each of the views. I was poking around in the documentation and stumbled across this for the NSPopupButton Menu :
// Overrides behavior of NSView. This is the menu for the popup, not a context menu. PopUpButtons do not have context menus.
#property (nullable, strong) NSMenu *menu;
I'm not sure if there is something that I'm doing wrong that is causing this problem or if what I'm trying to do in this context is not achievable off of an NSPopupButton NSMenu. If anyone has any experience with this and could offer advice I'd really appreciate it.
I've created a new project with two ViewControllers and imported a class that pushes ViewController from right to left instead LTR but can't manage to use it. I can see that pushViewController inside UIRightToLeft.m is not being called and I don't understand why.
My main goal is to get that working with RTL aniamtion.
#import "UIRightToLeft.h"
#implementation UIRightToLeft
- (id)initWithRootViewController:(UIViewController *)rootViewController
{
self = [super initWithRootViewController:rootViewController];
if (!self)
return nil;
return self;
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
NSLog(#"pushViewController");
// Add the viewController and a fake controller without animation. Then pop the fake controller with animation.
UIViewController *fakeController = [[UIViewController alloc] init] ;
[super setViewControllers:[[self viewControllers] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:viewController, fakeController, nil]] animated:NO];
[super popViewControllerAnimated:animated];
}
- (void)popViewControllerAnimatedStep2:(UIViewController *)viewController
{
// Push the new top controller with animation
[super pushViewController:viewController animated:YES];
// Remove the view that should have been popped
NSMutableArray *arr = [NSMutableArray arrayWithArray:[self viewControllers]];
[arr removeObjectAtIndex:[[self viewControllers] count]-2];
[super setViewControllers:[NSArray arrayWithArray:arr] animated:NO];
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
NSLog(#"popViewControllerAnimated");
if (animated)
{
// Save the controller that should be on top after this pop operation
UIViewController *newTopController = [[self viewControllers] objectAtIndex:[[self viewControllers] count]-2];
// Remove it from the stack. Leave the view that should be popped on top
NSMutableArray *arr = [NSMutableArray arrayWithArray:[self viewControllers]];
[arr removeObjectAtIndex:[[self viewControllers] count]-2];
[super setViewControllers:[NSArray arrayWithArray:arr] animated:NO];
// Schedule the next step
[self performSelector:#selector(popViewControllerAnimatedStep2:) withObject:newTopController afterDelay:0];
return [arr objectAtIndex:[arr count]-1];
}
return [super popViewControllerAnimated:NO];
}
#end
ViewController.m:
#import "ViewController.h"
#import "UIRightToLeft.h"
#import "SecondViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)pressMe:(UIButton *)sender {
SecondViewController *next = [[SecondViewController alloc]init];
[self.navigationController pushViewController:next animated:YES];
}
#end
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
- (IBAction)pressMe:(UIButton *)sender;
#end
(In my ViewController there's only one button draged to the second ViewController with push)
After looking at your error log I think you actually need to have your navigation controller subclass UIRightToLeft.
If you are using a Storyboard, select your navigation controller and set Custom Class to UIRightToLeft.
MainViewController contains UISegmentedControl with 3 segments.
SliderSubViewController contains a UISlider and -(void) sliderValueChanged:(UISlider*)sender
I want to add a code like this in SliderSubViewController.m but I'm stuck on how to access the UISegmentedControl and add an image:
- (void)viewDidLoad
{
[super viewDidLoad];
mainController = [[MainViewController alloc] initWithNibName:nil bundle:nil];
[minPrisSlider addTarget:self
action:#selector(sliderValueChanged:)
forControlEvents:UIControlEventValueChanged];
- (void)sliderValueChanged:(UISlider*)sender
{
NSUInteger index = (NSUInteger)(minPrisSlider.value + 0.5); // Round the number.
[minPrisSlider setValue:index animated:NO];
int progressAsInt =(int)(minPrisSlider.value + 0.5f);
NSString *newText =[[NSString alloc] initWithFormat:#"%d,-",progressAsInt];
minPrisText.text = newText;
if (minPrisSlider.value != 70) {
/*mainController.segmentedControl add main bundle image named "CheckboxChecked.png"
to current segment*/
}
}
If you have added SliderViewController as subview on MainViewController, One approach you can try is to just declare object of MainViewController in SliderViewController write #property and #synthesize, dont allocate it and while adding SliderView as subview just assign it as
sliderviewcontrollerObject.mainViewControllerObject = self;
now you can access segmentedControl from sliderview ass self.mainViewControllerObject.segmentedbar
Also you can try delegate approach.
IT IS SOLVED BY #T-X. I've marked the changes with "// SOLUTION" in the code!!!
I have the following issue, if I presentModalViewController, the NSMutableArray "projectsArray" gets reset.
Here is my code:
Here the .h file:
// ViewController.h
#import <UIKit/UIKit.h>
#class AddProject;
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
IBOutlet UITableView *projectsTableView;
UIAlertView *reallyDelete;
BOOL candelButtonClicked;
NSIndexPath *actualIndexPath;
NSMutableArray *projectsArray;
NSInteger ID;
NSString *NAME;
AddProject *addProject;
**ViewController *viewContr; // SOLUTION**
}
- (IBAction)addNewProject:(id)sender;
- (void)addToTableView:(NSString *)projectName;
#property (nonatomic, retain) UIAlertView *reallyDelete;
#property (nonatomic) NSInteger cancelButtonIndex;
#property IBOutlet UITableView *projectsTableView;
#property (nonatomic, retain) AddProject *addProject;
**#property (nonatomic, retain) ViewController *viewContr; // SOLUTION**
#end
Here the .m file:
// ViewController.m
#import "ViewController.h"
#import "CustomTableCell.h"
#import "AddProject.h"
#interface ViewController ()
#end
#implementation ViewController
**#synthesize viewContr; // SOLUTION**
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
projectsArray = [[NSMutableArray alloc] init];
ViewController *element = [[ViewController alloc] init];
element->ID = 1;
element->NAME = #"Demo project";
NSLog(#"viewdidload");
[projectsArray addObject:element];
NSLog(#"1. Array length: %d", [projectsArray count]);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([projectsArray count] < 1) {
NSLog(#"2. Array length: 0");
return 0;
} else {
NSLog(#"2. Array length: %d", [projectsArray count]);
return [projectsArray count];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 78;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *customTableCellIdentifier = #"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:customTableCellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
ViewController *element = [[ViewController alloc] init];
element = [projectsArray objectAtIndex:indexPath.row];
cell.nameLabel.text = element->NAME;
cell.prepTimeLabel.text = [NSString stringWithFormat:#"%i", element->ID];
NSLog(#"3. Array length: %d", [projectsArray count]);
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
reallyDelete = [[UIAlertView alloc] initWithTitle:#"Deleting" message:#"Do your really want to delete that row?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[reallyDelete show];
actualIndexPath = indexPath;
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex != [reallyDelete cancelButtonIndex])
{
//If "Yes" clicked delete
[projectsArray removeObjectAtIndex:actualIndexPath.row];
NSLog(#"delete row");
[self.projectsTableView deleteRowsAtIndexPaths:[NSMutableArray arrayWithObjects:actualIndexPath, nil] withRowAnimation:YES];
[self.projectsTableView reloadData];
}
}
#pragma mark Edit
- (IBAction)addNewProject:(id)sender {
NSLog(#"4. Array length: %d", [projectsArray count]);
AddProject *addProjectView = [[AddProject alloc] initWithNibName:#"AddProject" bundle:nil];
NSLog(#"4.1 Array length: %d", [projectsArray count]);
addProjectView.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
NSLog(#"4.2 Array length: %d", [projectsArray count]);
addProjectView.modalPresentationStyle = UIModalPresentationFormSheet;
NSLog(#"4.3 Array length: %d", [projectsArray count]);
**addProjectView.viewContr = self; // SOLUTION**
[self presentModalViewController:addProjectView animated:YES];
NSLog(#"4.4 Array length: %d", [projectsArray count]);
addProjectView.view.superview.frame = CGRectMake(addProjectView.view.center.x/2.5, addProjectView.view.center.y/3, 800, 600);
NSLog(#"4.5 Array length: %d", [projectsArray count]);
}
- (void)addToTableView:(NSString *)projectName
{
NSLog(#"Project Array: %#", projectsArray);
NSLog(#"addToTableView: %#", projectName);
NSLog(#"5. Array length: %d", [projectsArray count]);
ViewController *element = [[ViewController alloc] init];
element->ID = 2;
element->NAME = projectName;
[projectsArray addObject:element];
NSLog(#"6. Array length: %d", [projectsArray count]);
[self.projectsTableView reloadData];
[self.projectsTableView setNeedsDisplay];
}
- (void)viewDidUnload
{
[self setProjectsTableView:nil];
self.projectsTableView = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
Here the .h file of the modal presented view:
// AddProject.h
#import <UIKit/UIKit.h>
#class ViewController;
#interface AddProject : UIViewController
{
IBOutlet UITextField *projectName;
ViewController *viewContr;
}
- (IBAction)back:(id)sender;
- (IBAction)next:(id)sender;
#property (nonatomic, retain) ViewController *viewContr;
#end
The .m file of the modal presented file:
// AddProject.m
#import "AddProject.h"
#import "ViewController.h"
#interface AddProject ()
#end
#implementation AddProject
**#synthesize viewContr; // SOLUTION I've mist to synthesize it**
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
**// SOLUTION no new allocation/init of ViewController**
}
- (IBAction)back:(id)sender
{
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)next:(id)sender
{
[viewContr addToTableView:projectName.text];
[viewContr.projectsTableView reloadData];
[viewContr.projectsTableView setNeedsDisplay];
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewDidUnload
{
projectName = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
I've tested the code by using breakpoints and I could locate the issue when the AddProject class "viewDidLoad" starts loading. But I don't know why it resets the array.
You navigate from ViewController Screen to Add project Screen. In Add Project Screen, you add a new project and want to delegate back this information to the ViewController Screen where you came from.
Now, you are trying to have an instance of ViewController in your AddProject, which you newly instantiate in viewDidLoad method and when project is added, you provide the information to it by calling - (void)addToTableView:(NSString *)projectName on it.
This doesn't work because - The instance of ViewController you allocated is a new instance and not the one you navigated from. When you do alloc + init you get a new object. Thus, although you get addToTableView: called on your ViewController, it doesn't work because the ViewController instance that is receiving this message is a new instance and not the one you navigated from.
Thus, all you needed to do is - have a ViewController property in your Add Project, which you would set before navigating. And then you can send message to this property - As explained in this transcript.
I would like to say - However this thing works, it provides tight coupling between the two controllers.
Ideally, This cane be done by making ViewController (where you go navigate from) as a delegate of Add Project (where you navigate to). And then Add Project can delegate the added project to it's delegate by something like - (void) addProjectController:(AddProjectViewController *) controller didAddProject:(NSString *) projectName. This provides lose coupling between the two view controllers. To understand delegation concept, refer to Delegates and Datasources. Alternatively, search for other Stack overflow posts.
You should allocate projectsArray in the init method of the view controller, not in in viewDidLoad. projectsArray = [[NSMutableArray alloc] init]; allocated a new empty array tach time the view is loaded.
Okay my issue is fixed through the stackoverflow chat. The tips for the final solution came from user #T-X. I've added the tips in the code in my question. They are marked with "// SOLUTION".
In Add Project View controller's viewDidLoad method, remove the line
viewContr = [[ViewController alloc] init];. Now make the variable
viewContr in your ViewController class a property. Also synthesize
it. And finally in the - (IBAction)addNewProject:(id)sender method,
before presenting the controller, do - addProjectView.viewContr = self;. It will solve your issue.
(quote from chat.stackoverflow | #T-X)
I have been pulling my hair out all afternoon trying to figure out why the following code will not work. All I am trying to do is pass a string, from one class to another.
In my FirstDetailViewController.h file I declare the NSString
#property(nonatomic, retain) NSString *infoForArray;
And then in my Grinding01_DetailViewController.m I try to set a value for the string
#import "Grinding01_DetailViewController.h"
#import "FirstDetailViewController.h"
#implementation Grinding01_DetailViewController
...
NSString *didLoadMessage = #"Grinding01 Loaded";
FirstDetailViewController *temp = [[FirstDetailViewController alloc] initWithNibName:#"FirstDetailView" bundle:nil];
temp.infoForArray = didLoadMessage;
[self.navigationController pushViewController:temp animated:YES];
}
When I output the infoForArray from the FirstDetailViewController.h it is null.
Any help would be appreciated, I think there's a simple step that I am missing, but I just can't see it.
EDIT: Here is the code from the FirstDetailViewController
FirstDetailViewController.h
#import <UIKit/UIKit.h>
#import "Protocols.h"
#interface FirstDetailViewController : UIViewController <SubstitutableDetailViewController> {
//for the output
IBOutlet UITextView *outputView;
UIToolbar *navigationBar;
NSMutableArray *logMessages;
}
#property (nonatomic, retain) IBOutlet UIToolbar *navigationBar;
//for incoming messages
#property(nonatomic, retain) NSString *infoForArray;
#end
FirstDetailViewController.m
#import "FirstDetailViewController.h"
#implementation FirstDetailViewController
#synthesize navigationBar, infoForArray;
-(void)viewDidLoad{
[super viewDidLoad];
//The log cannot be changed
outputView.editable = NO;
}
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidUnload {
[super viewDidUnload];
self.navigationBar = nil;
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
NSLog(#"message: %#", infoForArray);
outputView.text = infoForArray;
}
#pragma mark -
#pragma mark Managing the popover
- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
// Add the popover button to the toolbar.
NSMutableArray *itemsArray = [navigationBar.items mutableCopy];
[itemsArray insertObject:barButtonItem atIndex:0];
[navigationBar setItems:itemsArray animated:NO];
[itemsArray release];
}
- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
// Remove the popover button from the toolbar.
NSMutableArray *itemsArray = [navigationBar.items mutableCopy];
[itemsArray removeObject:barButtonItem];
[navigationBar setItems:itemsArray animated:NO];
[itemsArray release];
}
#pragma mark -
#pragma mark Rotation support
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
#pragma mark -
#pragma mark Memory management
- (void)dealloc {
[navigationBar release];
[super dealloc];
}
#end
It sounds like the trouble area is in in FirstDetailViewController. I would suggest posting the code for that so we can see what's going on.
my guess is you are checking for infoForArray somewhere in the instantiation process of FirstDetailViewController, which occurs before you set temp.infoForArray = didLoadMessage.
Just for reference, if you check for infoForArray in viewDidLoad that will be too early. viewDidLoad is triggered when the view is put into memory. What you want is viewDidAppear, which you may have to add yourself