Is using subviews in Alert undocumented - objective-c

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?

Related

Cocoa Message with no action required

I'm trying to figure out a way to give a user feedback when they have saved settings. similar to Microsoft's "File Saved" dialog Is there a class for this type of dialog? I do not want to require any action by the user. Just "Your setting have been saved" then disappears after a short delay. Maybe a better way to describe would be like a jQuery message box with a fade in fade out type thing
Is there a class for this type of dialog?
That isn't a "dialog", because you're not accepting input from the user. At best, it's an alert, and you could therefore use NSAlert (see also "Dialogs and Special Panels") however, what you are contemplating is contrary to the recommendations given in the HIG for "Alerts":
Avoid using an alert merely to give users information. Although it’s important to tell users about serious problems, such as the potential for data loss, users don’t appreciate being interrupted by alerts that are informative but not actionable. Instead of displaying an alert that merely informs, give users the information in another way, such as in an altered status indicator.
In other words, this probably wouldn't be considered a good user experience by the OS X-using population.
You can still do this, if you absolutely must, by creating a sheet or alert window and setting a timer to dismiss it.
A much better plan would be to have a label somewhere in your interface whose text could display this information, again using a timer to clear the notice after an appropriate duration.
Yet another option (possibly the best) would be to put this notice somewhere that the user only sees it upon request. The HIG mentions Mail.app's information area at the bottom of its sidebar, for example.
It is simple to fade a window in and out using the NSViewAnimation see also NSAnimation Class
An example I use something like this.
- (void)fadeWindowIn{
//--make sure the window starts from 0 alpha. Or you may get it jumping in view then try and fade in.
[theWindow setAlphaValue:0.0];
//-- set up the dictionary for the animation options
NSDictionary *dictIn = [NSDictionary dictionaryWithObjectsAndKeys:
theWindow,NSViewAnimationTargetKey,
NSViewAnimationFadeInEffect,NSViewAnimationEffectKey,nil];
NSViewAnimation * fadeWindowIntAnim = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dictIn]] autorelease];
[fadeWindowIntAnim setAnimationCurve:NSAnimationLinear];
[fadeWindowIntAnim setDuration:2];
[fadeWindowIntAnim setFrameRate:20.0];
//--start the animation
[fadeWindowIntAnim startAnimation];
//--set the timer for the fade out animation
[NSTimer scheduledTimerWithTimeInterval:4.8 target:self selector:#selector(fadeWindowOut) userInfo:nil repeats:NO];
}
-(void)fadeWindowOut{
//-- fade the window.
NSDictionary *dictOut = [NSDictionary dictionaryWithObjectsAndKeys:
theWindow,NSViewAnimationTargetKey,
NSViewAnimationFadeOutEffect,NSViewAnimationEffectKey,nil];
NSViewAnimation * fadeOutAnim = [[[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:dictOut]] autorelease];
[fadeOutAnim setAnimationCurve:NSAnimationLinear];
[fadeOutAnim setDuration:1.2];
[fadeOutAnim setFrameRate:20.0];
[fadeOutAnim startAnimation];
}
theWindow is the NSWindow or NSView you want to fade in and out. Read the references to understand the options.
You can create your own such popup (using NSTimer to dismiss as needed), but perhaps an easier way would be to use the existing third-party library at http://code.google.com/p/toast-notifications-ios/. This library emulates Android's "toast" functionality.
Note that this library is for iOS development (not OSX), but wasn't sure which platform you were planning to target. Regardless, it should be adaptable with a little work.
The other answers about timers and such cover that aspect of it pretty well. I just wanted to jump in and suggest you take a look at the Growl framework. This seems to be the preferred way to do this sort of passive notification until Apple builds it into the OS.
Among other things, it gives the user a lot of control over how the notifications look, where they live on the screen, how long they stay up, and which apps are even allowed to display them. And they do this without you having to write any code. The downside is that it's another thing for your users to have to install, which could be a deal breaker for your app.
They also recently moved into the App Store and started charging a nominal fee ($2 or $3, I think) which could be seen as a downside but I think of it as a more positive thing: users will have a much easier time installing it now.
Some apps that make use of Growl notifications include BBEdit, Transmission, Scrivener, Twitteriffic, etc. Which is to say that it's not a fly-by-night thing.
As a user, I hate it when apps try to roll their own notifications since I lose all of the control that I get with Growl.
Just a thought, anyway.

Flip UIAlertView

I have not found a way to do this without using private APIs, which is quite a shame, as it is something my app and many others could benefit from. I have an UIAlertView with a Details... button on it, which I want to sort of 'flip' to the other side and show a different UIAlertView, much like UITwoSidedAlertViewController, but not using any private APIs.
First of all, is this even possible? I looked over the UIAlertView header, but I couldn't see anything relating to this. UIAlertView does inherit from UIView, so I suppose that simple view animation might just be the key, but as I said, I've never done anything like view animation flips.
Why not just use one of the several UIAlertView replacements that have been created and then add the flip functionality yourself?
You can find a UIAlertView replacement here: https://github.com/TomSwift/TSAlertView
The flip can be created using a standard flip animation block via [UIView transitionWithView ...]

Why does the execution order of touchesBegan, target-action and touchesEnded change with fast touches of UIButton?

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.

NSUndoManager and enabling/disabling an undo button

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.

Logging all cocoa events?

I'm doing usability testing and would like to log all user input events: mouse movements, clicks, drags, and keyboard input. I'm not having much luck figuring out how, or finding any code to do so. Any hints? I took a look at the CoreGraphics EventTap mechanisms, but I'm worried that it will be too low-level; I'd like to actually know what particular UI elements the user clicks on.
Edit to clarify:
I'm doing usability testing, so I want to keep track of what parts of the interface the user uses and doesn't use. So, I want to keep track of "Button 'foo' was clicked 7 times at these particular timestamps, the user scrolled through this list and selected such-and-such item" and so forth.
If you're just looking to track them for your application, you could override -[NSApplication sendEvent:]. It's responsible for dispatching all the events your application receives to the appropriate responders.
I think you'll need to do a lot of swizzling.
Try swizzling tryToPerform:with: first; if that doesn't work, you'll need to swizzle such methods as mouseDown:, mouseUp:, the drag event methods, keyDown:, keyUp:, and the undocumented gesture methods.
One complication in the latter solution is that you'll need to swizzle those methods on not just NSResponder, but on several of its subclasses, because many view classes provide their own implementations, which may or may not call up to their ancestors' implementations.
Instruments has a way to record user interface events. It seems like you might be able to use the dtrace calls that underlie this to accomplish what you're going for.
This is completely different than what you're thinking about right now, but consider something like Silverback if you haven't yet.