53

I have made Android application a few months ago. The Toast class is very useful for me. I do not need to consider the main Thread and place to show it. Anywhere I can show it and just leave that and it is automatically disappeared.

Toast.makeToast(context, msg, Toast.LENGTH_SHORT).show(); 

That's it. ^^

What about iPhone? Is there something like the Toast? Just show message and do not need to care about it. It will be automatically disappeared.

3
  • considering that only with iOS 4 allowed background apps, there was no previous need for such a feature. No doubt someone will make one, but I'm not familiar with anything like it. Commented Aug 19, 2010 at 15:01
  • 3
    @StephenFurlani, although it's not obvious immediately, toast messages are also useful, and widely used, in foreground apps. Commented Jul 13, 2014 at 8:47
  • 1
    @DanielS. I whole-heartedly agree. However, the OP is almost 4 years old. At this point there are some GREAT features w/ iOS 8 regarding UIPopoverPresentationControllers and passthroughViews Commented Jul 31, 2014 at 15:54

5 Answers 5

72

I've been writing for Android for a long time and I am missing Toast. I've implemented one. Need code? here you are:

ToastView.h

#import <UIKit/UIKit.h> @interface ToastView : UIView @property (strong, nonatomic) NSString *text; + (void)showToastInParentView: (UIView *)parentView withText:(NSString *)text withDuaration:(float)duration; @end 

ToastView.m

#import "ToastView.h" @interface ToastView () @property (strong, nonatomic, readonly) UILabel *textLabel; @end @implementation ToastView @synthesize textLabel = _textLabel; float const ToastHeight = 50.0f; float const ToastGap = 10.0f; - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code } return self; } -(UILabel *)textLabel { if (!_textLabel) { _textLabel = [[UILabel alloc] initWithFrame:CGRectMake(5.0, 5.0, self.frame.size.width - 10.0, self.frame.size.height - 10.0)]; _textLabel.backgroundColor = [UIColor clearColor]; _textLabel.textAlignment = NSTextAlignmentCenter; _textLabel.textColor = [UIColor whiteColor]; _textLabel.numberOfLines = 2; _textLabel.font = [UIFont systemFontOfSize:13.0]; _textLabel.lineBreakMode = NSLineBreakByCharWrapping; [self addSubview:_textLabel]; } return _textLabel; } - (void)setText:(NSString *)text { _text = text; self.textLabel.text = text; } + (void)showToastInParentView: (UIView *)parentView withText:(NSString *)text withDuaration:(float)duration; { //Count toast views are already showing on parent. Made to show several toasts one above another int toastsAlreadyInParent = 0; for (UIView *subView in [parentView subviews]) { if ([subView isKindOfClass:[ToastView class]]) { toastsAlreadyInParent++; } } CGRect parentFrame = parentView.frame; float yOrigin = parentFrame.size.height - (70.0 + ToastHeight * toastsAlreadyInParent + ToastGap * toastsAlreadyInParent); CGRect selfFrame = CGRectMake(parentFrame.origin.x + 20.0, yOrigin, parentFrame.size.width - 40.0, ToastHeight); ToastView *toast = [[ToastView alloc] initWithFrame:selfFrame]; toast.backgroundColor = [UIColor darkGrayColor]; toast.alpha = 0.0f; toast.layer.cornerRadius = 4.0; toast.text = text; [parentView addSubview:toast]; [UIView animateWithDuration:0.4 animations:^{ toast.alpha = 0.9f; toast.textLabel.alpha = 0.9f; }completion:^(BOOL finished) { if(finished){ } }]; [toast performSelector:@selector(hideSelf) withObject:nil afterDelay:duration]; } - (void)hideSelf { [UIView animateWithDuration:0.4 animations:^{ self.alpha = 0.0; self.textLabel.alpha = 0.0; }completion:^(BOOL finished) { if(finished){ [self removeFromSuperview]; } }]; } @end 

Call from ViewController

 [ToastView showToastInParentView:self.view withText:@"What a toast!" withDuaration:5.0]; 
Sign up to request clarification or add additional context in comments.

5 Comments

Great (@Scarmysun: except for the extra colon before the implementation body. SO won't let me do an edit that short, otherwise I'd have fixed it.. ). Upvoted
This crashed for me until I renamed "setTextLabel:" to something else. "setTextLabel:" gets called automatically any time the property is updated (since a property is just shorthand for using the getter or setter). After I made that change, it worked well.
Nice comment @BeccaP . I've updated my implementation. Now text label is private property and instantiated lazily. Added a "text" public property.
I had to change int toastsAlreadyInParent = 0; to 1 or else I had to call it twice before it would display.
This is a nice little object. Well done. Note that when setting the ToastView frame, you need to do that using the parent view's bounds, not frame. So this line: CGRect parentFrame = parentView.frame; should be CGRect parentFrame = parentView.bounds;. You won't see this error unless you have a parent view with a non-zero frame origin.
4

There is no class "out-of-the-box" in UIKit to do this. But it is quite easy to create a class that will offer this behavior.

You just have to create a class that inherit from UIView. This class will have the responsibility - to create what you want to display, - to add itself in parent view hierarchy - to dismiss itself using a timer.

You will be able to use it like :

[ToastView toastViewInView:myParentView withText:@"what a wonderful text"]; 

Regards, Quentin

4 Comments

No code example? You have only suggested an API for a toast should work.
I have a better solution, just code something like: import ProjectGenerator miracle.insertFinishedProject() ...it should work.
You can find a code sample below in this thread. Thanks @Scarmysun
@Josh, that is no good unless you call the DoWhatIMean procedure...repeatedly.
2

You can do this in many ways one of the way is using UIAlertViewController() in swift3

let alertManager=UIAlertController(title: nil, message: "Welcome!", preferredStyle: .alert) self.present(alertManager, animated: true, completion: nil) DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1, execute: { alertManager.dismiss(animated: false, completion: nil) }) 

Comments

2

Edit: Updated for Swift 3

Here is a Swift 3 version based on wojciech_maciejewski's answer. This looks more like Android Toast and doesn't stack toasts on each other. It draws toast into the center of the screen. It can handle long multiline texts.

import UIKit class ToastView: UIView { private static let hLabelGap: CGFloat = 40.0 private static let vLabelGap: CGFloat = 20.0 private static let hToastGap: CGFloat = 20.0 private static let vToastGap: CGFloat = 10.0 private var textLabel: UILabel! static func showInParent(_ parentView: UIView, _ text: String, duration: Double = 3.0) { let labelFrame = CGRect(x: parentView.frame.origin.x + hLabelGap, y: parentView.frame.origin.y + vLabelGap, width: parentView.frame.width - 2 * hLabelGap, height: parentView.frame.height - 2 * vLabelGap) let label = UILabel() label.font = UIFont.systemFont(ofSize: 15.0) label.text = text label.backgroundColor = UIColor.clear label.textAlignment = NSTextAlignment.center label.textColor = UIColor.white label.numberOfLines = 0 label.frame = labelFrame label.sizeToFit() let toast = ToastView() toast.textLabel = label toast.addSubview(label) toast.frame = CGRect(x: label.frame.origin.x - hToastGap, y: label.frame.origin.y - vToastGap, width: label.frame.width + 2 * hToastGap, height: label.frame.height + 2 * vToastGap) toast.backgroundColor = UIColor.darkGray toast.alpha = 0.0 toast.layer.cornerRadius = 20.0 toast.center = parentView.center label.center = CGPoint(x: toast.frame.size.width / 2, y: toast.frame.size.height / 2) parentView.addSubview(toast) UIView.animate(withDuration: 0.4, animations: { toast.alpha = 0.9 label.alpha = 0.9 }) toast.perform(#selector(hideSelf), with: nil, afterDelay: duration) } @objc private func hideSelf() { UIView.animate(withDuration: 0.4, animations: { self.alpha = 0.0 self.textLabel.alpha = 0.0 }, completion: { t in self.removeFromSuperview() }) } } 

Usage from another controller:

ToastView.showInParent(navigationController!.view, "Hello world") 

Comments

1

I'm posting swift version of Scarmysun answer:) many thanks

import Foundation import UIKit class ToastView: UIView { static let toastHeight:CGFloat = 50.0 static let toastGap:CGFloat = 10; lazy var textLabel: UILabel = UILabel(frame: CGRectMake(5.0, 5.0, self.frame.size.width - 10.0, self.frame.size.height - 10.0)) static func showInParent(parentView: UIView!, withText text: String, forDuration duration: double_t) { //Count toast views are already showing on parent. Made to show several toasts one above another var toastsAlreadyInParent = 0; for view in parentView.subviews { if (view.isKindOfClass(ToastView)) { toastsAlreadyInParent++ } } var parentFrame = parentView.frame; var yOrigin = parentFrame.size.height - getDouble(toastsAlreadyInParent) var selfFrame = CGRectMake(parentFrame.origin.x + 20.0, yOrigin, parentFrame.size.width - 40.0, toastHeight); var toast = ToastView(frame: selfFrame) toast.textLabel.backgroundColor = UIColor.clearColor() toast.textLabel.textAlignment = NSTextAlignment.Center toast.textLabel.textColor = UIColor.whiteColor() toast.textLabel.numberOfLines = 2 toast.textLabel.font = UIFont.systemFontOfSize(13.0) toast.addSubview(toast.textLabel) toast.backgroundColor = UIColor.darkGrayColor() toast.alpha = 0.0; toast.layer.cornerRadius = 4.0; toast.textLabel.text = text; parentView.addSubview(toast) UIView.animateWithDuration(0.4, animations: { toast.alpha = 0.9 toast.textLabel.alpha = 0.9 }) toast.performSelector(Selector("hideSelf"), withObject: nil, afterDelay: duration) } static private func getDouble(toastsAlreadyInParent : Int) -> CGFloat { return (70.0 + toastHeight * CGFloat(toastsAlreadyInParent) + toastGap * CGFloat(toastsAlreadyInParent)); } func hideSelf() { UIView.animateWithDuration(0.4, animations: { self.alpha = 0.0 self.textLabel.alpha = 0.0 }, completion: { t in self.removeFromSuperview() }) } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.