30

I'd like to display a temporary message on the iPhone/iPad displaying confirmation of an action, or some quick status about some background activity.

Is there a standard control to do this? I've seen apps do this. A rounded rectangle, dark and partially transparent with text inside. It does not ask for user input but disappears on its own in some short period of time. Android has a similar construct which is standard. Also similar to the windows displayed by Growl.

Suggestions appreciated.

Srikar Appalaraju
  • 71,928
  • 54
  • 216
  • 264
David
  • 2,770
  • 5
  • 35
  • 43

10 Answers10

16

There is a user library on cocoacontrols.com that emulates the Android-style Toast pop-ups. Might be what you are looking for.

http://www.cocoacontrols.com/platforms/ios/controls/altoastview

There's also this one that follows the same idea.

http://www.cocoacontrols.com/platforms/ios/controls/itoast

Lukasz Czerwinski
  • 13,499
  • 10
  • 55
  • 65
Joe Masilotti
  • 16,815
  • 6
  • 77
  • 87
  • 1
    This is closest to what I was looking for, although I ended up writing my own. – David Nov 26 '11 at 01:42
  • 1
    Update: The ever popular [MBProgressHUD](https://github.com/jdg/MBProgressHUD) now has a [Toast-like notification](http://dl.dropbox.com/u/378729/MBProgressHUD/7.png). – Joe Masilotti Aug 07 '12 at 21:59
15

Create a class that inherits from UIAlertView. In your constructor just call [super init] and then add any view you want as a subview. You can even design this view in Interface Builder. Something like this:

- (id)initWithMessage:(NSString *)message dismissAfter:(NSTimeInterval)interval
{
  if ((self = [super init]))
  {
     CustomView * customView = [[[CustomView alloc] init] autorelease]; // or load from NIB
     [self addSubview:customView];
     [self performSelector:@selector(dismissAfterDelay) withObject:nil afterDelay:interval];
  }
  return self;
}

- (void)dismissAfterDelay
{
  [self dismissWithClickedButtonIndex:0 animated:YES]; 
}

To display your custom alert view just init it, and call show as with a regular UIAlertView.

CustomAlertView * cav = [[CustomAlertView alloc] initWithMessage:@"Doing Something];
[cav show];
[cav release];

As an nice side-effect, when you present this view the background will darken and you will get the nice wobbly animation for any alert view.

Goles
  • 11,599
  • 22
  • 79
  • 140
Marco Mustapic
  • 3,879
  • 1
  • 21
  • 20
  • 3
    The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified. – MC. May 29 '13 at 18:14
9

Use UIAlertView. Here's a quick link to a simple example:

UIAlertView

Edited: Sorry, didn't see about disappearing on it's own. I think you need to have some confirmation by users of messages received. UIAlertView pretty much accomplishes that. Not sure if you have it gradually fade away unless you're in an app with a view that has a timer or event based delay that will show a view and then eventually remove it.

Second edit: Found a way to implement it. You can manually call the dismiss function after some set time that you've designated. (Sorry for the messy post) It would look something like this:

//Create UIAlertView alert
alert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"Some message" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles: nil];

//After some time
[alert dismissWithClickedButtonIndex:0 animated:TRUE];
Kennzo
  • 437
  • 2
  • 11
  • I've implemented something quick and dirty using a UIAlertView with no buttons. I dismiss it myself after a delay. It works, but does not look anywhere near as good as what I described. If its not built into the platform, there must be an implementation somewhere. Doesn't seem too hard to implement on your own, but seems that someone should have already done it. The app 'FeeddlerRSS' uses something like it constantly. – David Sep 17 '10 at 18:38
  • I found a post for another similar question. I think this is more what you're talking about: http://stackoverflow.com/questions/593147/how-to-display-a-progress-indicator-overlay-hud-on-iphone – Kennzo Sep 17 '10 at 18:56
8

I created an Android-Kind toast, pretty simple because I don't need more functionality in it for now.

When it is shown it is added at the bottom of the parent's view, so if that view is the VC's view then it will be at the bottom center of the device.

The frame is autoadjusted to the text length.

You use it doing: [self.view addSubview: [[ToastAlert alloc] initWithText: @"Sent"]];, it will be auto-removed so no reference is needed.

I haven't implemented this, but you can create a static method to shorten and clarify the instruction, sort of: [ToastAlert showText: @"Sent" inView: self.view];.

The class:

ToastAlert.h

@interface ToastAlert : UILabel {

}

- (id)initWithText: (NSString*) msg;

@end

ToastAlert.m

#import "ToastAlert.h"
#import <QuartzCore/QuartzCore.h>

@implementation ToastAlert

#define POPUP_DELAY  1.5

- (id)initWithText: (NSString*) msg
{

    self = [super init];
    if (self) {

        self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.7];
        self.textColor = [UIColor colorWithWhite:1 alpha: 0.95];
        self.font = [UIFont fontWithName: @"Helvetica-Bold" size: 13];
        self.text = msg;
        self.numberOfLines = 0;
        self.textAlignment = UITextAlignmentCenter;
        self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;


    }
    return self;
}

- (void)didMoveToSuperview {

    UIView* parent = self.superview;

    if(parent) {

        CGSize maximumLabelSize = CGSizeMake(300, 200);
        CGSize expectedLabelSize = [self.text sizeWithFont: self.font  constrainedToSize:maximumLabelSize lineBreakMode: NSLineBreakByTruncatingTail];

        expectedLabelSize = CGSizeMake(expectedLabelSize.width + 20, expectedLabelSize.height + 10);

        self.frame = CGRectMake(parent.center.x - expectedLabelSize.width/2,
                                parent.bounds.size.height-expectedLabelSize.height - 10,
                                expectedLabelSize.width,
                                expectedLabelSize.height);

        CALayer *layer = self.layer;
        layer.cornerRadius = 4.0f;

        [self performSelector:@selector(dismiss:) withObject:nil afterDelay:POPUP_DELAY];
    }
}

- (void)dismiss:(id)sender {
    // Fade out the message and destroy self
    [UIView animateWithDuration:0.6  delay:0 options: UIViewAnimationOptionAllowUserInteraction
                     animations:^  { self.alpha = 0; }
                     completion:^ (BOOL finished) { [self removeFromSuperview]; }];
}

@end
htafoya
  • 18,261
  • 11
  • 80
  • 104
8

Use a UIAlertView with nil title and nil buttons, then dismiss it when desired. Here's how I did this:

Create an instance variable for the alert view in your .h file:

@interface StatusMessageController : UIViewController {
    UIAlertView *statusAlert;
}

In your .m file, create a method to show the alert view and start a timer, and another to handle when the timer expires to dismiss the alert:

- (void)showStatus:(NSString *)message timeout:(double)timeout {
    statusAlert = [[UIAlertView alloc] initWithTitle:nil
                                                    message:message
                                                   delegate:nil
                                          cancelButtonTitle:nil
                                          otherButtonTitles:nil];
    [statusAlert show];
    [NSTimer scheduledTimerWithTimeInterval:timeout
                                     target:self
                                   selector:@selector(timerExpired:)
                                   userInfo:nil
                                    repeats:NO];
}

- (void)timerExpired:(NSTimer *)timer {
    [statusAlert dismissWithClickedButtonIndex:0 animated:YES];
}

Whenever you want to show the status message, invoke it:

[self showStatus:@"Computing" timeout:4.5];

At any time, you can also dismiss the alert with:

[statusAlert dismissWithClickedButtonIndex:0 animated:YES];

You can also change the message on-the-fly with new status:

statusAlert.message = @"Looking up user";
async8192
  • 199
  • 2
  • 8
4

I ended up creating my own class. Didn't inherit from UIAlertView. General structure is,

-(id)initWithText:(NSString *)msg {
    // Create a view. Put a label, set the msg
    CALayer *layer = self.layer;
    layer.cornerRadius = 8.0f;
    ...
    self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.8];
    [self performSelector:@selector(dismiss:) withObject:nil afterDelay:2.0];
    [self setAutoresizesSubviews:FALSE];
    return self;
}


- (void)dismiss:(id)sender {
    // Fade out the message and destroy self
    [UIView animateWithDuration:0.5 
                 animations:^  { self.alpha = 0; }
                 completion:^ (BOOL finished) { [self removeFromSuperview]; }];
}
David
  • 2,770
  • 5
  • 35
  • 43
2

Similar answer to @marco-mustapic's, but without inheritance.

- (void)dismissAlert:(UIAlertView *)alertView
{
    [alertView dismissWithClickedButtonIndex:0 animated:YES];
}

- (void)showPopupWithTitle:(NSString *)title
                    mesage:(NSString *)message
              dismissAfter:(NSTimeInterval)interval
{
    UIAlertView *alertView = [[UIAlertView alloc]
           initWithTitle:title
                 message:message
                delegate:nil
        cancelButtonTitle:nil
        otherButtonTitles:nil
    ];
    [alertView show];
    [self performSelector:@selector(dismissAlert:)
               withObject:alertView
               afterDelay:interval
    ];
}

To use it:

[self showPopupWithTitle:@"Hi" message:@"I like pie" dismissAfter:2.0];

Throw it into a category on NSObject or something to always have it around.

theory
  • 9,178
  • 10
  • 59
  • 129
2

*Swift 2.2 Answer:

func showPopupWithTitle(title: String, message: String, interval: NSTimeInterval) {
    let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
    presentViewController(alertController, animated: true, completion: nil)
    self.performSelector(#selector(dismissAlertViewController), withObject: alertController, afterDelay: interval)
}

func dismissAlertViewController(alertController: UIAlertController) {
    alertController.dismissViewControllerAnimated(true, completion: nil)
}

showPopupWithTitle("Title", message: "Message", interval: 0.5)
Idan
  • 5,405
  • 7
  • 35
  • 52
Chaitanya
  • 21
  • 2
2

This is just a Swift 3 version of user2234810 2.2 version.

func showPopupWithTitle(title: String, message: String, interval: TimeInterval) {
    let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
    present(alertController, animated: true, completion: nil)
    self.perform(#selector(dismissAlertViewController), with: alertController, afterDelay: interval)
}

func dismissAlertViewController(alertController: UIAlertController) {
    alertController.dismiss(animated: true, completion: nil)
}
showPopupWithTitle(title: "Title", message: "Message", interval: 0.5)
Micah Montoya
  • 757
  • 1
  • 8
  • 24
2

You can use my own StatusAlert framework written in Swift. It gives you ability to show Apple system-like alert as well as present the same alert without an image, title or message anywhere in UIView.

It is available via Cocoapods and Carthage and supports iPhone X, Safe Areas layout, iPads and allows some customizations.

StatusAlertExample application screenshow

LowKostKustomz
  • 424
  • 4
  • 8
  • Nice job. Would be nice to have a property to disable the blur. If `isBlurAvailable` is true, background settings are ignored. – Paglian May 24 '18 at 18:53