Implement SCNSceneRendererDelegate on watchOS - watchkit

On iOS, I can use SCNSceneRendererDelegate and implement renderer(_:updateAtTime:) in order to update my scene just before every frame is rendered. I set this up by doing this in my view controller:
(self.view as! SCNView).delegate = self
SCNSceneRendererDelegate is available on watchOS. However, on watchOS I don't have access to an SCNView, so I can't set its delegate. Is there any way, on watchOS, to get a callback right before each frame renders so that I can update my scene?

On watchOS you deal with WKInterfaceSCNScene (instead of SCNView) which also conforms to the SCNSceneRenderer protocol.

Related

How to initialise controllers using provider state in flutter

I recently refactored half my app to use the provider pattern and i now have a problem. The main issue is that i need to initialise controllers in the init (e.g. a text controller to have an initial value or the list size of a tab controller)
How am i meant to init Controllers if the data i need has to come from the state in the build method.
For example.
// This must go in the build as it requires state
myTabsController = TabController(length: myState.list.length, vsync: this);
I'm initialisng the controller every time it builds now... How am i meant to put this in the init but still access the state variables (as there is no context).
I've tried using the afterFirstLayout() callback from the AfterLayoutMixin library but that just causes more problems. Currently with the tab bar it flashes error as no tab initialised for the first frame and then displays properly when the afterFirstLayout is called and initialises the tab. This seems like a hacky fix
I would like to learn more about how to use this pattern properly and what would be the best solution to this problem.
Feel free to ask me to clarify more.
Thanks for your help.

Why does Firebase observeEventType return a snapshot everytime in viewWillAppear?

I have a query that is observed on viewWillAppear on View Controller A
let query = FIRDatabase.database().reference().child("tags").
queryOrdered(byChild: "users/uid").queryEqual(toValue: userId)
In the same view controller, in viewDidDisappear I remove this observer.
So let's say I push into another view controller from View Controller A.
When I come back to View Controller A, the observer returns a snapshot, even though my data on the backend hasn't changed.
I only want a snapshot returning if there's been some actual change to my database. How do I solve this problem? Thanks.
One answer is to simply let the observer do it's job and update your data in the background.
So if the user is on controllerA the observer updates a datasource (an array) so then the UI is updated from that array.
When the user switches to controllerB, the observer can still update the array, but don't update the UI in controllerA since there's no need to.
When the user switches back to A you'll have current data available so just reload the tableView (assuming iOS here) from the array.
This solution reduces the 'polling' nature and let's Firebase do the heavy lifting to notify your app when it needs to. You're just reloading the tableViews from an array when that controller becomes active.
Edit
The idea here is to add the observer once - perhaps when the view loads the first time only (viewDidLoad) or maybe in your app delegate. Once you add the observer it will update your dataSource arrays when data changes so when you move from view to view the only action needed will be to reload the tableView from the updated array.
There are times when you may want to remove an observer but it doesn't sound like you need to do that from your question. So - attach the observers once and let them update the dataSource arrays as the data changes. Reload your tableViews when switching views.
You have put the query in viewWillAppear, which means every time you come to viewController A, this query will be executed irrespective of you have removed the observer or not.
Try putting the same query in viewDidLoad which means, the query will be called once and don't remove the observer anywhere. Now the query would be called only when data gets changed in firebase.

Trouble passing a pointer between child ViewControllers in iOS6

My problem is with an iOS 6 tabbed application. My work-in-progress has 5 tabs and several tabs are gateways to other view controllers. Most pages need access to a Model object, which contains data stored as arrays, strings, and so on. A bare-bones model is populated at runtime and the user can add to it throughout the application lifespan. For example, the code listed below is from my AppDelegate file , where it is passing a pointer to the bare-bones Model to the Project View Controller. This works fine: the tab application uses the navigation controller array stack; because I know the Project page is at index 2, I can send the model to it.
My problem has to do with the sub views of the Project page. For example, as a sub view to the Project page there is (or should be) a File_IO page where the user handles file operations. The File_IO page also needs access to the Model object. But when I try to send the Model pointer from the Project page to the File_IO page, the technique I used previously (from the AppDelegate to the Project) does not work. I point to an empty Model in the FileIO ViewController.
Example code: this is in the AppDelegate, and it works fine: the bare-bones Model in the Project ViewController is populated with the data.
//To the Project View Controller...
UINavigationController *project_NavigationController =
[[tabBarController viewControllers] objectAtIndex:2];
Project_ViewController *project_ViewController =
[[project_NavigationController viewControllers] objectAtIndex:0];
//This hides the navigation bar on the project page but it also removes the bar on any subsequent pages! They need to be programmmatically reset in ViewDidLoad.
[project_NavigationController setNavigationBarHidden:YES animated:YES];
project_ViewController.currentGeoModel = newGeoModel;
Now, my Project_ViewController is embedded in a NavigationController and has 4 child ViweControllers. One is named FileIO_ViewController. I want to pass the currentModel from the Project_ViewController to the FileIO_ViewController. Under the - (void)viewDidLoad method I have tried a number of things, which do not work. For example,
UINavigationController *project_NavigationController = (UINavigationController *) self.presentedViewController;
FileIO_ViewController *fileIO_ViewController = [[project_NavigationController viewControllers] objectAtIndex:1];
fileIO_ViewController.currentModel = currentModel;
compiles but when I try to access currentModel inside the FileIO_ViewController methods, it is empty.
If anyone can take the time to help I would be very appreciative. The best answer for me would be in the form of an explicit code example showing how to pass the pointer to an object like my Model from a ViewController to another ViewController where you do not explicitly know where in the stack the child VC lies. (In my example I used Index 1 but I do not actually know at which Index the FileIO_ViewController lives as I have three other ViewControllers under the Project_ViewController. I've tried several integers with no success.)
If you do answer this, please consider me a New Guy when it comes to iOS 6 and objective C -- climbing Mount Apple has been a long haul and I isn't anywhere near the top yet!
Thanks
Tim Redfield
Norway
If you have a shared single model for your app, you shouldn't proactively pass the pointer around, you should make the model available from a single location and leave it to individual objects to access this same model when they need to. A good location for your model pointer is in your Application delegate.
In the appDelegate's .h file, declare a property for your model:
//appDelegate.h
#property (nonatomic, strong) MyAppModel* appModel;
After you instantiate your model in the appDelegate, just assign it to the property:
//appDelegate.m
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.appModel = [[MyAppModel alloc] init];
//set up bare-bones appModel here
return YES;
}
Then you can access this property from any viewController that needs model data thus:
#import appDelegate.h;
AppDelegate* appDelegate = [UIApplication sharedApplication] delegate;
MyModel* model = appDelegate.model
(Better still, make the model object into it's own Singleton object, but using the appDelegate will get you started)
If you need to pass models around, then you need to take care that you are passing them to the right object. This is where your existing code is breaking. For example you are making many assumptions about the structure of a NavigationController stack. But whenever you move back down a stack by popping a controller off the top, you lose that top controller instance completely. When you 'return' to that controller by going forwards on the stack, in fact a new instance is created (unless you have taken care to keep a pointer hanging around and make sure to push to that pointer). If you need more help on this aspect, you will need to describe exactly the layout of your app, and how you are creating your viewControllers, navigationController stack, etc.
update (following your comments)
If your changes to the model are valid throughout the app, then I don't see why you can't just access the model when you need to from wherever you happen to be in the app via appDelegate.model. If you have concurrent versions of the model, you could look at making a singleton data manager object which could handle the details of storing an array of models (or a model history) and providing the correct model as per request. The principle is the same - rather than proactively passing model objects into your viewControllers, let them request data from a centralised source as they need it.
Regarding your description "Now, my Project_ViewController is embedded in a NavigationController and has 4 child ViewControllers.", I don't think you have quite grasped the distinction between Classes, Storyboard scenes, and instances.
You have this arrangement in a part of your storyboard:
UINavigationController
| push push push
|->UIViewController1 -----> UIViewController2 -----> UIViewController3 ----->
segue segue segue
You talk about passing data directly from VC1 (say) to VC3 by accessing the NavController's stack.
What you need to consider is that the storyboard describes a template showing how instances will interrelate when they are instantiated. They are not instantiated just because the storyboard is loaded. When you have arrived at VC1, the ability to segue to VC2 and VC3 is laid out before you in the template, but neither VC2 nor VC3 - as instances - exist until you initiate the segue. The segue process instantiates it's destinationViewController Therefore it makes no sense to pass data from VC1 directly into VC3. When you are at VC1, the navController's stack only contains one item (a VC1 instance); when you segue to VC2, it then contains instances of VC1 and VC2, and it is only when you actually segue to VC3 that the instance is created and placed in the stack.
Stepping through your code:
UINavigationController *project_NavigationController =
(UINavigationController *) self.presentedViewController;
the presentedViewController property works with modal segues, where you have a presenting, and a presented, view Controller. NavControllers on the other hand work with push segues (as they push child viewControllers onto their viewControllers stack, which is where you can obtain pointers to them from).
In this context, self.presentedViewController is nil, which you are assigning to a new variable. The code does nothing, but the compiler won't complain.
FileIO_ViewController *fileIO_ViewController =
[[project_NavigationController viewControllers] objectAtIndex:1];
project_NavigationControlle is nil, so it's properties are all nil. fileIO_ViewController gets nil.
Likewise in the last line you are sending a message to nil, which is not an error, but faintly redundant.

flex mobile project : memory management

I have developed my first flex mobile application which is of TabbedViewNavigatorApplication. Application is working fine but when I test the application in "profile handler", memory usage goes on increasing as I navigate through the application. When I came to know that, I have to remove all the added eventlisteners and I have to nullify the objects which are no longer needed. When I switch between tabs , tabs are initialising again and again.
I dont know where can I remove the eventlisteners. I mean, I have written functions for each eventlisteners . Do I need to remove eventlistener when control goes to the function definition.
I have written sample code
var more:Image = new Image();
more.width = 70;
more.height=29;
more.x=10;
more.y=276;
more.source = "Assets/more button.png";
more.addEventListener(MouseEvent.CLICK, MORE_clickHandler);
mainGroup.addElement(more);
private function MORE_clickHandler(e:MouseEvent):void {
// Do I need to remove the eventlistener here
}
Also , do I need to explicitly nullify the object of Image class which I created or garbage collector will handle it. If I need to explicitly nullify it, where to do this.
Thanks
Garbage collection is an important part of any language, especially on mobile. Since mobile devices are a lot more limited than say our desktop counterparts, you need to be very careful what is being created/stored to memory. My motto is, if you don't see it, you shouldn't keep it. You can destroy views but keep their state using a view model.
To remove a view, you need to first remove it from the display list (removeElement(yourObject)), remove all event listeners, and nullify any referencing variable. If any variable still has a reference to it, it won't get garbage collected.
I recommend you read up a bit more on garbage collection as well as some neat tricks like pooling and virtualization (item renderers in a list).
You can setup an event listerener with a weak reference.
This implies that when the only reference to your object is the listener, the object itself can still be garbage collected and the listener will not keep it in memory.
The following will do the trick :
more.addEventListener(MouseEvent.CLICK, MORE_clickHandler,false, 0, true);
Another option would be to subclass the image class and let it implement an IDisposable interface, which would force you to implement a dispose() method.
Some handy resources:
http://www.intriguemedia.net/2007/09/24/when-to-use-weak-references
http://gskinner.com/blog/archives/2006/07/as3_weakly_refe.html
cheers

Displaying TableView when MKAnnotation(Pin) in MKMapView touched/pressed/clicked

I want to display quite a bit of demographic data for a certain pin when someone touches on it, so providing a pop-up isn't going to cut it. I figured once the pin is touched I will just stick a tableviewController onto the NavigationController and the table view will have access to the object and display the single objects information, with one item per row and 1 section.
Anyway I'm having a hard time figuring out MKMapViewDelegates methods as it appears none of them do what I need and/or allow me to return a tableview or push that view onto the navigation controller.
I played around with:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation;
But that requires a MKAnnotationView be returned and I really just need this method to work by showing the user a table view of all the data. I was hoping for something simple like a userDidTouchPin method....
Anyone have any ideas how to accomplish what I am trying to do?
If you want to do something when the user selects the pin (and not a button on its callout), then implement the mapView:didSelectAnnotationView: delegate method and present or push your detail view controller there.
The selected annotation object is available as the annotation property of the view parameter:
- (void)mapView:(MKMapView *)mapView
didSelectAnnotationView:(MKAnnotationView *)view
{
YourDetailViewController *dvc = [[YourDetailViewController alloc] init...
dvc.annotation = view.annotation;
//present or push here
[dvc release];
}
You might need to check which type of annotation was selected (eg. if MKUserLocation was selected do nothing, etc) and you might need to cast the view.annotation to your own annotation class to easily access any custom properties you may have.

Resources