Monotouch - UIView.Animate does nothing in a subview why? - uiview

I have a virtual function called DoRotate() that does an animation of a subview rotating. Pretty simple... It is fired from a timer event, and is always invoked on the main thread.
This works like a charm...
Now, I sub-class the whole view, and create another view that tries to do the same thing. It is displayed as a sub-view (full screen) of the main view... Now, UIView.Animate() is a No-op.. It never executes the code inside the block at all. It is actually the EXACT same function that is being called in the other view... It is attempting to animate a sub view (a sub-sub-view of the original view...
But it is like the OS is just saying "too bad, so sad, we are not going to work anymore.." - which is typical of the OS,but that is another topic...
Originally, I tried to just make this view another view that would flip over to from the base view, but apparently, without a navigation controller it won't work, and if you have one then the popup menu that I created will no longer popup...
UIView.Animate (tm, 0, UIViewAnimationOptions.CurveLinear, () =>
{
MapRotating = true;
_DoRotate ();
},
() => {
MapRotating = false;});
}
The MapRotating = true; never gets called.. I've put this before the animate call, but then it never tries again if the animate fails... This way, it keeps failing forever...
_DoRotate computes the angle to rotate and rotates it (sets a transform)...
Is there a UIView.LastError() method that I can call for it to tell me WHY is it Ignoring my request... (ooh, how about throwing an exception if we passed it something it didn't like?)
-Chris

Related

How can I force update my UI immediately in Swift 5 using UIKit?

I ran into a problem with my UI, which is not updating immediately.
I am calling someCustomView.isHidden = false first. After that I create a new instance of a new View Controller. Inside the new VCs viewDidLoad(), I am loading a "new Machine Learning Model", which takes some time.
private func someFuncThatGetsCalled() {
print("1")
self.viewLoading.isHidden = false
print("2")
performSegue(withIdentifier: "goToModelVCSegue", sender: nil)
}
As soon as I press the button that calls this function, "1" and "2" is printed in the console. However the view is not getting visible before the viewDidLoad() of my new VC is finished.
Is there any possibility to force update a UIView immediately? setNeedsDisplay() did not work for me.
Thanks for your help!
Use layoutIfNeeded() Apple Docs
layoutIfNeeded()
Lays out the subviews immediately, if layout updates are pending.
Use this method to force the view to update its layout immediately. When using Auto Layout, the layout engine updates the position of views as needed to satisfy changes in constraints. Using the view that receives the message as the root view, this method lays out the view subtree starting at the root. If no layout updates are pending, this method exits without modifying the layout or calling any layout-related callbacks.
So As a rule of thumb,
layoutifneeded : Immediate (current update cycle) , synchronous call
setNeedsLayout :relaxed ( wait till Next Update cycle) , asynchronous call
So, layoutIfNeeded says update immediately please, whereas setNeedsLayout says please update but you can wait until the next update cycle.
how to use
yourView.layoutIfNeeded()
You can also refer to the diagram to better remember the order of these passes
Source Apple docs on layoutIfNeeded
Image credit Medium artcle
A couple problems...
If you have a view controller that "takes some time" to load, you should not try to do it in that manner.
The app will be non-responsive and appear "frozen."
A much better approach would be:
on someFuncThatGetsCalled()
hide viewLoading and replace it with an activity indicator (spinner, or something else that let's the user know the app is not stuck)
instantiate your ModelVC
when ModelVC has finished its setup, have it inform the current VC (via delegate)
current VC then shows / navigates to the already instantiated and prepared ModelVC
Or, probably a better option... Move your time-consuming setup in ModelVC to a point after the view has appeared. You can show an activity indicator in viewDidLoad(). That is really the most common UX - you see it all the time when the new VC has to retrieve remote data to display - and it would fit wit what users have come to expect.

How to stop a previous viewController's activity after being popped

I have a navigation controller that segues, using push, to a second view controller. I use:
controller.navigationController!.popToRootViewController(animated: true)
And this works. This code causes my controller to be popped off the stack and it returns to the root view. However, I can still hear my code running (e.g. music/sound effects) in the root view.
I have explored a few avenues.
One is to create a function that runs when my back buttons is pressed. Currently, it removes all the actions/pauses all the music:
func pauseAndRemoveEverything() {
for child in fgNode.children { child.removeAllActions() }
for child in bgNode.children { child.removeAllActions() }
soundEffectsPlayer?.volume = 0
musicPlayer?.volume = 0
soundEffectsPlayer?.stop()
musicPlayer?.stop()
}
The second option is to use multi-threading and dispatching little blocks of code; then cancelling them all when the user hits the back button. Currently my code is all on timed blocks, and I don't know how the dispatch will affect this, or whether it is more trouble than it is worth.
I want the actions on the previous controller to completely cease on segue, and reduce the effect it has on memory and energy use when this occurs. How do I do this?

UIVIewController and UIView - How to refresh UIView

After having spent the whole day looking for a solution I feel only more confused and upset.
Let's face the problem:
I'm developing a single view iOS app made up of an AppDelegate (of course..), A ViewController and a "DrawingClass" (subclass of UIView).
In the main.storyboard i can see my mainViewControllerScene, and inside this main view directed by the viewController, I have inserted a UIView object from the palette in the interface builder and set it to be controlled by my "Drawing class" because I need to use the DrawRect method to draw custom lines.
Well, when starting the app, the defaults lines in my "DrawingClass" are being drawn, so drawrect is being called.
But when, after having pressed a button linked to an IBAction, I try
to call again drawrect through setNeedsDisplay or anything it doesn't
work.
Let's be more clear:
-I'm sure that the view controlled by "DrawingClass" is being drawn correctly on startup
-I'm sure that the IBAction is called (I used an NSLog)
-I can't figure out how to redraw that view. (The one controlled by "DrawingClass")
In the viewController I tried both [self.view setNeedsDisplay] and [myView setNeedDisplay] but none of them called my drawrect method in the "DrawingClass"
What I'm doing wrong? Am I forgetting to init something ? I tried even to call those methods on the main thread but nothing.
I think this question could help many so please ask if you need something more to work out this problem.
Thank you so much.

setNeedsDisplay is submitted on main queue but is not called

The method below is called on a non-main thread, to be specific, in a recording audio queue callback
- (void)myMethod
{
//...
dispatch_async(dispatch_get_main_queue(), ^{
[myGraphView setNeedsDisplayInRect:CGRectMake(a, b, c, d)];
NSLog(#"Block called");
});
//...
}
where myGraphView is a custom UIView object. For what I know, setNeedsDisplayInRect: should be called on main thread which is why I have dispatch_async... in place. Now the problem is the method - (void)drawRect:(CGRect)rect I implemented for myGraph is never called even though the NSLog in the block has been called for many times.
There are a few possibilities here.
From the Class Reference:
Note: If your view is backed by a CAEAGLLayer object, this method has
no effect. It is intended for use only with views that use native
drawing technologies (such as UIKit and Core Graphics) to render their
content.
The other option, which is probably the cause in this case, has to do with the actual geometry. If the provided rectangle is invalid or off screen, the call does nothing. I would suggest you verify the that the rectangle is being calculated as it should be.
Thanks to #Neal's answer which led me to find out that myGraphView was, after it had been alloc-inited the first time, alloc-inited again. However, unlike the first alloc-init after which I added myGraphView to its superview, I forgot to do so after the second alloc-init.
The lesson I've learned here is that when a view is not doing what it's expected to do, such as not being displayed or updated, always check this third possibility where you forget to add it back to its superview after it's got alloc-inited again somewhere in your code. Also, if the view has a delegate you would tend to forget to set it as well.

Data-init function executing twice

I have a problem, I dont know why but my data-init function in a main view is being called when I navigate back to that page for the first time. I want to separate some initialization logic from show logic in the starting view.
View is defined as a first (and only) view inside the body element.
<div data-role="view" id="..." data-model="..." data-init="initFnc" data-show="show">
</div>
I create the app like this:
var app = new kendo.mobile.Application(document.body, { transition: "slide" });
So once again sequence of events, just to be clear:
app started, main view opened -> init and show functions called
navigate away to another view, navigate back -> init and show functions called
navigate away to another view, navigate back -> show function called
In step 2, I want to call only the "show" function.
Thanks!
This behaviour is not normal - the init event should be fired once. Most likely your navigation goes wrong and loads your homepage as a remote view. Or you instantiate the app multiple times.
That does not seems to be the case, what you shared looks completely valid. Take a look in this demo.

Resources