2

In a UIView I have a nav button with an IBAction & method in the top-level view controller.

In the IBAction code, I flip a boolean so that when execution returns to the UIView, there's some new setup prior to drawRect: repainting the view.

If all this were in the ViewController, I could put the new setup code in something like ViewDidAppear so it executes each time the button is pressed. However, there's no such method at the UIView level. There is initWithCoder, but this only seems to be executed once (when the storyboard/nib loads).

So my question is - either, is there a way to call the initiWithCoder method explicitly from my IBAction at the VC level (I've tried [self initWithCoder:nil] but the breakpoint at the UIView level doesn't trigger) or is there a method that runs when execution returns to the UIView level, a la ViewDidAppear?

Thanks

Image of goal: enter image description here

Tony
  • 373
  • 5
  • 18

1 Answers1

2

Unless you really know what you're doing (I mean really know), don't call -initWithCoder: yourself. You're meant to implement it just as you implement -drawRect: and let the system call it. If you ever find yourself calling something like this directly and you can't explain the deep technical reasons why there's no other way, then it's the wrong approach. Read and follow the documentation (not just the method's doc) to make sure you understand whatever method you're using. It'll tell you.

That said, what you're wondering is if there's a point in a view's lifecycle where you can "do something" (check a BOOL and perform some work if YES/NO) any time the view "appears". The answer is yes, and -willMoveToSuperview "can" work.

BUT

That's the "wrong" approach, IMO. The BOOL property ('draw a twiddle next time I'm asked to draw) can and probably should live in the UIView, but its state should be set in its controller since this is specific to your app. Views are supposed to be (highly) reusable; controllers are supposed to implement your app's specific logic and drive the views according to the model state and user (or system) actions.

So: when you want to enable the "draw a twiddle" operation, your view controller should set the view instance's drawTwiddle flag then probably flag the view for drawing. Your view will then have -drawRect: called at some point you shouldn't try to control and, when it does, it sees that self.drawTwiddle == YES and draws the twiddle along with whatever other drawing it does.

At that point, you might be tempted to have the view set its own drawTwiddle flag to NO since the behavior is intended to fire once. Don't do this. BEWARE: Other user actions or system events may call -drawRect: at any time so the twiddle may not actually be seen by the user (it may appear and disappear faster than is visible). 'So', the right thing to do is to make the controller (via some direct action, system event, or timer) responsible for setting and unsetting the drawTwiddle flag, then flagging the view for redisplay.

Adding

It's also unusual to put an IBOutlet or an IBAction in a UIView. Most of the time, unless you're creating some compound control whose parts aren't intended to be accessed and managed individually, your architecture is clearer (and more closely follows the spirit of the MVC design pattern) by letting the controller manage/own the outlets and actions.

Joshua Nozzi
  • 60,946
  • 14
  • 140
  • 135
  • This makes a lot of sense and thanks for the reasoning. I will ensure my button toggle code in the VC controls the boolean state and look to test it in drawRect: and adjust the view accordingly. My only concern at this point is that I really need the button press on the nav bar to instantly change an object on the view so I'll need to determine if/how to do that. I guess I could explore some place to call setNeedsDisplay. Thanks again. – Tony Feb 25 '13 at 01:43
  • I'm not sure I understand. Navigation actions are normally fired on "touch-up" (when the button is touched and released) to give the user a chance to touch, say "whoops", and slide their finger off the button before releasing to cancel the event. It's unwise to short-circuit this. Can you explain why this is so important and what, ultimately, you're trying to do? I fear you might be focused entirely on too specific a solution to your problem. – Joshua Nozzi Feb 25 '13 at 01:55
  • Sure. When the nav button is pressed, IBAction code in my VC runs. This is where (per your advice) I'll set the drawtwiddle state there but I notice in the debugger that drawRect: (where I will test the state of drawtwiddle and change the view accordingly) isn't entered until (e.g.) I touch the screen and my gesture recognizer calls setNeedsDisplay. If I just press the button and don't touch the screen, nothing happens (as I would expect). – Tony Feb 25 '13 at 02:44
  • First, it's not for you to know when -drawRect: is called. It's only your place to ask the system to redraw it "when it's convenient" (graphics performance reasons). Second, I think you misunderstood the scope of my request. I'm looking for "I need to flash an indicator just before the menu switches" or "I need to play a 'click down' sound when the nav button is down, then a 'click up' sound when it's released" or something similar. I'm looking for a high-level explanation of your ultimate goal since there may very well be a better way. That way depends on your goal, though. :-) – Joshua Nozzi Feb 25 '13 at 04:12
  • AH, ok. Thanks for the patience :). I have a user-selected photo, a semi-transparent black layer over it, a "top" UIView and then a 100 x 600 horizontal UIView programmatically generated on the very top that essentially punches a hole through to the original photo. This horizontal window can be dragged about. The goal of the button is to turn it into a vertical window that can be dragged about. All that code (drawRect, gestures) happens in the "top" view. The button code is in the VC. (image in original post) – Tony Feb 25 '13 at 04:26
  • What does the tapping *do*, though? Are you saying tapping the "punch through" view applies the actual hole to the photo? Why does this have to happen before a complete touch-release cycle like most any other UI action? I'm unfortunately still confused as to your goal. Might be time to open a new question as this sounds very different from what you originally asked and what was answered. Just how this site works. :-) – Joshua Nozzi Feb 25 '13 at 14:09
  • Looking again, if your goal is to drag a visual filter around (like a loupe but an affects-applied loupe), it seems to me this whole thing is overcomplicated. :-) All of it could be achieved with a single UI view handling touch events and its own custom drawing. Maintain an unmodified copy of the original image but *draw* the modified copy with all the loupe decorations depending on where the last touches were. It's just that none of this really explains why you want to short-circuit the normal "change property, ask for refresh, draw current state when asked" cycle. – Joshua Nozzi Feb 25 '13 at 14:11
  • Thanks. You may be right :) I'm sure there's a more elegant and efficient way of achieving the broader goal, but my original issue remains - when I press the nav bar button, I can trigger an IBAction event (at the VC level) fine - in order to change the drawtwiddle state. However, the screen does not change visually (from horiz to vert or vice-versa) until I make a gesture (such as attempting to move the existing horiz window - then drawRect kicks in and bang - the window changes to vertical. That's why I was asking if there's a way to force that redraw on the basis of the button press alone. – Tony Feb 25 '13 at 17:03
  • OH. I *think* you might be making an assumption about what's happening behind the scenes. Time to post your IBAction code (in a new question describing the problem and your goal since it's significantly different from the question you asked and got an answer for above). Definitely time for a new question *with code*. :-) – Joshua Nozzi Feb 25 '13 at 17:48
  • OK. Will do - about to hit the road so may be a while. I'll pop the link to the new question as a comment here so you can find it. Thanks again Joshua. – Tony Feb 25 '13 at 18:47
  • New question posted. http://stackoverflow.com/questions/15084270/button-to-trigger-drawrect-to-redraw-a-view – Tony Feb 26 '13 at 08:12