iOS自定義控件:PopMenu

現(xiàn)在很多的應(yīng)用都有類(lèi)似的彈窗控件坐榆,最出名應(yīng)該是企鵝和微信了吧拗馒。想著為了滿(mǎn)足自己的效果就動(dòng)手寫(xiě)了這個(gè)汛骂。首先要感謝寫(xiě)出彈窗箭頭的代碼的人iHandle罕模,這方面的知識(shí)我很欠缺,以后找機(jī)會(huì)惡補(bǔ)一下帘瞭。
先上兩張出名的效果圖:

??

微信

樣式定義

主要以UITableView作為容器顯示樣式

  1. 背景顏色
  2. 文字樣式:顏色淑掌,字體大小,對(duì)齊方式
  3. 顯示類(lèi)型:僅文字蝶念,僅圖標(biāo)抛腕,文字 + 圖標(biāo)
  4. 是否顯示下劃線(xiàn)
  5. 箭頭方向
  6. 彈窗寬度
  7. 單元格的高度

箭頭生成

typedef NS_ENUM(NSInteger, ArrowDirection) {
    ArrowDirectionRight = 0,
    ArrowDirectionBottom,
    ArrowDirectionLeft,
    ArrowDirectionTop,
};

#define CORNER_RADIUS   8   // 默認(rèn)矩形框圓角半徑
#define ARROW_WIDTH     30  // 默認(rèn)箭頭寬帶
#define ARROW_HEIGHT    12  // 默認(rèn)箭頭高度
#define ARROW_DIRECTION 1   // 默認(rèn)箭頭方向,向下
#define ARROW_POSITION  0.5 // 默認(rèn)箭頭相對(duì)位置媒殉,居中
#define ARROW_RADIUS    3   // 默認(rèn)箭頭指向處的圓角半徑

@interface WudanLayer : NSObject
@property (nonatomic, assign) CGFloat cornerRadius;
@property (nonatomic, assign) CGFloat arrowRadius;
@property (nonatomic, assign) CGFloat arrowHeight;
@property (nonatomic, assign) CGFloat arrowWidth;
@property (nonatomic, assign) ArrowDirection arrowDirection;
@property (nonatomic, assign) CGFloat arrowPosition;
@property (nonatomic, assign) CGSize size;

- (instancetype)initWithSize:(CGSize)size;
- (CAShapeLayer *)layer;
@end

@implementation WudanLayer

#pragma mark - preparation
- (NSMutableArray *)keyPoints {
    NSMutableArray *points = [NSMutableArray array];
    CGPoint beginPoint;
    CGPoint topPoint;
    CGPoint endPoint;
    CGFloat validWidthForTopPoint = _size.width - 2 * _cornerRadius - _arrowWidth;
    CGFloat validHeightForTopPoint = _size.height - 2 * _cornerRadius - _arrowWidth;
    CGFloat x = 0, y = 0;
    CGFloat width = _size.width, height = _size.height;
    
    switch (_arrowDirection)
    {
        case ArrowDirectionRight:
        {
            width -= _arrowHeight;
            topPoint = CGPointMake(_size.width , _size.height / 2 + validHeightForTopPoint*(_arrowPosition - 0.5));
            beginPoint = CGPointMake(topPoint.x - _arrowHeight, topPoint.y - _arrowWidth/2);
            endPoint = CGPointMake(beginPoint.x, beginPoint.y + _arrowWidth);
        }
            break;
        case ArrowDirectionBottom:
        {
            height -= _arrowHeight;
            
            topPoint = CGPointMake(_size.width / 2 + validWidthForTopPoint*(_arrowPosition - 0.5), _size.height);
            beginPoint = CGPointMake(topPoint.x + _arrowWidth/2, topPoint.y - _arrowHeight);
            endPoint = CGPointMake(beginPoint.x - _arrowWidth, beginPoint.y);
        }
            break;
        case ArrowDirectionLeft:
        {
            x = _arrowHeight;
            width -= _arrowHeight;
            
            topPoint = CGPointMake(0, _size.height / 2 + validHeightForTopPoint*(_arrowPosition - 0.5));
            beginPoint = CGPointMake(topPoint.x + _arrowHeight, topPoint.y + _arrowWidth/2);
            endPoint = CGPointMake(beginPoint.x, beginPoint.y - _arrowWidth);
        }
            break;
        case ArrowDirectionTop:
        {
            y = _arrowHeight;
            height -= _arrowHeight;
            
            topPoint = CGPointMake(_size.width / 2 + validWidthForTopPoint*(_arrowPosition - 0.5), 0);
            beginPoint = CGPointMake(topPoint.x - _arrowWidth/2, topPoint.y + _arrowHeight);
            endPoint = CGPointMake(beginPoint.x + _arrowWidth, beginPoint.y);
        }
            break;
    }
    
    points = [NSMutableArray arrayWithObjects: [NSValue valueWithCGPoint:beginPoint],
              [NSValue valueWithCGPoint:topPoint],
              [NSValue valueWithCGPoint:endPoint],
              nil];
    CGPoint bottomRight = CGPointMake(x + width, y + height);
    CGPoint bottomLeft = CGPointMake(x, y + height);
    CGPoint topLeft = CGPointMake(x, y);
    CGPoint topRight = CGPointMake(x + width, y);
    NSMutableArray *rectPoints = [NSMutableArray arrayWithObjects: [NSValue valueWithCGPoint:bottomRight],
                                  [NSValue valueWithCGPoint:bottomLeft],
                                  [NSValue valueWithCGPoint:topLeft],
                                  [NSValue valueWithCGPoint:topRight],
                                  nil];
    int rectPointIndex = (int)_arrowDirection;
    for(int i = 0; i < 4; ++i) {
        [points addObject:[rectPoints objectAtIndex:rectPointIndex]];
        rectPointIndex = (rectPointIndex + 1) % 4;
    }
    return points;
}

#pragma mark - Draw bubblePath
- (CGPathRef)bubblePath {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    NSMutableArray *points = [self keyPoints];
    CGPoint currentPoint = [[points objectAtIndex:6] CGPointValue];
    CGContextMoveToPoint(ctx, currentPoint.x, currentPoint.y);
    CGPoint pointA, pointB;
    CGFloat radius;
    int i = 0;
    while(1) {
        if (i > 6)
            break;
        radius = i < 3 ?  _arrowRadius : _cornerRadius;
        pointA = [[points objectAtIndex:i] CGPointValue];
        pointB = [[points objectAtIndex:(i + 1) % 7] CGPointValue];
        CGContextAddArcToPoint(ctx, pointA.x, pointA.y, pointB.x, pointB.y, radius);
        i = i + 1;
    }
    CGContextClosePath(ctx);
    CGPathRef path = CGContextCopyPath(ctx);
    CGContextRelease(ctx);
    return path;
}

- (CAShapeLayer *)layer{
    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.path = [self bubblePath];
    return layer;
}

#pragma mark - Setup
- (void)setDefaultProperty {
    _cornerRadius   = CORNER_RADIUS;
    _arrowWidth     = ARROW_WIDTH;
    _arrowHeight    = ARROW_HEIGHT;
    _arrowDirection = ARROW_DIRECTION;
    _arrowPosition  = ARROW_POSITION;
    _arrowRadius    = ARROW_RADIUS;
}

#pragma mark - Init
- (instancetype)initWithSize:(CGSize)size {
    if(self = [super init]) {
        [self setDefaultProperty];
        _size = size;
    }
    return self;
}
@end

單元格默認(rèn)樣式

typedef NS_ENUM(NSInteger, WDPopMenuType) {
    WDPopMenuTypeNormal,
    WDPopMenuTypeOnlyTitle,
    WDPopMenuTypeOnlyIcon,
};

@interface WDPopMenuCell : UITableViewCell
@property (nonatomic, strong) UIImageView *iconImageView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, assign) WDPopMenuType showType;
@end

@implementation WDPopMenuCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self setupSubViewsProperties];
    }
    return self;
}

- (void)setupSubViewsProperties {
    self.iconImageView                                           = [[UIImageView alloc] init];
    self.iconImageView.contentMode                               = UIViewContentModeScaleAspectFit;
    self.iconImageView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentView addSubview:self.iconImageView];

    self.titleLabel                                              = [[UILabel alloc] init];
    self.titleLabel.font                                         = [UIFont systemFontOfSize:15];
    self.titleLabel.textColor                                    = [UIColor blackColor];
    self.titleLabel.translatesAutoresizingMaskIntoConstraints    = NO;
    [self.contentView addSubview:self.titleLabel];
}

- (void)setShowType:(WDPopMenuType)showType {
    _showType = showType;
    [self setupSubViewsConstraint];
}

- (void)setTitleLabel:(UILabel *)titleLabel {
    _titleLabel = titleLabel;
    titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
}

- (void)setupSubViewsConstraint {
    if (self.showType == WDPopMenuTypeNormal) {
        self.titleLabel.textAlignment = NSTextAlignmentLeft;
        
        [self.contentView addConstraints:@[
                                           [NSLayoutConstraint constraintWithItem:self.iconImageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeading multiplier:1 constant:15],
                                           [NSLayoutConstraint constraintWithItem:self.iconImageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0],
                                           ]];
        
        [self.contentView addConstraints:@[
                                           [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.iconImageView attribute:NSLayoutAttributeTrailing multiplier:1 constant:10],
                                           [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0],
                                           ]];
    } else if (self.showType == WDPopMenuTypeOnlyTitle){
        self.titleLabel.textAlignment = NSTextAlignmentCenter;
        [self.contentView addConstraints:@[
                                           [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0],
                                           [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0],
                                           ]];
    } else {
        [self.contentView addConstraints:@[
                                           [NSLayoutConstraint constraintWithItem:self.iconImageView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0],
                                           [NSLayoutConstraint constraintWithItem:self.iconImageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0],
                                           ]];
    }
}
@end

接口代碼

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface WDPopMenu : UIView
@property (nonatomic, copy, readonly) NSArray<NSString *> *titles;
@property (nonatomic, copy, readonly) NSArray<NSString *> *iconImageNames;

/**
 僅顯示圖標(biāo) + 默認(rèn)樣式
 
 @param iconImageNames 圖標(biāo)數(shù)組
 @param position 初始位置
 @param complete 點(diǎn)擊回調(diào)
 */
+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> *)iconImageNames
                       originPosition:(CGPoint)position
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete;


/**
 僅顯示圖標(biāo) + 默認(rèn)樣式
 
 @param iconImageNames 圖標(biāo)數(shù)組
 @param position 初始位置
 @param isNeedSeparatorLine 是否顯示分割線(xiàn)
 @param complete 點(diǎn)擊回調(diào)
 */
+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> *)iconImageNames
                       originPosition:(CGPoint)position
                    needSeparatorLine:(BOOL)isNeedSeparatorLine
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete;



/**
 僅顯示標(biāo)題 + 默認(rèn)樣式
 
 @param titles 標(biāo)題數(shù)組
 @param position 初始位置
 @param complete 點(diǎn)擊回調(diào)
 */
+ (void)showPopMenuWithTitles:(NSArray<NSString *> *)titles
               originPosition:(CGPoint)position
         selectedItemComplete:(void(^)(NSInteger currentIndex))complete;


/**
 僅顯示標(biāo)題 + 默認(rèn)樣式
 
 @param titles 標(biāo)題數(shù)組
 @param position 初始位置
 @param isNeedSeparatorLine 是否顯示分割線(xiàn)
 @param complete 點(diǎn)擊回調(diào)
 */
+ (void)showPopMenuWithTitles:(NSArray<NSString *> *)titles
               originPosition:(CGPoint)position
            needSeparatorLine:(BOOL)isNeedSeparatorLine
         selectedItemComplete:(void(^)(NSInteger currentIndex))complete;



/**
 顯示文字和圖標(biāo) + 默認(rèn)樣式
 
 @param iconImageNames 圖標(biāo)數(shù)組
 @param titles 標(biāo)題數(shù)組
 @param position 初始位置
 @param complete 點(diǎn)擊回調(diào)
 */
+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> * _Nullable)iconImageNames
                               titles:(NSArray<NSString *> * _Nullable)titles
                       originPosition:(CGPoint)position
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete;


/**
 顯示文字和圖標(biāo) + 默認(rèn)樣式
 
 @param iconImageNames 圖標(biāo)數(shù)組
 @param titles 標(biāo)題數(shù)組
 @param position 初始位置
 @param isNeedSeparatorLine 是否顯示分割線(xiàn)
 @param complete 點(diǎn)擊回調(diào)
 */
+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> * _Nullable)iconImageNames
                               titles:(NSArray<NSString *> * _Nullable)titles
                       originPosition:(CGPoint)position
                    needSeparatorLine:(BOOL)isNeedSeparatorLine
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete;



/**
 可選顯示圖標(biāo)担敌、標(biāo)題
 
 @param iconImageNames 圖片數(shù)組 可以為空
 @param titles 標(biāo)題數(shù)組 可以為空
 @param position 初始位置
 @param height 單個(gè)item高度 默認(rèn)為45
 @param width 顯示View的高度 默認(rèn)為屏幕寬度的一半
 @param color 顯示View的背景顏色
 @param isNeedSeparatorLine 是否顯示分割線(xiàn)
 @param complete 點(diǎn)擊回調(diào)
 */
+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> * _Nullable)iconImageNames
                               titles:(NSArray<NSString *> * _Nullable)titles
                       originPosition:(CGPoint)position
                         heightOfItem:(CGFloat)height
                          widthOfView:(CGFloat)width
                backgroundColorOfView:(UIColor *)color
                    needSeparatorLine:(BOOL)isNeedSeparatorLine
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete;


/**
 可選顯示圖標(biāo)、標(biāo)題
 
 @param iconImageNames 圖片數(shù)組 可以為空
 @param titles 標(biāo)題數(shù)組 可以為空
 @param position 初始位置
 @param height 單個(gè)item高度 默認(rèn)為45
 @param width 顯示View的高度 默認(rèn)為屏幕寬度的一半
 @param color 顯示View的背景顏色
 @param isNeedSeparatorLine 是否顯示分割線(xiàn)
 @param titleLabel 自定義Item的Label樣式
 @param complete 點(diǎn)擊回調(diào)
 */
+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> * _Nullable)iconImageNames
                               titles:(NSArray<NSString *> * _Nullable)titles
                       originPosition:(CGPoint)position
                         heightOfItem:(CGFloat)height
                          widthOfView:(CGFloat)width
                backgroundColorOfView:(UIColor *)color
                     titleLabelOfItem:(UILabel * _Nullable)titleLabel
                    needSeparatorLine:(BOOL)isNeedSeparatorLine
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete;
@end

NS_ASSUME_NONNULL_END

實(shí)現(xiàn)代碼

#define WD_SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
#define WD_SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)

@interface WDPopMenu () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, copy) void (^selectedItemComplete)(NSInteger currentIndex);
@property (nonatomic, strong) UIView *backgroundView;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, assign) CGFloat viewWidth;
@property (nonatomic, assign) CGFloat itemHeight;
@property (nonatomic, assign) CGPoint beginPosition;
@property (nonatomic, strong) UIColor *cellColor;
@property (nonatomic, copy, readwrite) NSArray<NSString *> *titles;
@property (nonatomic, copy, readwrite) NSArray<NSString *> *iconImageNames;
@end

@implementation WDPopMenu

- (instancetype)init {
    self = [super init];
    if (self) {
        [self addSubview:self.tableView];
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTappedTarget:)];
        [self.backgroundView addGestureRecognizer:tap];
    }
    return self;
}

+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> *)iconImageNames
                       originPosition:(CGPoint)position
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete {
    
    [self showPopMenuWithIconImageNames:iconImageNames
                         originPosition:position
                      needSeparatorLine:YES
                   selectedItemComplete:complete];
}

+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> *)iconImageNames
                       originPosition:(CGPoint)position
                    needSeparatorLine:(BOOL)isNeedSeparatorLine
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete {
    
    [self showPopMenuWithIconImageNames:iconImageNames
                                 titles:nil
                         originPosition:position
                      needSeparatorLine:isNeedSeparatorLine
                   selectedItemComplete:complete];
}

+ (void)showPopMenuWithTitles:(NSArray<NSString *> *)titles
               originPosition:(CGPoint)position
         selectedItemComplete:(void(^)(NSInteger currentIndex))complete {
    
    [self showPopMenuWithTitles:titles
                 originPosition:position
              needSeparatorLine:YES
           selectedItemComplete:complete];
}

+ (void)showPopMenuWithTitles:(NSArray<NSString *> *)titles
               originPosition:(CGPoint)position
            needSeparatorLine:(BOOL)isNeedSeparatorLine
         selectedItemComplete:(void(^)(NSInteger currentIndex))complete {
    
    [self showPopMenuWithIconImageNames:nil
                                 titles:titles
                         originPosition:position
                      needSeparatorLine:isNeedSeparatorLine
                   selectedItemComplete:complete];
}

+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> * _Nullable)iconImageNames
                               titles:(NSArray<NSString *> * _Nullable)titles
                       originPosition:(CGPoint)position
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete {
    
    [self showPopMenuWithIconImageNames:iconImageNames
                                 titles:titles
                         originPosition:position
                      needSeparatorLine:YES
                   selectedItemComplete:complete];
}

+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> * _Nullable)iconImageNames
                               titles:(NSArray<NSString *> * _Nullable)titles
                       originPosition:(CGPoint)position
                    needSeparatorLine:(BOOL)isNeedSeparatorLine
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete {
    
    [self showPopMenuWithIconImageNames:iconImageNames
                                 titles:titles
                         originPosition:position
                           heightOfItem:0
                            widthOfView:0
                  backgroundColorOfView:[UIColor whiteColor]
                      needSeparatorLine:isNeedSeparatorLine
                   selectedItemComplete:complete];
}

+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> * _Nullable)iconImageNames
                               titles:(NSArray<NSString *> * _Nullable)titles
                       originPosition:(CGPoint)position
                         heightOfItem:(CGFloat)height
                          widthOfView:(CGFloat)width
                backgroundColorOfView:(UIColor *)color
                    needSeparatorLine:(BOOL)isNeedSeparatorLine
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete {
    
    [self showPopMenuWithIconImageNames:iconImageNames
                                 titles:titles
                         originPosition:position
                           heightOfItem:height
                            widthOfView:width
                  backgroundColorOfView:color
                       titleLabelOfItem:nil
                      needSeparatorLine:isNeedSeparatorLine
                   selectedItemComplete:complete];
}

+ (void)showPopMenuWithIconImageNames:(NSArray<NSString *> * _Nullable)iconImageNames
                               titles:(NSArray<NSString *> * _Nullable)titles
                       originPosition:(CGPoint)position
                         heightOfItem:(CGFloat)height
                          widthOfView:(CGFloat)width
                backgroundColorOfView:(UIColor *)color
                     titleLabelOfItem:(UILabel * _Nullable)titleLabel
                    needSeparatorLine:(BOOL)isNeedSeparatorLine
                 selectedItemComplete:(void(^)(NSInteger currentIndex))complete {
    
    WDPopMenu *v                = [[WDPopMenu alloc] init];
    v.iconImageNames            = iconImageNames;
    v.titles                    = titles;
    v.beginPosition             = position;
    v.itemHeight                = height == 0 ? 45 : height;
    v.viewWidth                 = width == 0 ? [[UIScreen mainScreen] bounds].size.width / 2 : width;
    v.tableView.backgroundColor = color;
    v.cellColor                 = color;
    v.titleLabel                = titleLabel;
    v.selectedItemComplete      = complete;
    v.tableView.rowHeight       = v.itemHeight;
    if (isNeedSeparatorLine) {
        v.tableView.separatorStyle  = UITableViewCellSeparatorStyleSingleLine;
    } else {
        v.tableView.separatorStyle  = UITableViewCellSeparatorStyleNone;
    }
    [v.tableView reloadData];
    [v showView];
}

- (void)backgroundTappedTarget:(id)sender {
    [self hidenView];
}

- (void)showView {
    [[[UIApplication sharedApplication] keyWindow] addSubview:self.backgroundView];
    [[[UIApplication sharedApplication] keyWindow] addSubview:self];
    [self setupViewAndArrowShape];
    [self showAnimation];
}

- (void)setupViewAndArrowShape {
    CGRect toFrame = CGRectZero;
    toFrame.size.width = self.viewWidth;
    if (self.iconImageNames.count == 0 || self.iconImageNames == nil) {
        toFrame.size.height = self.itemHeight * self.titles.count + 15;
    } else if (self.titles == nil || self.titles.count == 0) {
        toFrame.size.height = self.itemHeight * self.iconImageNames.count + 15;
    } else {
        toFrame.size.height = self.itemHeight * self.titles.count + 15;
    }
    
    WudanLayer *bbLayer = [[WudanLayer alloc] initWithSize:toFrame.size];
    
    if (self.beginPosition.x + self.viewWidth / 2 > WD_SCREEN_WIDTH) {
        toFrame.origin.x = WD_SCREEN_WIDTH - 10 - self.viewWidth;
        bbLayer.arrowDirection = ArrowDirectionTop;
        bbLayer.arrowPosition = (self.beginPosition.x - toFrame.origin.x + 15) / self.viewWidth;
        self.layer.anchorPoint = CGPointMake(bbLayer.arrowPosition, 0);
    } else if (self.beginPosition.x - self.viewWidth / 2 < 0){
        toFrame.origin.x = 10;
        bbLayer.arrowDirection = ArrowDirectionTop;
        bbLayer.arrowPosition = (self.beginPosition.x - toFrame.origin.x - 15) / self.viewWidth;
        self.layer.anchorPoint = CGPointMake(bbLayer.arrowPosition, 0);
    } else {
        toFrame.origin.x = self.beginPosition.x - self.viewWidth / 2;
        bbLayer.arrowDirection = ArrowDirectionTop;
        bbLayer.arrowPosition = (self.beginPosition.x - toFrame.origin.x) / self.viewWidth;
        self.layer.anchorPoint = CGPointMake(bbLayer.arrowPosition, 0);
    }
    
    if (self.beginPosition.y + toFrame.size.height > WD_SCREEN_HEIGHT) {
        toFrame.origin.y = self.beginPosition.y - toFrame.size.height - 15;
        bbLayer.arrowDirection = ArrowDirectionBottom;
        self.layer.anchorPoint = CGPointMake(bbLayer.arrowPosition, 1);
    } else {
        toFrame.origin.y = self.beginPosition.y + 15;
    }
    
    self.tableView.frame = CGRectZero;
    self.frame = CGRectMake(self.beginPosition.x, self.beginPosition.y, 0, 0);
    self.alpha = 0;
    self.frame = toFrame;
    if (self.beginPosition.y + toFrame.size.height > WD_SCREEN_HEIGHT) {
        self.tableView.contentInset = UIEdgeInsetsZero;
    } else {
        self.tableView.contentInset = UIEdgeInsetsMake(15, 0, 0, 0);
    }
    self.tableView.frame = CGRectMake(0, 0, toFrame.size.width, toFrame.size.height);
    
    bbLayer.cornerRadius = 8;
    bbLayer.arrowHeight = 15;
    bbLayer.arrowWidth = 30;
    bbLayer.arrowRadius = 0;
    [self.layer setMask:[bbLayer layer]];
}

- (void)showAnimation {
    self.layer.affineTransform = CGAffineTransformMakeScale(0.1, 0.1);
    [UIView animateWithDuration:0.35 animations:^{
        self.backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.1];
        self.alpha = 1;
        self.layer.affineTransform = CGAffineTransformIdentity;
    }];
}

- (void)hidenView {
    [UIView animateWithDuration:0.35 animations:^{
        self.alpha = 0;
        self.layer.affineTransform = CGAffineTransformMakeScale(0.1, 0.1);
        self.backgroundView.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
    } completion:^(BOOL finished) {
        [self removeFromSuperview];
        self.layer.affineTransform = CGAffineTransformIdentity;
        [self.backgroundView removeFromSuperview];
    }];
}

#pragma mark - UITableView DataSource Method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (self.titles.count > 0) {
        return self.titles.count;
    }
    
    if (self.iconImageNames.count > 0){
        return self.iconImageNames.count;
    }
    return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    WDPopMenuCell *cell = [tableView dequeueReusableCellWithIdentifier:@"WDPopMenuCell"];
    if (!cell) {
        cell = [[WDPopMenuCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"WDPopMenuCell"];
    }
    
    if (self.iconImageNames.count == 0 || self.iconImageNames == nil) {
        cell.showType = WDPopMenuTypeOnlyTitle;
        cell.titleLabel.text = self.titles[indexPath.row];
        
    } else if (self.titles == nil || self.titles.count == 0) {
        cell.showType = WDPopMenuTypeOnlyIcon;
        cell.iconImageView.image = [UIImage imageNamed:self.iconImageNames[indexPath.row]];
    } else {
        cell.showType = WDPopMenuTypeNormal;
        cell.iconImageView.image = [UIImage imageNamed:self.iconImageNames[indexPath.row]];
        cell.titleLabel.text = self.titles[indexPath.row];
    }
    if (self.titleLabel) {
        cell.titleLabel.textColor = self.titleLabel.textColor;
        cell.titleLabel.textAlignment = self.titleLabel.textAlignment;
        cell.titleLabel.font = self.titleLabel.font;
    }
    
    cell.contentView.backgroundColor = self.cellColor;
    
    if (indexPath.row == [tableView numberOfRowsInSection:indexPath.section] - 1) {
        cell.separatorInset = UIEdgeInsetsMake(0, self.bounds.size.width, 0, 0);
    } else {
        cell.separatorInset = UIEdgeInsetsMake(0, 0, 0, 0);
    }
    return cell;
}

#pragma mark - UITableView Delegate Method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    [self hidenView];
    self.selectedItemComplete(indexPath.row);
}

#pragma mark - Setter
- (UITableView *)tableView {
    if (!_tableView) {
        _tableView                 = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        _tableView.tableFooterView = [[UIView alloc] init];
        _tableView.dataSource      = self;
        _tableView.delegate        = self;
        _tableView.scrollEnabled   = NO;
    }
    return _tableView;
}

- (UIView *)backgroundView {
    if (!_backgroundView) {
        _backgroundView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    }
    return _backgroundView;
}
@end

實(shí)現(xiàn)效果圖

效果圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末廷蓉,一起剝皮案震驚了整個(gè)濱河市全封,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桃犬,老刑警劉巖刹悴,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異攒暇,居然都是意外死亡土匀,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)形用,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)就轧,“玉大人,你說(shuō)我怎么就攤上這事田度〉龇幔” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵每币,是天一觀的道長(zhǎng)携丁。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么梦鉴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任李茫,我火速辦了婚禮,結(jié)果婚禮上肥橙,老公的妹妹穿的比我還像新娘魄宏。我一直安慰自己,他們只是感情好存筏,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布宠互。 她就那樣靜靜地躺著,像睡著了一般椭坚。 火紅的嫁衣襯著肌膚如雪予跌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天善茎,我揣著相機(jī)與錄音券册,去河邊找鬼。 笑死垂涯,一個(gè)胖子當(dāng)著我的面吹牛烁焙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播耕赘,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼骄蝇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了操骡?” 一聲冷哼從身側(cè)響起乞榨,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎当娱,沒(méi)想到半個(gè)月后吃既,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡跨细,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年鹦倚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冀惭。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡震叙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出散休,到底是詐尸還是另有隱情媒楼,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布戚丸,位于F島的核電站划址,受9級(jí)特大地震影響扔嵌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜夺颤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一痢缎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧世澜,春花似錦独旷、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至封恰,卻和暖如春麻养,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俭驮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工回溺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留春贸,地道東北人混萝。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像萍恕,于是被迫代替她去往敵國(guó)和親逸嘀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 1 《紅樓夢(mèng)》第48回,寫(xiě)的是香菱學(xué)詩(shī)的故事类垫,曹雪芹是在給香菱立傳司光。香菱學(xué)詩(shī),心無(wú)旁騖悉患,終有所成残家。我被震撼到了。我...
    永遠(yuǎn)的心靈之約閱讀 809評(píng)論 7 7
  • 學(xué)海長(zhǎng)泛 行到深林煉古鐵售躁,十年一劍落孫山坞淮。 禹門(mén)淺浪空留處,學(xué)海無(wú)涯長(zhǎng)泛間陪捷。 小亮疏機(jī)登進(jìn)士回窘,大經(jīng)滿(mǎn)腹授推官。 平...
    c志超閱讀 164評(píng)論 0 0
  • “當(dāng)iPhone X上市了,我沒(méi)買(mǎi)。一方面覺(jué)得這次在設(shè)計(jì)上沒(méi)有大的突破付枫,另一方面我買(mǎi)不起烹玉。” 同事發(fā)了這條朋友圈阐滩。...
    愛(ài)上文化閱讀 632評(píng)論 0 3
  • 超級(jí)個(gè)體第三天二打,每日一問(wèn): 今天雙十一,就來(lái)聊聊購(gòu)物吧掂榔。你買(mǎi)過(guò)的什么商品很大程度提高過(guò)你的工作效率或者生活質(zhì)量继效?這...
    得到超級(jí)個(gè)體閱讀 1,106評(píng)論 0 2
  • 1.當(dāng)你知道一切都無(wú)法逃避時(shí),你會(huì)學(xué)著用笑來(lái)哭泣装获。 2.憂(yōu)傷是夜色中無(wú)聲泛起的涼瑞信,它如此的真切而熟悉,宛如故人重臨...
    噠噠的每一天閱讀 335評(píng)論 4 7