I have a method that sets up UIView properties. And I call it directly in some cases and from UIView animateWithDuration: block. In this method I change among other things path of view's layer, so it works incorrectly with animation (path must be animated explicitly).
How can I determine that my method is called from inside [UIView animateWithDuration:...]?
[UIView animateWithDuration:duration animations:^{ [myClass myMethod]; }];
...
- (void)myMethod
{
if (__view_is_animating__) // how to check here?
{...}
}
At current moment I set custom viewIsAnimationFlag before [UIView animateWithDuration:...] and check it in myMethod but it's rudely.
Thanks to David Rönnqvist at objc.io:
"the view returns NSNull whenever the layer asks for an action (animation handler for any animatable property), except when the property change happened inside of an animation block"
NSLog(#"we are inside animation block: %#",
[myView actionForLayer:myView.layer forKey:#"position"] ? #"YES" : #"NO");
Related
I have a subclassof NSWindowController with an associated xib file.
From my app delegate I display this using the following code:
if(!wc)
wc = [[NSWindowController alloc]initWithWindowNibName:#"MyNewWindowController"];
[wc showWindow:nil];
This displays the window. Now I want to reference that window in the new window controller but can't work out how. Specifically I have a button on the new window and I want to write something like:
- (IBAction)doStuffAndCloseWindow:(id)sender
{
[self doSomeStuff];
[*window* orderOut:nil];
}
I've tried creating a window variable (like the one created in appdelegate) but the compiler says my window variable is private.
So have do I declare and reference a window in my MyNewWindowController.m?
Thanks
That would be the 'window' method of NSWindowController. It's also a property that you can access via ".window".
So, in the first code snippet, that would be:
[wc window]
and in the second code snippet (assuming "doStuffAndCloseWindow" is part of your subclassed NSWindowController):
- (IBAction)doStuffAndCloseWindow:(id)sender
{
[self doSomeStuff];
[[self window] orderOut:nil];
}
Thanks to Michael, see above, first declare your subclassed NSwindowController thus
#property IBOutlet MyNewWindowController *wc;
Then in the implementation of the subclassed window controller, you can refer to the associated window with
[[self window] .....];
For example
[[self window] orderOut:self];
In normal case, a blue retangle would appear outside a NSTextField object which becomes the first responder, like this image:
link for Normal Case
However, I got a NSTextField that have no the blue border outside. Why is that?
Here is how it happerns:
1> I create a typical MAC OS app.
2> I switch app's subview by calling the corresponding view's addSubview: and removeFromSuperview methods.
3> In one subview (which is actually the image referenced above) I click the "Next" button. Its action is something like this (defined in the subview's controller .m file):
- (IBAction)actionNextClicked:(id)sender{
//_hdlThreadNext is a NSThread object
[[_hdlThreadNext alloc] initWithTarget:self selector#selector(threadNext:) object:nil];
[_hdlThreadNext start];
}
And the thread is like:
- (void)threadNext:(id)sender{
#autoreleasepool{
BOOL success;
[CATransation begin];
/* send username and password and decrypt responce */
... // balabala... and set "success"
if (success){
[[self view] removeFromSuperview];
[self sendMessageToSuperview:#"Add Next View"]; // Superview's method, telling superview to call addSubview: to add another subview
}
else{
/* Nothing special to do */
}
[CATransation commit];
}
}
4> The subview switch to another one. Its combo view seemed to be OK: image for combo view
But the other NSTextView's blue border would NOT appear anymore!
Does Any guy know what wrong I had done? Thank you very much!
Perhaps I did totally wrong programming, so that few people met this problem.
I found a way to solve this problem. I mentioned that all (or most of?) the graphic changes should be done in main thread in a blog. Therefore I change the "if(success)" as:
if(success){
dispatch_async(dispatch_get_main_queue()' ^{
[[self view] removeFromSuperview];
[self sendMessageToSuperview:#"Add Next View"];
});
}
Solved, the focus rings come back.
when we touch the tabbaritem of the tabbarcontroller the delegate methods are called:
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController;
but when try to do the same thing programmatically, i.e.
[self.tabbarController setSelectedIndex:selectedIndexNo];
or
[self.tabBarController setSelectedViewController:[self.tabBarController.viewControllers objectAtIndex:0]];
the delegate methods are not called. What is the reason for that?
override UITabBarController setSelectedIndex:
-(void)setSelectedIndex:(NSUInteger)selectedIndex
{
//must call super function.
[super setSelectedIndex:selectedIndex];
[self myMethod];
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
[self myMethod];
}
When you are setting them yourself via code, than you are aware that this is the time when the delegate method will be called. so whatever you wish to do you can do it at the time of setting the index programmatically. Say you want to call a method aMethod on tabbardelegate being called. you can call the method as soon as you set the index.
[self.tabbarController setSelectedIndex:selectedIndexNo];
[self aMethod];
I have a UIPopoverViewController that is displaying a custom UIViewController properly. When I click a button I have an action run and as a result I add a view to the view hierarchy of the UIViewController's view.
The problem is that it is very slow, and takes several seconds for the view to appear. I'm not doing anything out of the ordinary with my UIViewController's code.
- (void)showAccountChooser {
self.twitterAccountPicker = [TwitterAccountPicker new];
[self.view addSubview:self.twitterAccountPicker.view];
self.twitterAccountPicker.view.frame = self.view.bounds;
self.twitterAccountPicker.view.transform = CGAffineTransformMakeScale(.05, .05);
[UIView animateWithDuration:0.5f animations:^{
self.twitterAccountPicker.view.transform = CGAffineTransformMakeScale(1, 1);
} completion:^(BOOL finished) {
//[self.twitterAccountPicker viewDidAppear:YES];
}];
}
The UIViewController that I'm adding is trivial and does not heavy processing in the viewDidLoad or viewWill/DidAppear. I have set break points and verified that it is not doing anything bad.
Anyone else notice this when adding views?
After setting break points trying to debug this, I realized that my showAccountChooser method was being called from a block invoke, which was happening on a background thread. Moving this call to the main thread resolved the issue.
I tried to call [self setNeedsDisplay:YES] in a NSTimer selector in order to trigger the drawRect method.
At first, I put the NSTimer init code in a button func:
-(IBAction)buttonPush:(id)sender
{
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(myTimerAction:)
userInfo:nil
repeats:YES];
}
-(void)myTimerAction:(NSTimer *) timer
{
[self setNeedsDisplay:YES];
}
The "setNeedsDisplay" is called normally but the code inside the drawRect is never called:
- (void)drawRect:(NSRect)dirtyRect
{
NSLog(#"drawRect");
}
Then I tried to move the NSTimer init code to "- (id)initWithFrame:(NSRect)frame", then everything works just fine.
(the drawRect is called correctly every 1 sec).
What's the difference between the two methods above?
What should I do if I want to trigger the Timer in a button?
Just wondering, in what class does that code reside? I would assume the buttonPush: action is inside a controller, correct?
If so, then you should have:
-(void)myTimerAction:(NSTimer *) timer
{
[[self view] setNeedsDisplay:YES];
}
because setNeedsDisplay: is a method of NSView, not NSViewController.
(BTW probably the reason why it works if you put it inside initWithFrame: is because that one is a NSView initializer: I'm guessing that when you move the code there you are also moving the myTimerAction: method, which then has "self" referring correctly to the view.)
Since it works when you use initWithFrame:, the problem is probably that buttonPush: isn't hooked up correctly. Try setting a breakpoint in buttonPush: an see if it is actually called when you click the button.