how Xcode automatically remove skspritenode form its parent's node - xcode

I'm new in objectif-c and sprite-kit development (but not in programming). So I'm starting the Apple's Spri. The app works perfectly, except that the nodes representing the rocks are automatically removed form its parent when rocks fall through the bottom of the scene. This, without implementing the -(void)didSimulatedPhysics method :
- (void)didSimulatedPhysics
{
[self enumerateChildNodesWithName:#"rock" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0)
[node removeFromParent];
}];
}
I can see that with the nodes counter shown on the screen : it doesn't increase with the number of rocks.
So my question is : is Xcode manage the removeFromParent method for my program ? if yes, could you tell me the option to disable. I try to sert ARC to NO, but it doesn't make any change.
I hope my message is clear. If not, please tell me. Thanks in advance for your answer.
Raphael

I can see that with the nodes counter shown on the screen : it doesn't
increase with the number of rocks.
This is because Sprite Kit counts only "rendered" nodes (ie nodes on screen) by default. To see culled nodes, you have to enable an additional, undocumented debug flag:
[self.scene.view setValue:#(YES) forKey:#"_showsCulledNodesInNodeCount"];
Alternatively, to get the true node count of a node, such as the scene, add this where you want to log the node count:
NSLog(#"node count: %u", (unsigned int)self.children.count);
In other words: if you don't remove a node from its parent, Sprite Kit will not do this for automatically under no circumstances. It will however clear up the node graph of the old scene when presenting a new scene, provided there aren't any retain cycles (commonly found when holding a strong reference to a parent or sibling node in a custom SKNode subclass).

Before you order an SKView to present a scene, you must be calling these lines:
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES; // This is what shows the node count.
From Apple's documentation, the showsNodeCount property is described as:
A Boolean value that indicates whether the view displays the count of
the nodes visible in the scene.
Hence, Sprite Kit does not remove them from their parent. If those nodes were to come back into the view's bounds, they would in turn increase this node count.

Related

Implement a Finder-like treeview and representing its nodes with lazy loading using NSOutlineview

When implement a big file system tree in NSOutlineview, it lacks an event notification when user click the left side down arrow triangle icon of a node. This is very important when lazy-loading a large amount of file nodes into a directory node and represent it into NSOutlineview. Otherwise, developer have to load entire directory into it, in this way, the loading will force end user to wait, this is not acceptable. In short, current version of NSOutlineview can't implement lazy-loading caused by this issue. Does anyone meet this issue or have an alternative solution to implement a Finder-like treeview with Cocoa NSOutlineview, any help will be great appreciate.
In Addition:
NSOutlineview does not emit an outlineViewItemWillExpand event when click the icon, and there still lacks enough info to get which node will expand even that event emitted, [NSOutlineview selectedRow] can't work because the node which will be expanded have not been selected yet.
I load my child nodes in the following call in NSOutlineViewDataSource:
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
guard let node = item as? Node else { return false }
return node.children.count > 0
}
The children property of my Node class is lazy, and hence forced to load here.
This method is only called on Nodes that are visible in the outliner. This way you are only loading one layer ahead of the visible tree. The disclosure triangle (expand arrow) will then be visible if children exist.
If you have an efficient way to know the number of children a node has without actually loading them, then it may be better just to return the child count in outlineView(_: isItemExpandable:) and then do the actual load in outlineViewItemWillExpand(_:) as Willeke has suggested in the comments.

MKMapView visibleMapRect not completely visible

I have a MKMapView that marks a specific region using MKPolygon. I want the map to have minimum zoom factor that fits inside the mapView / screen, means completely visible.
To achieve this I tried the following
MKPolygon *overlay;
[[self mapView] setVisibleMapRect:[overlay boundingMapRect] edgePadding:UIEdgeInsetsMake(16, 16, 16, 16) animated:YES];
Unfortunately my mapView does not set the whole rect visible. The rect fills the map instead of fitting.
This is what I need vs. what I got:
vs.
How can I achieve my goal?
I finally found the answer. It didn't have to do anything with the MKMapView. I just started using size classes in Xcode 6 and so there was my problem located.
The call to [MKMapView -setVisibleMapRect:edgePanning:animated:] was made too early, namely in the -viewDidLoad method.
When I was thinking of Annas comment and probed a bit I discovered the she was totally right. Then it dawned on me that I should call the mapView method later, e.g. -viewDidLayoutSubviews.
As I moved the one line it worked just perfectly! Thanks, Anna!
You want to pass [overlay boundingMapRect] through -[[self mapView] mapRectThatFits:].
A map rectangle that is still centered on the same point of the map but whose width and height are adjusted to fit in the map view’s frame.
If -setVisibleMapRect is set in -viewDidLayoutSubviews, then the visible maprect will be reset when the map is zoomed in/out.
My enhancement is: define a class level flag and invoke -setVisibleMapRect only if that flag is on. Once -setVisibleMapRect is invoked, turn the flag off. From now on, zooming in/out the map won't reset the visible maprect. Here is my code in Swift:
var resetMapRect = true // class level variable
override void viewDidLayoutSubviews() {
if resetMapRect {
// set visible maprect here
resetMapRect = false // no more setting maprect
}
}

NSOutlineView expand / collapse animation from code

i'm wondering how does one animate the expansion/collapse of an NSOutlineView's tree node from code ?
// this works ok but doesn't animate
NSTreeNode *node = [self.outlineView itemAtRow:self.outlineView.clickedRow];
if([self.outlineView isItemExpanded:node])
{
[self.outlineView.animator collapseItem:node];
}else{
[self.outlineView.animator expandItem:node];
}
an outline view naturally animates if you expand a node via the default-drawn arrow
so there IS a way...
My original code was OK, this just wasn't available under 10.7
Original text from Application Kit Release Notes for OS X v10.8 :
NSOutlineView
The following methods now support being animated via the -animator proxy: -expandItem:, -expandItem:expandChildren:, -collapseItem:, and -collapseItem:collapseChildren:. As an example, to animate the expansion of a particular item: [[outlineView animator] expandItem:item];
The problem is likely the node you are passing to collapseItem:. You need to pass the object your tree controller uses to represent the node rather than the actual node from your data model. If you are using NSTreeController, then you need to traverse the structure returned from -[NSTreeController arrangedObjects] to locate the node that represents your data model object.

UIimages move to the center when going to another view

I have a drag n drop kind of app. Where you can select images and drag them anywhere on the screen!
The problem I'm running into is, when you move to another view, all the images reset to the center, when I return back.
For example if I press a button to take me to screen 2, all the "dragged" images I just did, will move back to the center.
This only happens when I have AUTOLAYOUT enabled :(
I have all my images start out in the center, so I'm guessing its something with autolayout...
Any ideas ?!
Here's an example of my drag image code.
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
CGPoint translation = [recognizer translationInView:self.view];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:self.view];
}
Your drag and drop code is manually configuring the layout by mutating the view.center property.
Once auto layout is enabled, auto layout takes responsibility for setting up the layout, so it takes exclusive responsibility for setting view.center, view.bounds, and view.frame (which is actually just calculated from center and bounds).
So once auto layout is enabled, although you can still set view.center manually, auto layout will clobber whatever you do the next time it calculates the layout that satisfies the constraints you have in place.
So how do you update your code to work with auto layout? If you want to use auto layout, what you need to do is modify your handlePan: method so that instead of modifying view.center it modifies whichever auto layout constraint is being used to calculate view.center. The details of this will depend on your constraint configuration. But if we assume, for example, that there is an NSLayoutContraint topSpaceConstraint that sets the view's top space to the superview, and another NSLayoutConstraint leftSpaceConstraint that sets the view's left space to the superview, then you could produce the same effect as
view.center = CGPointMake(view.center.x + translation.x,
view.center.y+ translation.y);
by instead doing something like
topSpaceConstraint.constant = topSpaceConstraint.constant + translation.y;
leftSpaceConstraint.constant = leftSpaceConstraint.consant + translation.x;
[view setNeedsLayout];
[view layoutIfNeeded];
The first two lines update the constraints. The last two lines cause the auto layout system to recalculate the resulting frame and apply it right away, rather than waiting until the next turn of the run loop.
Alternatively, you might be able to get away with updating the frame manually when you get UIGestureRecognizerChanged events, and then making the result "permanent" by updating the constraints as shown above only once you get UIGestureRecognizerEnded event. That would be more performant, since during the many UIGestureRecognizerChanged events you'd be updating the frame directly rather than relying on the auto layout system to do it.

How do I fix the height of my view after collapsing NSSplitView?

I've got an NSSplitView with an NSScrollView in the bottom view. The problem is when I collapse, and then re-open (un-collapse) the bottom view, the height of the scroll view is beyond the height of that bottom view so the top part of the scoll view is being clipped. I've got my scroll view and my split view set to autoresize in all directions in IB. Do I need to adjust the height of that scroll view after the un-collapse or am I setting a resizing property wrong, or something else? Below is a before and after image of what the clipping looks like.
Before Collapse:
After Collapse and re-open (notice the scroll bar in the bottom view is clipped)
The problem stems from the fact that cocoa autoresizing rules work by scaling deltas from the previous state to the current state. If any of the margins go to 0 they'll never scale back up as the view grows because of the multaplicative nature of the scaling.
The typical approach to working around this is to use the NSSplitView delegate methods to prevent the split view from getting to small and then have it snap shut - which internally keeps the collapsed view at the minimum size.
Here's a link to the split view documentation.
Also, if you think about the user experience, your views probably look really awful when they're sized down below a certain point - views probably start overlapping, and becoming too small to show their content. Adding this snapping-collapsing behavior addresses both problems.
If you want to see an example of this, Mac OS X's Mail.app snaps its inline message view closed when it gets to a certain height. You should mimic that behavior.
I have the same problem. Fixed it using BWToolkit's split views, which allow you to determine the maximum and minimum height for each view.
You could "reset" things via NSUserDefaults, possibly.. There are keys for such things as NSSplitView Subview Frames, etc, to which you can assign coordinates, a la 0.000000, 0.000000, 0.000000, 720.000000, NO
While Jon Hess could describe the problem well (as soon as a subview's width becomes 0 the autosizing information gets lost for auto-width elements), the solution is not really given for all cases.
Constraining the width did not help in my case, as the subview can be collapsed.
I managed to achieve an acceptable solution, by implementing the splitView delegate method -splitviewWillResizeSubviews: to maintain a minimum width by setting the subview to hidden instead of shrinking it to zero:
- (void)splitViewWillResizeSubviews:(NSNotification *)notification {
NSUInteger divider = [[[notification userInfo] valueForKey:#"NSSplitViewDividerIndex"] intValue];
NSView *subview = nil;
if(divider == SPLITVIEW_DIVIDER_SIDEBAR) {
subview = (NSView*)[self.splitView.subviews objectAtIndex:SPLITVIEW_SIDEBAR_INDEX];
}
if(subview) {
if(subview.frame.size.width < SPLITVIEW_MINIMUM_SIDEBAR_WIDTH) {
CGRect correctedFrame = subview.frame;
correctedFrame.size.width = SPLITVIEW_MINIMUM_SIDEBAR_WIDTH;
subview.frame = correctedFrame;
subview.hidden = YES;
} else {
subview.hidden = NO;
}
}
}

Resources