美文网首页
Swift 仿QQ的长按手势Menu效果

Swift 仿QQ的长按手势Menu效果

作者: 船长_ | 来源:发表于2017-11-29 21:16 被阅读141次

本文参考:原Objective-C作者:si1ence

demo.gif

1.满足的条件:

  • 1.左侧和右侧展示的按钮不一样(比如右侧自己发送的消息有撤回)
  • 2.不同类型的消息展示的按钮不一样(比如文本可以复制,文件类型的消息可以进行下载)
  • 3.MenuView 要根据targetRect(即文本框的frame)自动计算出自己合适的frame,靠上还是靠下,特别长的文本要显示在中间
  • 4.tableView 滑动,当前页面消失、点击 MenuView 的按钮,该控件都要从父View 移除(发送对应的通知)
  • 5.点击每个按钮要响应对应的事件(通过代理方法来实现)

作者:si1ence
链接:http://www.jianshu.com/p/ea2c238c8907

2.实现思路:

  • 1.监听cell的长按手势
  • 2.自定义View,添加到keyWindow
  • 3.通过设置代理,实现代理的方法,监听具体按钮的点击
  • 4.监听tableView 滑动,移除MenuView
  • 5.手指长按cell时,计算出气泡相对于keyWindowrect,找出最合适的menu位置

3.所用到的知识点:

  • 1.代理的用法(代码示例)
@objc protocol MenuViewDelegate : NSObjectProtocol {
   @objc optional func menuToThumbup()
   @objc optional func menuToCopy()
   @objc optional func menutoDelete()
   @objc optional func menuToTransmit()
   @objc optional func menuToDowanload()
   @objc optional func menuToPreview()
}

open weak var delegate : MenuViewDelegate?

// MARK: Private Method
extension MenuView {
    
    @objc func thumbupButtonTapped(){
        
        if let delegate = delegate,delegate.responds(to: #selector(delegate.menuToThumbup)) {
            delegate.menuToThumbup!()
        }
        self.removeFromSuperview()
    }
}
  • 2.结构体的用法(代码示例)
struct MenuItemType : OptionSet {
    
    public var rawValue = 0  // 因为RawRepresentable的要求
    public static var copys = MenuItemType(rawValue : 1 << 0)

    public static var transmit = MenuItemType(rawValue : 1 << 1)

    public static var collect = MenuItemType(rawValue : 1 << 2)

    public static var delete = MenuItemType(rawValue : 1 << 3)

    public static var revoke = MenuItemType(rawValue : 1 << 4)

    public static var download = MenuItemType(rawValue : 1 << 5)
}

// 用法示例
if message?.msgDirection == .incoming {
      customMenu.itemType = [.copys,.transmit,.collect,.delete]
}else{
      customMenu.itemType = [.copys,.transmit,.collect,.revoke,.delete]
}
  • 3.UIStackView用法(代码示例)
lazy var containerView : UIStackView = {
      let containerView = UIStackView()
      containerView.alignment = .fill
      containerView.isUserInteractionEnabled = true
       return containerView
}()

containerView.addArrangedSubview(copyingButton)
containerView.addArrangedSubview(transmitButton)
containerView.addArrangedSubview(collectButton)
  • 4.UIButton 分类,实现自定义button图片和文字相对位置
enum ImagePosition {
    case left
    case right
    case top
    case bottom
}

extension UIButton {
    
    /**
     *  利用UIButton的titleEdgeInsets和imageEdgeInsets来实现文字和图片的自由排列
     *  注意:这个方法需要在设置图片和文字之后才可以调用,且button的大小要大于 图片大小+文字大小+spacing
     *
     *  @param spacing 图片和文字的间隔
     */
     func setImagePosition(position : ImagePosition,spacing: CGFloat) {
        
        let imageWith : CGFloat = imageView!.image!.size.width
        let imageHeight : CGFloat = imageView!.image!.size.height
        
        let attrs = [NSAttributedStringKey.font:titleLabel!.font!]
        let labelWidth : CGFloat = titleLabel!.text!.size(withAttributes: attrs).width
        let labelHeight : CGFloat = titleLabel!.text!.size(withAttributes: attrs).height
        
        //image中心移动的x距离
        let imageOffsetX : CGFloat = (imageWith + labelWidth) / 2 - imageWith / 2
        //image中心移动的x距离
        let imageOffsetY : CGFloat = imageHeight / 2 + spacing / 2
        //label中心移动的x距离
        let labelOffsetX : CGFloat = (imageWith + labelWidth / 2) - (imageWith + labelWidth) / 2
        //label中心移动的y距离
        let labelOffsetY : CGFloat = labelHeight / 2 + spacing / 2
        
        switch position {
        case .left:
            imageEdgeInsets = UIEdgeInsetsMake(0, -spacing/2, 0, spacing/2)
            titleEdgeInsets = UIEdgeInsetsMake(0, spacing/2, 0, -spacing/2)
        case .right:
            imageEdgeInsets = UIEdgeInsetsMake(0, labelWidth + spacing/2, 0, -(labelWidth + spacing/2))
            titleEdgeInsets = UIEdgeInsetsMake(0, -(imageHeight + spacing/2), 0, imageHeight + spacing/2)
        case .top:
            imageEdgeInsets = UIEdgeInsetsMake(-imageOffsetY, imageOffsetX, imageOffsetY, -imageOffsetX)
            titleEdgeInsets = UIEdgeInsetsMake(labelOffsetY, -labelOffsetX, -labelOffsetY, labelOffsetX)
        case .bottom:
            imageEdgeInsets = UIEdgeInsetsMake(imageOffsetY, imageOffsetX, -imageOffsetY, -imageOffsetX)
            titleEdgeInsets = UIEdgeInsetsMake(-labelOffsetY, -labelOffsetX, labelOffsetY, labelOffsetX)
         }
    }
}
  • 5.SnapKit的简单用法
avatarHeaderView.snp.makeConstraints({ (make) in
    make.top.equalToSuperview()
    make.width.height.equalTo(kAvatarSize)
    make.leading.equalToSuperview().offset(kAvatarMarginH)
})

bubbleView.snp.makeConstraints({ (make) in
    make.width.lessThanOrEqualToSuperview()
    make.left.equalToSuperview().offset(kAvatarSize + 18)
    make.top.equalTo(contentView)
    make.right.lessThanOrEqualTo(contentView).offset(-73)
    make.bottom.equalTo(contentView).offset(-20).priority(.low)
})
  • 6.枚举成员的初始值用法
enum MessageType : String {
    case text = "text"
    case image = "image"  
    case voice = "voice"
}
var identifier : String {
    get{
        let rawIdentifier = self.msgDirection == .incoming ? kCellIdentifierLeft : kCellIdentifierRight
        return String.init(format: "%@%@", rawIdentifier,self.msgType.rawValue)
    }
}

GitHub下载链接

相关文章

网友评论

      本文标题:Swift 仿QQ的长按手势Menu效果

      本文链接:https://www.haomeiwen.com/subject/gwmpbxtx.html