WKInterfaceTable's didSelectRowAtIndex never gets called in WKInterfaceController - watchkit

I have a WKInterfaceController and I added a table as following:
// .h
#interface InterfaceController : WKInterfaceController
#property (weak, nonatomic) IBOutlet WKInterfaceTable *table;
#end
// .m
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"did select");
}
- (void)table:(WKInterfaceTable *)table
didSelectRowAtIndex:(NSInteger)rowIndex{
NSLog(#"did select new");
}
However neither of the two methods gets called. I was unable to find any protocol declaration for WKInterfaceTable and neither any delegate property on the table.
Is there something that I am missing here?

I found out that the method was never called because I had set a segue to be triggered on selection of the row on Interface builder.
It seems that that by having no delegation and table protocols once you set a delegate it stops the didSelectRow method from being called.

In Apple's WKInterfaceController document it states that if you do not have any actions or segues then the method called is:
- table:didSelectRowAtIndex:
If you use segues then the methods called are:
For buttons:
- contextForSegueWithIdentifier:
For tables:
- contextForSegueWithIdentifier:inTable:rowIndex:

Swift 4
Here’s an example of selecting a WKInterfacetable row in a REST/JSON implementation.
Create a context property instance of the array class instead of using self.pushController.
override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
let message = messageObjects[rowIndex]
presentController(withName: "MessageView", context: message)
}

Related

How to delete a row in the 1st Interface Controller by pressing a button in the 2nd Interface Controller ( Modal View )

I have a table in the 1st interface controller , when a press on a row , a modal interface controller opens up , it contains a button.
I want the button to delete the row in the first interface controller.
Here is my code :
In the first interface controller
Blockquote
// It opens up a modal view ( with the context of the tapped row )
override func contextForSegueWithIdentifier(segueIdentifier: String, inTable table: WKInterfaceTable, rowIndex: Int) -> AnyObject? {
var timelineRow = timeline.reverse()
return timelineRow[rowIndex]
}
Blockquote
And here is my code in the second interface controller
Blockquote
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
sentContext = (context as? Dictionary)!
sentRow = sentContext
//sentRow contains the context
}
#IBAction func deleteRow() {
var sentRow : [String:String] = ["action":"delete"]
NSNotificationCenter.defaultCenter().postNotificationName("notification_DeleteRow", object: nil, userInfo: sentRow)
dismissController()
}
Blockquote
I've sent the index of the row through the contextForSegueWithIdentifier.
In the 2nd Interface Controller I've extracted the Context and put it in variable
I then send back the userInfo throught the NSNotificationCenter
My Problem :
How can I use the userInfo sent back from the modal controller in order to delete the tapped row.
How would I manage to delete the tapped row (1st IC) by pressing on the delete button situated in the (2nd IC)
There are a few options in this situation:
You could use NSUserDefaults, and while it would work, this isn't how that class is intended to be used.
You can create your own custom NSNotification and broadcast it from the modal controller. Your first interface controller would listen for this event and delete the appropriate record.
You can pass a reference to your first interface controller to the modal controller and retrieve it in awakeWithContext:. This allows you to set the first interface controller as a delegate. Once this happens, you can define whatever protocol you'd like to inform the first controller of important events.
I have a blog post that goes into more detail on the second two topics: Advanced WatchKit Interface Controller Techniques
This can be achieved with custom delegate easily,
#protocol MyCustomDelegate <NSObject>
- (void)deleteButtonTapped:(id)sender;
#end
- (IBAction)deleteButtonTapped:(id)sender {
if ([self.delegate respondsToSelector:#selector(deleteButtonTapped:)]) {
[self.delegate deleteButtonTapped:sender];
};
}
More detailed answer is here.

Passing parameters of dynamics ax

On Dynamics AX 2012,Passing between two step form a parameter that I would use to change the data source of the second form;
how you pass a parameter from the init form to init the data source?
I hope I have understood the question
If you wanto to pass a parameter between forms, you have multiway.
One solution.
In Form - A override a method clicked() control Button
void clicked()
{
Args args;
FormRun formRun;
;
args = new Args();
args.name(formstr(nameyourFormB));
args.record(nameTableSourceRecords);
args.caller(element);
formRun=new FormRun(args);
formRun.run();
formRun.wait();
}
So , in the SecondForm - Form - B
override method init()
public void init()
{
super();
if(element.args() && element.args().record() &&element.args().record().TableId == tableNum(nameSourceRecords))
{
nameTableSourceRecords = element.args().record() ;
stringEdit.text(nameTableSourceRecords.nameFieldTableSourceRecords);
}
}
You have to insert in Designs node Form-B a one StringEdite (set AutoDeclaration YES) in Properties.
Now, you open Form-A select a record, click on control Button -> will Open Form-B and you have set a value in your StringEdit control.
I hope to help you.
Greetings!
Get the parameter in the form.init(), save it to a variable in your form's classdeclaration, then override the datasource's init() method and manually create a FormDataSource object using the passed in parameter to determine the datasource.
Although I'm not sure how you're going to show this on form controls...the controls will expect the datasource to be what it is set up as. There's probably a better way to achieve whatever you're trying to do.
One solution of this is to use EnumTypeParameter and EnumParameter property on menuitem of child form. Set these parameter value on parent form and on child form init you just need an if clause then. like:
if (args.parmEnumType() == yourEnum && args.parmEnum() == 'yourEnumValue')
{
//set the desired datasource
}
these links may help you:
opening a form on basis of menuitem
and
xArgs.parmEnumType

iOS7 / XCode5 - "viewDidLoad" called at once, rather than when "presentViewController" was called

I have just upgraded to XCode5 and iOS7 and now my application has stopped working.
I am creating a new view based on a property of a current view, and I need to set some properties of the new view before I display it.
Previously, I did it like this :-
hqView *v = [[hqView alloc] initWithNibName:NULL bundle:NULL];
[v setProperty1:true];
[v setProperty2:false];
[self presentViewController:v animated:TRUE completion:NULL];
This then triggered the [viewDidLoad] method on the view controller, which had the following code in it :-
if ([self property1])
{
[list1 load]
}
else
{
[list2 load]
}
However now the [viewDidLoad] method is triggering as soon as I create the view, meaning that I am not able to set the properties before [viewDidLoad] is called and so it ALWAYS loads list2 regardless of what I actually want.
The thing is - this did NOT happen under iOS6, so I am wondering whether it is a new setting in XCode5 that has caused this to change, or if I am going to have to rewrite it to do what I need it to do?
You cannot know when viewDidLoad, viewWillAppear, etc... will be called.
My advice : Make a dedicated init method to your controller, something like :
#implementation hqView
- (instancetype)initWithProperty1:(BOOL)prop1 property2:(BOOL)prop2
{
// uses default NIB
self = [super initWithNibName:nil bundle:nil];
if (self){
[self setProperty1:prop1];
[self setProperty2:prop2];
}
return self;
}
#end
Set a breakpoint on your viewDidLoad method that is being called before your init method and you will be able to see what is causing the viewDidLoad to be called. you will probably find that it is being called because the view was referenced by some other code. this most often happens in a super class (like if you have a UIViewController superclass that implements common functionality for your view controllers). for example, if you accidentally put new code in that accessed self.view in a method in your superclass such as - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
you would notice this behavior. so make sure you are not accessing the view in any code before you wanted to. -rrh

How to trigger an action from a NSTableCellView in view based NSTableView when using bindings

I'm facing a problem with a view-based NSTableView running on 10.8 (target is 10.7, but I think this is not relevant).
I'm using an NSTableView, and I get content values for my custom NSTableCellView through bindings. I use the obejctValue of the NSTableCellView to get my data.
I added a button to my cell, and I'd like it to trigger some action when clicked. So far I have only been able to trigger an action within the custom NSTableCellView's subclass.
I can get the row that was clicked like this, using the chain:
NSButton *myButton = (NSButton*)sender;
NSTableView *myView = (NSTableView*)myButton.superview.superview.superview;
NSInteger rowClicked = [myView rowForView:myButton.superview];
From there I don't know how to reach my App Delegate or controller where the action is defined.
As I am using cocoa bindings, I do not have a delegate on the NSTableView that I could use to trigger my action.
Do you have any idea how I could talked back to controller ?
Many thanks in advance!
Although you are using bindings you can still set your controller as the delegate for your tableview in the interface builder.
I see that you already are able to access the table view from inside your cell. The next task must be simple, just set the table view delegate as the target for your button's action.
Thanks for your question, I also will be triggering an action from a button on a NSTableView. Your question helped to put me on the correct path.
First to address the your solution to finding which row number my NSTableView is on. I was able to find it without knowing the button, in my custom NSTableView I installed the following as a first attempt:
- (NSInteger)myRowNumber
{
return [(NSTableView*)self.superview.superview rowForView:self];
}
this works fine, however it is less than robust. It only works if you already know specifically how deep you are in the view hierarchy. A more robust and universal solution is:
- (NSInteger)myRowNumber
{
NSTableView* tableView = nil;
NSView* mySuperview = self;
do
{
NSView* nextSuper = mySuperview.superview;
if (nextSuper == nil)
{
NSException *exception =
[NSException exceptionWithName:#"NSTableView not found."
reason:[NSString stringWithFormat:#"%# search went too deep.",
NSStringFromSelector(_cmd)] userInfo:nil];
#throw exception;
}
if ([nextSuper isKindOfClass:[NSTableView class]])
tableView = (NSTableView*)nextSuper;
else
mySuperview = mySuperview.superview;
} while (tableView == nil);
return [tableView rowForView:self];
}
this not only works at the NSTableView level, but works with anything installed at any level above it, no matter how complex the view hierarchy.
As to the unanswered part of your question, I established an IBOutlet in my class and using interface builder tied if to my files owner (in my case my document class). Once I had a reference to the class I was sending my message to, and the row number, I call the function. In my case the call required that I pass the row number it originates from.
[self.myDoc doSomethingToRow:self.myRowNumber];
I tested this and it works at various levels of the view hierarchy above NSTableView. And it functions without having to have the row selected first (which appears to be assumed in Apples documentation).
Regards, George Lawrence Storm, Maltby, Washington, USA
Use rowForView: and the responder chain
To respond to a control's action embedded within an NSTableCellView, the control should issue the action to the First Responder. Alternatively, File Owner is possible but this is more tightly coupled.
Use rowForView: within the action method to determine which row's control issued the action:
- (IBAction)revealInFinder:(id)sender {
NSInteger row = [self.tableView rowForView:sender];
...
}
The action is implemented within any of the responder chain classes. Most likely, this will be your subclassed NSWindowController instance. The responder could also be the application delegate; assuming the delegate has a means to talk to the NSTableView.
See Apple's example TableViewPlayground: Using View-Based NSTableView and NSOutlineView to see this in action.
Suhas answer helped me.
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "EDIT_CELL_VIEW"), owner: self) as? SymbolManagerCell {
if let editButton = cell.subviews[0] as? NSButton {
editButton.target = cell // this is required to trigger action
}
return cell
}
return nil
}

I can't dispatch custom event from one module to another as it gives TypeError: Error #1034: Type Coercion failed:

I am trying to dispatch a custom event from one flex module to another.
The code which dispatch the event is as below
Application.application.Destination.child.dispatchEvent(
new AlgoEvent(AlgoEvent.GETFROMPARENT_LOCAL_EVENT));
here AlgoEvent is a custom event
on the other side the module which catches and handles the event has this code:
public function sendParametersToChild(e:AlgoEvent):void
{
//some codes
}
but when the statement Application.application.Destination.child.dispatchEvent(new AlgoEvent(AlgoEvent.GETFROMPARENT_LOCAL_EVENT)); is executed the debugger give the following run time exception:
TypeError: Error #1034: Type Coercion failed: cannot convert resources.events::AlgoEvent#4182239 to resources.events.AlgoEvent.
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[C:\autobuild\3.2.0\frameworks\projects\framework\src\mx\core\UIComponent.as:9298]
at components::Destination/sendTimeToChild()[E:\FlexProjects\MyApp\src\components\Destination.mxml:99]
at components::Destination/updateParameters()[E:\FlexProjects\MyApp\src\components\Destination.mxml:206]
at components::Destination/__CreateBasketButton_click()[E:\FlexProjects\MyApp\src\components\Destination.mxml:558]
I am not able to identify what is going wrong here.
Please help to solve this problem
This is my Event class
public class AlgoEvent extends Event
{
public static const GETFROMPARENT_LOCAL_EVENT:String = "getfromparent_local";
private var eventType:String;
public function AlgoEvent(eventType:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(eventType,bubbles,cancelable);
this.eventType=eventType;
}
}
While debugging am getting error in this funcion of UIComponent class
override public function dispatchEvent(event:Event):Boolean
{
if (dispatchEventHook != null)
dispatchEventHook(event, this);
return super.dispatchEvent(event);
}
Excaxtly this line gives the error: dispatchEventHook(event, this);
Import the AlgoEvent class in the main application and create a reference to it.
import resources.events.AlgoEvent;
private var dummyEvent: AlgoEvent;
Some explanations for this could be found here: Module domains
If your custom event doesn't carry any special event properties you could workaround your problem by using the standard Event class.
dispatchEvent(new Event(AlgoEvent.GETFROMPARENT_LOCAL_EVENT));
I had the same problem when dispatching, solved overriding two functions:
override public function clone():Event
{
return new AlgoEvent(type, bubbles, cancelable);
}
override public function toString():String
{
return formatToString("AlgoEvent","type"","bubbles","cancelable","eventPhase");
}
hope it helps out :)
Mr. splash suggested a solution which worked fro me:
Try to make the Custum Event (Algo Event in my case) class known to the main application.
I.e import it in the main application and create a variable of it..
And it works for a main reason>>when we try to communicate betwwen the modules using event dispatching what happens is :the modules are loaded at the run time but the classes like event classes are linked to the modules at the run time..
But the Event class is compiled before the modules are loaded..
application defines a Custum Event Class at compile time, and the module defines its own Custum Event Class when it is published. Then when the application is run, the Custum Event Class dispatched in the application doesn't match the one in the module
swf.
For the problem which is causing this error one can check the link:
http://www.kirupa.com/forum/showthread.php?t=320390
and also
http://www.jeffdepascale.com/index.php/flash/custom-events-in-loaded-swf-files/
Mate framework takes care of all this.
It gives you a global event bus, for all modules in your app.
http://mate.asfusion.com/
Try to override the clone() method in your customized Event,AlgoEvent.
Add the following code to your AlgoEvent.as class and try:
override public function clone():Event{
return new AlgoEvent(eventType,bubbles,cancelable);
}
HTH.
Your custom Event class should look like this:
public class AlgoEvent extends Event
{
public static const GETFROMPARENT_LOCAL_EVENT:String = "getfromparent_local";
public function AlgoEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
};
override public function clone():AlgoEvent
{
return new AlgoEvent(type, bubbles, cancelable);
};
};
You should use the Event's inherited type property instead of creating a new one.
Also, the UIComponent has it's own dispatchEvent method, so you don't have to create your own - only if it works differently to the inherited one.
Regards,
Rob
Okay, it must be said that what you're doing, from an architectural standpoint, is wrong. Calling Application.application is bad for so many reason, especially if you're then starting to go down the display tree. The second any of the children changes, your build is now broke, and you won't know that until runtime because it's a module.
What you need is an application framework. A way to increase complexity without decreasing maintainability. There are many out there, but my personal favorite is Parsley. I've used it on many very large projects with much success. The problem you're trying to solve right now, dispatching one event where the other module listens for it, is extremely trivial (can be done in about 3 lines of code).
I recommend you look it over as well as my presentation on an introduction to parsley.

Resources