web-dev-qa-db-ja.com

iOS用のうなり声/トーストスタイルの通知ライブラリ

IOSでうなり声やトーストスタイルの通知を実装するためのライブラリを誰かが推奨できますか?たとえば、ユーザーがプロファイルを保存した後、通知をフェードインさせ、3秒間そのままにして、「プロファイルを保存しました」と報告し、フェードアウトさせたいとします。現在、1つの[OK]ボタンでユーザーのワークフローを中断するUIAlertViewがありますが、それはやり過ぎだと感じています。

Android Toastクラス は私がiOSで探しているものの例です。

ありがとう!

17
esilver

私はあなたが役に立つと思う解決策を作成しました: https://github.com/scalessec/toast

これはobj-cカテゴリとして記述されており、基本的にUIViewの任意のインスタンスにmakeToastメソッドを追加します。例えば:

[self.view makeToast:@"Profile saved"
            duration:2.0
            position:@"bottom"];
45
user2393462435

私はそれをこのように解決しました:

  1. ビューに共通のラベルを作成します。すべての画面を広くし、必要なサイズを指定して、テキストを中央に配置します。
  2. その位置を「上」に設定します。このラベルは、コントロールのリスト内のすべてのコントロールの下にある必要があります。
  3. それをインターフェース、プロパティ、合成に追加します(そこで「toastLabel」と呼びましょう)。
  4. XIBファイルで「toastLabel」に関連付けます
  5. ViewWillAppearに次の行を追加して、最初のラベルを非表示にします。

    [toastLabel setHidden:TRUE];
    
  6. ボタンクリック(またはその他のイベント)で次のコードを追加します。

    toastLabel.text = @"Our toast text";
    [toastLabel setHidden:TRUE];
    [toastLabel setAlpha:1.0];
    CGPoint location;
    location.x = 160; 
    location.y = 220; 
    toastLabel.center = location;
    location.x = 160; 
    location.y = 320; 
    [toastLabel setHidden:FALSE];
    [UIView animateWithDuration:0.9 animations:^{
        toastLabel.alpha = 0.0;
        toastLabel.center = location;
    }];
    

このラベルは「落ちて」消えます。

6
nickeyzzz

少し遅れていますが、これが私の見解です。

https://github.com/pcperini/PCToastMessage

5
Patrick Perini

私のオープンソースライブラリTSMessagesを試すことができます: https://github.com/toursprung/TSMessages

それは本当に使いやすく、iOS5/6とiOS7でも美しく見えます。

5
KrauseFx

自分で作りました。クリシュナンによってリンクされたクラスは醜く、正しく回転しませんでした。

https://github.com/esilverberg/ios-toast

外観は次のとおりです。 enter image description here

4
esilver

私はこのようにそれを作りました:

+ (void)showToastMessage:(NSString *)message {    
UIAlertView *toast = [[UIAlertView alloc] initWithTitle:nil
                                                message:message
                                               delegate:nil
                                      cancelButtonTitle:nil
                                      otherButtonTitles:nil, nil];
[toast show];

// duration in seconds
int duration = 2;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, duration * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    [toast dismissWithClickedButtonIndex:0 animated:YES];
});

}

IOS9 +の更新されたソリューション:

+ (void)showToastMessage:(NSString *)message
                root:(id)view {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil
                                                                             message:message
                                                                      preferredStyle:UIAlertControllerStyleAlert];
// duration in seconds
int duration = 2;
[view presentViewController:alertController animated:YES completion:nil];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, duration * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    [alertController dismissViewControllerAnimated:YES completion:nil];
});
}
3
levo4ka
2
tc.

ねえ、あなたはこれを探しています。

https://github.com/PaulSolt/WEPopover#readme

1
Praveen S

これはまさにあなたが望むものです。これも完了ブロックがあるので非常に便利です。ご覧ください:) https://github.com/PrajeetShrestha/EkToast

1

Swift 2.0:

アイデアは、CocoaPodsへの依存がゼロのToastクラスを作成することです。

参照:https://github.com/scalessec/Toast-Swift

空のSwiftファイル(file-New-File- Empty Swift File-Name it Toast。)

次のコードを追加します。

//  Toast.Swift

import UIKit
import ObjectiveC

enum ToastPosition {
 case Top
 case Center
 case Bottom
}

extension UIView {

 private struct ToastKeys {
  static var Timer        = "CSToastTimerKey"
  static var Duration     = "CSToastDurationKey"
  static var Position     = "CSToastPositionKey"
  static var Completion   = "CSToastCompletionKey"
  static var ActiveToast  = "CSToastActiveToastKey"
  static var ActivityView = "CSToastActivityViewKey"
  static var Queue        = "CSToastQueueKey"
 }

 private class ToastCompletionWrapper {
  var completion: ((Bool) -> Void)?

  init(_ completion: ((Bool) -> Void)?) {
   self.completion = completion
  }
 }

 private enum ToastError: ErrorType {
  case InsufficientData
 }

 private var queue: NSMutableArray {
  get {
   if let queue = objc_getAssociatedObject(self, &ToastKeys.Queue) as? NSMutableArray {
    return queue
   } else {
    let queue = NSMutableArray()
    objc_setAssociatedObject(self, &ToastKeys.Queue, queue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    return queue
   }
  }
 }

 // MARK: - Make Toast Methods
 func makeToast(message: String) {
  self.makeToast(message, duration: ToastManager.shared.duration, position: ToastManager.shared.position)
 }
 func makeToast(message: String, duration: NSTimeInterval, position: ToastPosition) {
  self.makeToast(message, duration: duration, position: position, style: nil)
 }
 func makeToast(message: String, duration: NSTimeInterval, position: CGPoint) {
  self.makeToast(message, duration: duration, position: position, style: nil)
 }
 func makeToast(message: String, duration: NSTimeInterval, position: ToastPosition, style: ToastStyle?) {
  self.makeToast(message, duration: duration, position: position, title: nil, image: nil, style: style, completion: nil)
 }
 func makeToast(message: String, duration: NSTimeInterval, position: CGPoint, style: ToastStyle?) {
  self.makeToast(message, duration: duration, position: position, title: nil, image: nil, style: style, completion: nil)
 }
 func makeToast(message: String?, duration: NSTimeInterval, position: ToastPosition, title: String?, image: UIImage?, style: ToastStyle?, completion: ((didTap: Bool) -> Void)?) {
  var toastStyle = ToastManager.shared.style
  if let style = style {
   toastStyle = style
  }

  do {
   let toast = try self.toastViewForMessage(message, title: title, image: image, style: toastStyle)
   self.showToast(toast, duration: duration, position: position, completion: completion)
  } catch ToastError.InsufficientData {
   print("Error: message, title, and image are all nil")
  } catch {}
 }

 func makeToast(message: String?, duration: NSTimeInterval, position: CGPoint, title: String?, image: UIImage?, style: ToastStyle?, completion: ((didTap: Bool) -> Void)?) {
  var toastStyle = ToastManager.shared.style
  if let style = style {
   toastStyle = style
  }

  do {
   let toast = try self.toastViewForMessage(message, title: title, image: image, style: toastStyle)
   self.showToast(toast, duration: duration, position: position, completion: completion)
  } catch ToastError.InsufficientData {
   print("Error: message, title, and image cannot all be nil")
  } catch {}
 }

 // MARK: - Show Toast Methods
 func showToast(toast: UIView) {
  self.showToast(toast, duration: ToastManager.shared.duration, position: ToastManager.shared.position, completion: nil)
 }
 func showToast(toast: UIView, duration: NSTimeInterval, position: ToastPosition, completion: ((didTap: Bool) -> Void)?) {
  let point = self.centerPointForPosition(position, toast: toast)
  self.showToast(toast, duration: duration, position: point, completion: completion)
 }

 func showToast(toast: UIView, duration: NSTimeInterval, position: CGPoint, completion: ((didTap: Bool) -> Void)?) {
  objc_setAssociatedObject(toast, &ToastKeys.Completion, ToastCompletionWrapper(completion), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);

  if let _ = objc_getAssociatedObject(self, &ToastKeys.ActiveToast) as? UIView where ToastManager.shared.queueEnabled {
   objc_setAssociatedObject(toast, &ToastKeys.Duration, NSNumber(double: duration), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
   objc_setAssociatedObject(toast, &ToastKeys.Position, NSValue(CGPoint: position), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);

   self.queue.addObject(toast)
  } else {
   self.showToast(toast, duration: duration, position: position)
  }
 }

 // MARK: - Activity Methods
 func makeToastActivity(position: ToastPosition) {
  // sanity
  if let _ = objc_getAssociatedObject(self, &ToastKeys.ActiveToast) as? UIView {
   return
  }

  let toast = self.createToastActivityView()
  let point = self.centerPointForPosition(position, toast: toast)
  self.makeToastActivity(toast, position: point)
 }
 func makeToastActivity(position: CGPoint) {
  // sanity
  if let _ = objc_getAssociatedObject(self, &ToastKeys.ActiveToast) as? UIView {
   return
  }

  let toast = self.createToastActivityView()
  self.makeToastActivity(toast, position: position)
 }

 //Dismisses the active toast activity indicator view.
 func hideToastActivity() {
  if let toast = objc_getAssociatedObject(self, &ToastKeys.ActivityView) as? UIView {
   UIView.animateWithDuration(ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.CurveEaseIn, .BeginFromCurrentState], animations: { () -> Void in
    toast.alpha = 0.0
    }, completion: { (finished: Bool) -> Void in
     toast.removeFromSuperview()
     objc_setAssociatedObject(self, &ToastKeys.ActivityView, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
   })
  }
 }

 // MARK: - Private Activity Methods
 private func makeToastActivity(toast: UIView, position: CGPoint) {
  toast.alpha = 0.0
  toast.center = position

  objc_setAssociatedObject(self, &ToastKeys.ActivityView, toast, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

  self.addSubview(toast)

  UIView.animateWithDuration(ToastManager.shared.style.fadeDuration, delay: 0.0, options: .CurveEaseOut, animations: { () -> Void in
   toast.alpha = 1.0
   }, completion: nil)
 }

 private func createToastActivityView() -> UIView {
  let style = ToastManager.shared.style

  let activityView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: style.activitySize.width, height: style.activitySize.height))
  activityView.backgroundColor = style.backgroundColor
  activityView.autoresizingMask = [.FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleTopMargin, .FlexibleBottomMargin]
  activityView.layer.cornerRadius = style.cornerRadius

  if style.displayShadow {
   activityView.layer.shadowColor = style.shadowColor.CGColor
   activityView.layer.shadowOpacity = style.shadowOpacity
   activityView.layer.shadowRadius = style.shadowRadius
   activityView.layer.shadowOffset = style.shadowOffset
  }

  let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
  activityIndicatorView.center = CGPoint(x: activityView.bounds.size.width / 2.0, y: activityView.bounds.size.height / 2.0)
  activityView.addSubview(activityIndicatorView)
  activityIndicatorView.startAnimating()

  return activityView
 }

 // MARK: - Private Show/Hide Methods
 private func showToast(toast: UIView, duration: NSTimeInterval, position: CGPoint) {
  toast.center = position
  toast.alpha = 0.0

  if ToastManager.shared.tapToDismissEnabled {
   let recognizer = UITapGestureRecognizer(target: self, action: "handleToastTapped:")
   toast.addGestureRecognizer(recognizer)
   toast.userInteractionEnabled = true
   toast.exclusiveTouch = true
  }

  objc_setAssociatedObject(self, &ToastKeys.ActiveToast, toast, .OBJC_ASSOCIATION_RETAIN_NONATOMIC);

  self.addSubview(toast)

  UIView.animateWithDuration(ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.CurveEaseOut, .AllowUserInteraction], animations: { () -> Void in
   toast.alpha = 1.0
   }) { (Bool finished) -> Void in
    let timer = NSTimer(timeInterval: duration, target: self, selector: "toastTimerDidFinish:", userInfo: toast, repeats: false)
    NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
    objc_setAssociatedObject(toast, &ToastKeys.Timer, timer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  }
 }

 private func hideToast(toast: UIView) {
  self.hideToast(toast, fromTap: false)
 }

 private func hideToast(toast: UIView, fromTap: Bool) {

  UIView.animateWithDuration(ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.CurveEaseIn, .BeginFromCurrentState], animations: { () -> Void in
   toast.alpha = 0.0
   }) { (didFinish: Bool) -> Void in
    toast.removeFromSuperview()

    objc_setAssociatedObject(self, &ToastKeys.ActiveToast, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    if let wrapper = objc_getAssociatedObject(toast, &ToastKeys.Completion) as? ToastCompletionWrapper, completion = wrapper.completion {
     completion(fromTap)
    }

    if let nextToast = self.queue.firstObject as? UIView, duration = objc_getAssociatedObject(nextToast, &ToastKeys.Duration) as? NSNumber, position = objc_getAssociatedObject(nextToast, &ToastKeys.Position) as? NSValue {
     self.queue.removeObjectAtIndex(0)
     self.showToast(nextToast, duration: duration.doubleValue, position: position.CGPointValue())
    }
  }
 }

 // MARK: - Events
 func handleToastTapped(recognizer: UITapGestureRecognizer) {
  if let toast = recognizer.view, timer = objc_getAssociatedObject(toast, &ToastKeys.Timer) as? NSTimer {
   timer.invalidate()
   self.hideToast(toast, fromTap: true)
  }
 }

 func toastTimerDidFinish(timer: NSTimer) {
  if let toast = timer.userInfo as? UIView {
   self.hideToast(toast)
  }
 }

 // MARK: - Toast Construction
 func toastViewForMessage(message: String?, title: String?, image: UIImage?, style: ToastStyle) throws -> UIView {
  // sanity
  if message == nil && title == nil && image == nil {
   throw ToastError.InsufficientData
  }

  var messageLabel: UILabel?
  var titleLabel: UILabel?
  var imageView: UIImageView?

  let wrapperView = UIView()
  wrapperView.backgroundColor = style.backgroundColor
  wrapperView.autoresizingMask = [.FlexibleLeftMargin, .FlexibleRightMargin, .FlexibleTopMargin, .FlexibleBottomMargin]
  wrapperView.layer.cornerRadius = style.cornerRadius

  if style.displayShadow {
   wrapperView.layer.shadowColor = UIColor.blackColor().CGColor
   wrapperView.layer.shadowOpacity = style.shadowOpacity
   wrapperView.layer.shadowRadius = style.shadowRadius
   wrapperView.layer.shadowOffset = style.shadowOffset
  }

  if let image = image {
   imageView = UIImageView(image: image)
   imageView?.contentMode = .ScaleAspectFit
   imageView?.frame = CGRect(x: style.horizontalPadding, y: style.verticalPadding, width: style.imageSize.width, height: style.imageSize.height)
  }

  var imageRect = CGRectZero

  if let imageView = imageView {
   imageRect.Origin.x = style.horizontalPadding
   imageRect.Origin.y = style.verticalPadding
   imageRect.size.width = imageView.bounds.size.width
   imageRect.size.height = imageView.bounds.size.height
  }

  if let title = title {
   titleLabel = UILabel()
   titleLabel?.numberOfLines = style.titleNumberOfLines
   titleLabel?.font = style.titleFont
   titleLabel?.textAlignment = style.titleAlignment
   titleLabel?.lineBreakMode = .ByTruncatingTail
   titleLabel?.textColor = style.titleColor
   titleLabel?.backgroundColor = UIColor.clearColor();
   titleLabel?.text = title;

   let maxTitleSize = CGSize(width: (self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, height: self.bounds.size.height * style.maxHeightPercentage)
   let titleSize = titleLabel?.sizeThatFits(maxTitleSize)
   if let titleSize = titleSize {
    titleLabel?.frame = CGRect(x: 0.0, y: 0.0, width: titleSize.width, height: titleSize.height)
   }
  }

  if let message = message {
   messageLabel = UILabel()
   messageLabel?.text = message
   messageLabel?.numberOfLines = style.messageNumberOfLines
   messageLabel?.font = style.messageFont
   messageLabel?.textAlignment = style.messageAlignment
   messageLabel?.lineBreakMode = .ByTruncatingTail;
   messageLabel?.textColor = style.messageColor
   messageLabel?.backgroundColor = UIColor.clearColor()

   let maxMessageSize = CGSize(width: (self.bounds.size.width * style.maxWidthPercentage) - imageRect.size.width, height: self.bounds.size.height * style.maxHeightPercentage)
   let messageSize = messageLabel?.sizeThatFits(maxMessageSize)
   if let messageSize = messageSize {
    messageLabel?.frame = CGRect(x: 0.0, y: 0.0, width: messageSize.width, height: messageSize.height)
   }
  }

  var titleRect = CGRectZero

  if let titleLabel = titleLabel {
   titleRect.Origin.x = imageRect.Origin.x + imageRect.size.width + style.horizontalPadding
   titleRect.Origin.y = style.verticalPadding
   titleRect.size.width = titleLabel.bounds.size.width
   titleRect.size.height = titleLabel.bounds.size.height
  }

  var messageRect = CGRectZero

  if let messageLabel = messageLabel {
   messageRect.Origin.x = imageRect.Origin.x + imageRect.size.width + style.horizontalPadding
   messageRect.Origin.y = titleRect.Origin.y + titleRect.size.height + style.verticalPadding
   messageRect.size.width = messageLabel.bounds.size.width
   messageRect.size.height = messageLabel.bounds.size.height
  }

  let longerWidth = max(titleRect.size.width, messageRect.size.width)
  let longerX = max(titleRect.Origin.x, messageRect.Origin.x)
  let wrapperWidth = max((imageRect.size.width + (style.horizontalPadding * 2.0)), (longerX + longerWidth + style.horizontalPadding))
  let wrapperHeight = max((messageRect.Origin.y + messageRect.size.height + style.verticalPadding), (imageRect.size.height + (style.verticalPadding * 2.0)))

  wrapperView.frame = CGRect(x: 0.0, y: 0.0, width: wrapperWidth, height: wrapperHeight)

  if let titleLabel = titleLabel {
   titleLabel.frame = titleRect
   wrapperView.addSubview(titleLabel)
  }

  if let messageLabel = messageLabel {
   messageLabel.frame = messageRect
   wrapperView.addSubview(messageLabel)
  }

  if let imageView = imageView {
   wrapperView.addSubview(imageView)
  }

  return wrapperView
 }

 // MARK: - Helpers
 private func centerPointForPosition(position: ToastPosition, toast: UIView) -> CGPoint {
  let padding: CGFloat = ToastManager.shared.style.verticalPadding

  switch(position) {
  case .Top:
   return CGPoint(x: self.bounds.size.width / 2.0, y: (toast.frame.size.height / 2.0) + padding)
  case .Center:
   return CGPoint(x: self.bounds.size.width / 2.0, y: self.bounds.size.height / 2.0)
  case .Bottom:
   return CGPoint(x: self.bounds.size.width / 2.0, y: (self.bounds.size.height - (toast.frame.size.height / 2.0)) - padding)
  }
 }
}

// MARK: - Toast Style
struct ToastStyle {

 var backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.8)
 var titleColor = UIColor.whiteColor()
 var messageColor = UIColor.whiteColor()
 var maxWidthPercentage: CGFloat = 0.8 {
  didSet {
   maxWidthPercentage = max(min(maxWidthPercentage, 1.0), 0.0)
  }
 }


 var maxHeightPercentage: CGFloat = 0.8 {
  didSet {
   maxHeightPercentage = max(min(maxHeightPercentage, 1.0), 0.0)
  }
 }

 var horizontalPadding: CGFloat = 10.0
 var verticalPadding: CGFloat = 10.0
 var cornerRadius: CGFloat = 10.0;
 var titleFont = UIFont.boldSystemFontOfSize(16.0)
 var messageFont = UIFont.systemFontOfSize(16.0)
 var titleAlignment = NSTextAlignment.Left
 var messageAlignment = NSTextAlignment.Left
 var titleNumberOfLines = 0;
 var messageNumberOfLines = 0;
 var displayShadow = false;
 var shadowColor = UIColor.blackColor()
 var shadowOpacity: Float = 0.8 {
  didSet {
   shadowOpacity = max(min(shadowOpacity, 1.0), 0.0)
  }
 }

 var shadowRadius: CGFloat = 6.0
 var shadowOffset = CGSize(width: 4.0, height: 4.0)
 var imageSize = CGSize(width: 80.0, height: 80.0)
 var activitySize = CGSize(width: 100.0, height: 100.0)
 var fadeDuration: NSTimeInterval = 0.2

}

// MARK: - Toast Manager
class ToastManager {

 static let shared = ToastManager()
 var style = ToastStyle()
 var tapToDismissEnabled = true
 var queueEnabled = true
 var duration: NSTimeInterval = 3.0
 var position = ToastPosition.Bottom

}

Toast.Swiftの使用:

// basic usage
  self.view.makeToast("Sample Toast")

  // toast with a specific duration and position
  self.view.makeToast("Sample Toast", duration: 3.0, position: .Top)

  // toast with all possible options
  self.view.makeToast("Sample Toast", duration: 2.0, position: CGPoint(x: 110.0, y: 110.0), title: "Toast Title", image: UIImage(named: "ic_120x120.png"), style:nil) { (didTap: Bool) -> Void in
   if didTap {
    print("completion from tap")
   } else {
    print("completion without tap")
   }
  }

  //display toast with an activity spinner
   self.view.makeToastActivity(.Center)

  // display any view as toast
  let sampleView = UIView(frame: CGRectMake(100,100,200,200))
  sampleView.backgroundColor = UIColor(patternImage: UIImage(named: "ic_120x120")!)

  self.view.showToast(sampleView)
  self.view.showToast(sampleView, duration: 3.0, position: .Top, completion: nil)

サンプルプロジェクトは https://github.com/alvinreuben/ToastSample からダウンロードできます。

0
A.G

Swift 3

Rannie/Toast-Swift for Swift 3をしばらくの間非常に喜んで使用しており、非常によく似た「Androidのような」エクスペリエンスに推奨できます。別のポッドを必要とせずに実装が簡単で、ニーズに応じてかなりカスタマイズできます。

Easy Peasy

view.makeToastActivity()
view.hideToastActivity()
0
AMAN77

これはあなたが必要としていたものです。多くのアニメーションオプション、画面位置、および期間で作成されます。あなたもあなた自身の期間を提供することができます。以下でそれをチェックしてください。

https://github.com/avistyles/AVIToast