I must be doing stupid, but I can't see what: my UITableViewController subclass is never called when the edit button of my navigation is pressed.
What could be causing that?
My view hierarchy is loaded from a Nib file and put inside a popover. The [+] button is connected to the insertNewObject action of my UITableViewController subclass. It works fine.
The [Edit] button however has no action to connect to. The doc says it will automatically call the setEditing:animated: method of the view controller, which I override.
The nib file is set up pretty much as usual AFAICT. And in fact, I'm not sure what additional detail I can give that would suggest my mistake.
What is the control flow from the click on the [Edit] button to the call of the setEditing:animated method?
I feel like we must be missing the same thing.
Whatever the case, I made it work by doing the following.
IBOutlet UIBarButtonItem *editButton;
-(IBAction)editButtonPressed:(id)sender {
[self setEditing:YES animated:YES];
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animate
{
if(self.tableView.isEditing)
{
self.editButton.style = UIBarButtonItemStylePlain;
self.editButton.title = #"Edit";
}
else
{
//self.editButton.style = UIBarButtonSystemItemDone;
self.editButton.style = UIBarButtonSystemItemEdit;
self.editButton.title = #"Done";
}
// Toggle table view state
[super setEditing:!self.tableView.isEditing animated:animate];
}
I hooked the editButton up to the button I added to the nav bar and it's action to the editButtonPressed IBAction. After doing that my setEditing: is called (obviously) and the super call toggles the table view's editing state.
I'd like to use the system defined button styles, but the appropriate one is commented out because while it did change style I couldn't figure out how to change the text from "Edit" to "Done" so I had to do it all manually (that only worked if I left the button as Custom and set the style generically). This has the downside of not being localized (for free), etc.
Related
I have an NSTableView that is set to be 'view based', and within each NSTableCellView there is an NSButton and an NSTextField.
The text field is being populated correctly from an array controller. The buttons are appearing correctly but I'm having trouble working out how to hook up the click action.
I thought this would be possible by control-dragging from the NSButton in IB to a simple method like this one in my controller (in this case an NSDocument subclass):
- (IBAction)testAction:(NSButton *)sender {
NSLog(#"Test action");
}
It connects fine but never gets fired. Any ideas why this is or how to fix it?
I don't understand why this works, but I had the same problem and was able to get it working by assigning the table delegate and datasource to the file owner within IB, which is also the class of my click handlers. Only then did it seem to actually bind the click handlers for the buttons in my cell view. Previously I was setting the delegate and datasource in code after the view was loaded.
You have to subclass NSTableCellView class. put your onClick Action method in subclass files.
Let me know if i am not clear..
I have a simple test app (in OSX) that has a view controller with a button in its view. The button's action method is in the view controller's class, and that IBAction is connected in IB (through File's Owner). When the button is clicked, I get an EXC_BAD_Access error (except occasionally I get -[NSRunLoop buttonClick:] instead). I've read a bunch of posts here on SO having to do with NSViewControllers not being in the responder chain, but also that specifically hooking the action method up in IB should work. The only code I have is this:
In the app delegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
TestController *controller = [[TestController alloc] initWithNibName:#"TestController" bundle:nil];
[self.window.contentView addSubview:controller.view];
}
And, in the TestController class, just this:
-(IBAction)buttonClick:(id)sender {
NSLog(#"%#",sender);
}
I have 2 questions. Why is this happening, and where is the correct mvc place to put IBActions for button methods (shouldn't controller classes handle these)?
First off, I believe in the App Delegate there is already a property called viewController already defined for you. You should use this code self.viewController = [[TestController alloc] initWithNibName:#"TestController" bundle:nil]; instead of TestController *controller = [[TestController alloc] initWithNibName:#"TestController" bundle:nil];. If that doesn't fix it, then I'm not sure what's wrong. Your code should return the attributes of the button you clicked. I created my own sample project and tested out your code (although my app delegate looked different than yours) and it worked fine.
Second, you should elaborate on your second question "Why is this happening, and where is the correct mvc place to put IBActions for button methods (shouldn't controller classes handle these)?". It's not very clear what you mean.
Hope this helps a little.
NSViewController will not respond to IBActions on mac os x on iPhone It is the correct place to put your IBAction as the view should only draw its content (data) not change it. But on Mac os x the NSViewController is for setting up the NSView it's no ment to respond to IBActions you have two choices one is to put your IBAction in your NSView or is create a NSWindowController .
On mac osx you have plenty of screen space and you will alway have views within a window you'll use NSViewController to add them to your window and to setup your view but the window is first in your responder chain then your NSWindowController . eg: you may have one window and two view controller showing the same view which may have 5 test fields in it and view controller one loads that with data but you can not edit the data then view controller two loads the same view with the same data but enable editing for the text fields so. but all action methods will go to the WindowController .
I recommend you to check your viewController's identity inspector.
It may indicate wrong custom class.
viewController created basically usually has name ViewController on the section.
I have an issue with the UIPageControl. I simplified the issue for clarity below:
I put a button and a UIPageControl in my app with Interface builder and put the following in the code:
- (IBAction)tappedButton:(id)sender{
self.pageControl.currentPage = 3;
NSLog(#"Tapped Button");
}
- (IBAction)changePage:(id)sender{
NSLog(#"PAGE Changed!!!!");
}
I attached the Value Changed action to the pageController via Interface Builder.
When I tap on the button, I see the output "Tapped Button" and the third dot is highlighted... but the changePage method is never called.
Any ideas?
The changePage: method will only be called when the users request a change of page using the UIPageControl. It won't be called when the user taps the button. If you want to call the changePage: method when the button is pressed called it explicitly:
[self changePage: nil];
UIPageControl doesn't actually change pages for you. You have to write that code, inresponse to the valueChanged event. So, you want to connect that event of your UIPageControl object to your output (tappedButton: I guess), and then call your changePage: method to actually change the UI. Does that help?
There are some example projects referenced from the Apple docs that use UIScrollView and UIPageControl together. Check 'em out.
It's easy enough to set up a table view to allow editing. Just add one line to your UITableViewController:
self.navigationItem.rightBarButtonItem = self.editButtonItem;
This line adds an edit button on the nav bar that will change the table to editing mode and change its button text to "Done" while editing.
Is it possible to set this up in Interface Builder? I see that you can add a UIBarButtonItem and can set its "Identifier" to "Edit", but I don't see the expected behavior.
BTW, what does the "Identifier" in the Attributes panel do?
Yes, you can add UIBarButtonItems in Interface Builder, and they should work.
The identifier lets you use a preset button (like Edit or Reload), or you can choose Custom and make your own button.
EDIT: I may be able to help further if you could explain how UIBarButtonItems added through IB don't work.
UPDATE: UIViewController.editButtonItem is a special method that returns a UIBarButtonItem that invokes the view's setEditing method. You can achieve the same effect by creating a method that does the same thing and connecting the selector to your UIBarButtonItem in IB.
In your header file:
- IBAction edit:(id)sender;
and in your implementation file:
- (IBAction) edit:(id)sender {
[self setEditing:YES animated:YES];
}
then connect the selector to the UIBarButtonItem.
However, you might not be able to create this connection in the default Navigation-Based Application template since the Table View is in a separate file.
Have a look here: http://blog.tmro.net/2009/05/uitabbarbuttonitem-did-not-change-its.html
If you want your button to be able to change its label dynamically make sure you use a custom identifier otherwise its title will be immutable.
We still can't seem to do this specifically in Interface Builder as of Xcode 9.4.1. It's very easy to do in code, though.
You don't need to set up the button in IB at all. Simply add this code in your viewDidLoad method:
navigationItem.leftBarButtonItem = editButtonItem
That automatically sets up the Edit button, which turns to Done so the user can end editing.
To do anything custom associated with the editing process, override the view controller's setEditing(_ editing: Bool, animated: Bool) method.
For example, if you have a table view whose editing needs to be turned on and off, you can do this:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.setEditing(editing, animated: animated)
}
Make sure to call super.setEditing here.
Note: if you're using a UITableViewController, setEditing is already set up in the super class to handle the table view. You don't need to override it, unless you have other custom code you want to include when editing is enabled/disabled.
Sorry - this may be an easy question, I'm new to iPhone development and still wrapping my head around Views vs ViewControllers.
I have a NavigationViewController and I can push Views using the following method in the RootViewController which is connected to a Bar Button Item:
- (IBAction)switch:(id)sender {
NSLog(#"Swith...");
LibraryViewController *varLibraryViewController = [[LibraryViewController alloc] initWithNibName:#"LibraryViewController" bundle:nil];
[[self navigationController] pushViewController:varLibraryViewController animated:YES];
}
I want to call this same method from a button on the same view that is currently loaded. Basically I want to have the Bar Button at the top call the same method as a button on the view. I was wondering how to call a method in the ViewController from the view loaded from that viewController. Hopefully that makes sense.
Do I need to create an instance of the RootViewController? I would think that would already be instantiated. Thank you.
BTW, the code you have pasted there is leaking your LibraryViewController. You need to either explicitly release it after pushing it, or autorelease it when it's created.
Your RootViewController should have its own xib file. In this xib, the RootViewController is represented by the object named "File's Owner". You can link buttons on the view to File's Owner the same way you can link things to RootViewController in MainMenu.xib.
You'll want to declare your method as an IBAction in your header file:
- (IBAction) myMethod: (id) sender;
Save your header, then switch to Interface Builder. Right click on the Bar Button, and drag from the selector tag to your view controller object (probably the File Owner). When you release, you should be given a popup menu of available actions, and myMethod should be selectable.
If you don't get this popup, you may need to make sure your File Owner class is set properly: select the File Owner in the file window, then select "Tools" > "Identity Inspector" from the menu. In the inspector, type your view controller's class into the Class field.