In my iOS application's interface I have explicit undo & redo button (shake-to-undo is a pretty heavyweight action and in this context, undos are performed pretty frequently). Normally, I would use KVO to observe NSUndoManager's canUndo and canRedo key paths and enable & disable the buttons as the notifications come in. Unfortunately, NSUndoManager doesn't seem to be KVO-compliant for those key paths -- or at least, I'm not seeing anything in terms of notifications.
So my question is: How do I get this working? Am I just doing it wrong with KVO? (I've double & triple checked but there's always that possibility). Is there another way to do this that I'm not thinking of? (I've though about just checking the value of -canUndo every time the runloop spins but that has kind of a bad code smell to me).
(Note that it's quite possible that NSUndoManager is different on the iOS and Mac OS X, so I don't think "It works on the Mac" is going to be a helpful answer, in this case)
Register for your NSUndoManager's NSUndoManagerDidUndoChangeNotification and friends to examine when new undo groups are created and popped off the stack and update your button's states appropriately.
Related
Kind of a newbie question, so forgive me if I'm missing some basic concepts...
I have a view controller within a window, which contains multiple NSTableViews, each of which having a variety of associated observers added via addObserver. These work fine and as expected, with notifications going to each of the correct table views. However, if I close the window, then open a new window (of the same type), posted notifications are being sent to the table views of the window that was previously closed.
I was under the impression that as of macOS 10.11 observers did not have to be explicitly removed, which I would assume would happen when the original window is closed... UNLESS I'm missing something fundamental about closing windows. At the moment, I don't do anything special when the user closes a window, and the window just vanished from the screen. Do the views created in my viewDidLoad method live on even after the window is closed? Or no I need to explicitly dispose of these views in a method such as viewWIllDisappear?
Thanks!
Even when object you added as observer is disposed the observer is not. According to official documentation you have to pair each addObserver with corresponding removeObserver. This is true and for NotificationCenter and for KVO. By the way adding object as observer does not increase its owners, so under correct memory management left observer result in run-time crash - that is why needed paired removeObserver (if there is no crash in such situation it means there is leak).
The place where to do this depends on usage. If you add observer in viewWillAppear then it is better to remove it in viewWillDisappear, if you add on creation then remove should be done in deinit
I have checked the restorable option on my NSWindow
When I move my application and change its size and close/reopen my application it sets the window size and position to the last size and position
but this doesn't happen on every computer where i test it. it only happens to a few computers
they don't have special settings regarding the resume.
Does anybody have any experience with this?
But this doesn't happen on every computer where I test it. It only
happens to a few computers. They don't have special settings regarding
the resume.
Actually, they do. Take a look into the System Preferences > General pane. There should be an option named “Close windows when quitting an application” which is responsible for the restoration behavior. Additionally, in OS X Mountain Lion, Apple changed default behavior and now the app restores its state only when you quit using Command-Option-Q.
So, you probably should check which OS X version is installed on another Mac, and which preference is selected in the General pane. Hope this helps!
One thing you should know is that checking the "Restorable" option in IB only changes a window property. The actual restoring and saving is in your hands.
First, you have to conform to the NSWindowDelegate protocol and implement the
-window:willEncodeRestorableState:state and -window:didDecodeRestorableState:
methods that encode and decode your window properties (For example your windows frame, which you obviously get by calling [myWindow frame]).
You also need to conform to the NSWindowRestoration protocol and implement +restoreWindowWithIdentifier:state:completionHandler:. (make sure you set your class to restoration class, with the setRestorationClass method)
For additional, more in depth, information, you can visit this Apple Documentation document here.
If all you want is to restore the window's frame use setFrameAutosaveName:.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/WinPanel/Tasks/SavingWindowPosition.html
In many of my UIViewControllers, I update certain controls based on the state of my data. For example, I might have an edit button on a UITableViewController that should only be enabled when there is one or more items. Or perhaps I want to limit the number of items that can be added, and disable the 'add' button otherwise.
Every time I add or delete an item (or take any other action that can add/remove items), I have to remember to update any controls that might need enabling/disabling. This is trivial for the most part, but doesn't feel comfortable - there is a lot of repetition, and I have to remember to add the calls to updateControlEnabled (or whatever) whenever I add new functionality that might affect the data.
And then I noticed NSManagedObjectContextObjectsDidChangeNotification. Reading the docs, it looks like I can receive a notification whenever something changes in my managed object context. This seems ideal, but I have a few questions:
Is this an appropriate use of
NSManagedObjectContextObjectsDidChangeNotification?
Should I anticipate any performance impact if a controller
subscribes to these and parses each one to see if it needs to update
the UI? I will be checking the userInfo for every change, instead of
only those that I know I will care about.
Where should I subscribe to the notifications? My UIViewController has a
reference to the context, which helps, but I don't know where to
subscribe (loadView? viewDidLoad? init?) such that the view
controller will always have one and only one subscription.
The view controller will continue to receive and process notifications
when it's offscreen - enabling and disabling controls as the
data model is affected from elsewhere. Is this ok?
I guess I'm mostly just wondering if anyone else uses this approach and if so, what their experience is.
Q) Is this an appropriate use of NSManagedObjectContextObjectsDidChangeNotification?
A) Yes - I used it on OSX for a similar purpose.
Q) Should I anticipate any performance impact if a controller subscribes to these and parses each one to see if it needs to update the UI? I will be checking the userInfo for every change, instead of only those that I know I will care about.
A) NO - it will normally be a very small set of objects - ones that were directly changed.
Q) Where should I subscribe to the notifications? My UIViewController has a reference to the context, which helps, but I don't know where to subscribe (loadView? viewDidLoad? init?) such that the view controller will always have one and only one subscription.
A) Well, you cannot affect the UI til the view shows - so probably viewDidLoad or viewWillAppear. The problem with the later is you may get it a few times depending on push/pops, so maybe I'd do it in viewDidLoad.
Q) The view controller will continue to receive and process notifications when it's offscreen - enabling and disabling controls as the data model is affected from elsewhere. Is this ok?
A) Sure - when the view reappears all the elements will be setup correctly.
What you want to do is a classical use of that notification. Just check the thread it comes in on - if its not the mainThread then you want to make all your changes in a block posted to the mainThread.
UPDATE: With the blush of shame I discovered that the order had nothing to do with the speed of tapping. I was calling the visual code before the super touchesEnded:withEvent call, which was why if you tapped really fast, the display never got a chance to draw the highlighted state before being dismissed again. Because the code that was actually causing the main thread to block just a few milliseconds, the highlighted state would stay visible until the main thread unblocked again, where as if you tapped really fast, it looked like nothing happened at all. Moving the super call up to the top of the overridden method fixed it all. Sorry, if any moderator sees this post it can be deleted. shame
This problem must have been asked a 1000 times at SO, yet I can't find the explanation to match my specific issue.
I have a UIButton subclass with a custom design. Of course the design is custom enough that I can't just use the regular setSomething:forControlState: methods. I need a different backgroundcolor on touch, for one, and some icons that need to flash.
To implement these view changes, I (counter-intuitively) put the display code in (A) touchesBegan:withEvent and (Z) touchesEnded:withEvent:, before calling their respective super methods. Feels weird, but it works as intended, or so it seemed at first.
After implementing addTarget:action:forControlEvents was used to bind the UIControlEventTouchUpInside to the method (X) itemTapped:, I would expect these methods to always fire in the order (A)(X)(Z). However, if you tap the screen real fast (or the mouse in simulator), they fire in the order (A)(Z)(X). Where (A) and (Z) follow each other in such rapid succession, that the whole visual feedback for tapping is invisible. This is unwanted behavior. This also can't be the way to go, for so many apps need similar behavior, right?
So my question to you is: What am I doing wrong? One thing I'm guessing is that the visual appearance of the buttons shouldn't be manipulated in the touchesBegan:withEvent and touchesEnded:withEvent, but then where? Or am I missing some other well known fact?
Thanks for the nudge,
Eric-Paul.
I don't know why the order is different, but here's 2 suggestions to help deal with it.
What visual changes are you making to the button? If it's things like changing title/image/background image, you can do all this by modifying the highlighted state of the button. You can set a few properties like title and background image per-state. When the user's finger is down on the button, the highlighted state is turned on, so any changes you make to this state will be visible at this time. Do note that if you're making use of the selected state on the button, then you'll need to also set up the visual appearance for UIControlStateHighlighted|UIControlStateSelected, otherwise it will default back to inheriting from Normal when both highlighted & selected are on.
The other suggestion is to ditch touchesBegan:withEvent: and touchesEnded:withEvent: and switch over to using the methods inherited from UIControl, namely beginTrackingWithTouch:withEvent: and endTrackingWithTouch:withEvent:. You may also want to implement continueTrackingWithTouch:withEvent: and use the touchInside property to turn off your visual tweaks if the touch leaves the control.
I have asked a similar question here and got some answers, so first of all sorry for making you people bother once again.
But I have an argument this time. First I will show my piece of code
- (void) showTheAlert{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Hey!" message:#"?" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Yes",#"No",#"Don't know eaxactly.",nil];
[alertView setTag:101];
[alertView show];
}
- (void)willPresentAlertView:(UIAlertView *)alertView{
if(alertView.tag == 101){
[[[alertView subviews] objectAtIndex:2] setBackgroundColor:[UIColor colorWithRed:0.5 green:0.0f blue:0.0f alpha:0.5f]];
[[[alertView subviews] objectAtIndex:3] setBackgroundColor:[UIColor colorWithRed:0.0 green:0.5f blue:0.0f alpha:0.5f]];
}
}
And my final alert looks like
Now my confusion is that, [alertView subviews] is not documented as some people may say but alertview is a subclass of UIView, which has a property called subviews.
So I am using the documented property of a superclass which is definitely allowed.
So if this alertview may cause rejection of my app or not? ( I don't think apple will have any base to say I am using the undocumented or a private api. The look and feel is also alike to alertview.)
Apples iPhone Human Interface Guidelines about alert views clearly states:
The infrequency with which alerts appear helps users take them seriously. Be sure to > minimize the number of alerts your application displays and ensure that each one offers > critical information and useful choices. In general, try to avoid creating alerts that:
Update users on tasks that are
progressing normally.
Instead, consider using a progress
view or an activity indicator to
provide progress-related feedback to
users (these controls are described
in “Progress Views” and “Activity
Indicators”).
Ask for confirmation of
user-initiated actions.To get confirmation for an action the user initiated, even a potentially risky action such as deleting a contact, you should use an action sheet (described next in “Using Action Sheets”).
Inform users of errors or problems
about which they can do nothing.
Although it might be necessary to use an alert to tell users about a
critical problem they can’t fix, it’s
better to integrate such information
into the user interface, if possible.
For example, instead of telling users
every time a server connection fails,
display the time of the last
successful connection.
So, my advice, the time waiting for a potential rejection isn't worth your time. Don't risk it.
To follow on Henrik's reply, in the iPhone Human Interface Guidelines section "Designing an Alert", they say the following:
Although you can choose the number of
buttons to place in an alert, a
two-button alert is often the most
useful, because it is easiest for
users to choose between two
alternatives. It is rarely a good idea
to display an alert with a single
button because such an alert cannot
give users any control over the
situation; instead, it can only
display information and provide a
dismiss button. An alert that contains
three or more buttons is significantly
more complex than a two-button alert,
and should be avoided if possible. In
fact, if you find that you need to
offer users more than two choices, you
should consider using an action sheet
instead (see “Using Action Sheets” and
“Designing an Action Sheet” for more
information on this type of view).
Because users sometimes respond to
alerts without reading them carefully,
be sure to provide an appropriate
default choice. To help guide
inattentive users towards this choice,
make the light-colored, right-hand
button the safe, default alternative.
For example, you might choose to make
this button the Cancel button, to help
users avoid inadvertently causing a
dangerous action, or you might make it
represent the most common response, if
the resulting action isn’t
destructive.
The following guidelines describe how
buttons are configured in an alert:
In an alert with two buttons, the button on the left is always
dark-colored and the button on the
right is never dark-colored.
In a two-button alert that proposes a potentially risky action, the button
that cancels the action should be on
the right and light-colored.
In a two-button alert that proposes a benign action, the button that
cancels the action should be on the
left (and therefore dark-colored).
In an alert with a single button, the button is light-colored.
You are clearly violating the guidelines in size, shape, number, and color of the buttons in your alert view (red has a very clear meaning as a destructive action, not a confirmation). Even if Apple doesn't reject your application in review (which they tend to do for clear violations of the Human Interface Guidelines), this would be extremely confusing to your users.
Also, navigating the hidden view hierarchy for any Apple-supplied user interface element is a very bad practice. The view hierarchies are undocumented, and do change often. Many of the applications that started crashing when people upgraded to iPhone OS 3.0 did so because those applications did something funky with subviews of UI elements, and those elements changed in the new OS version. Apple even specifically called this out in the iPhone OS 3.0 migration documents (which I can't find now).
Because of the problems this caused, they appear to have cracked down on this practice and have been rejecting applications because of it. Even if they don't, it shows contempt for your users if you do this, because it means that you don't care if your application breaks with future OS upgrade.
I'm fairly sure altering UIAlertView by digging through the view hierarchy is a no-no. Firstly because it "uses standard iPhone screen images in a non-standard way, potentially resulting in user confusion", and secondly because if they change the view hierarchy your app is broken.
I might be wrong, I've never tried to get something like this onto the store, but my gut says don't risk it.
You can get a red button using a standard UIActionSheet, can you not?