Merge branch 'feature/cell_action_bubble' into develop

This commit is contained in:
nixzhu
2015-07-23 17:29:17 +08:00
8 changed files with 482 additions and 119 deletions
+12
View File
@@ -97,6 +97,7 @@
5039594F1B05E6E000797A3E /* ProfileSocialAccountGithubCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5039594D1B05E6E000797A3E /* ProfileSocialAccountGithubCell.xib */; };
503A54ED1B328ADB002077BD /* NSLocale+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503A54EC1B328ADB002077BD /* NSLocale+Yep.swift */; };
503CAB431AC00CB100DFE830 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503CAB421AC00CB100DFE830 /* ConversationViewController.swift */; };
5044BF061B5FA74800930ADC /* BubbleMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5044BF051B5FA74800930ADC /* BubbleMenuView.swift */; };
504BDC151B4528A500DBD35D /* Badges.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504BDC141B4528A500DBD35D /* Badges.xcassets */; };
504CBA3D1B3D04B800663A90 /* ChatTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504CBA3C1B3D04B800663A90 /* ChatTextView.swift */; };
504F41251ACE2D1D00FBB19A /* SampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504F41241ACE2D1D00FBB19A /* SampleView.swift */; };
@@ -321,6 +322,7 @@
5039594D1B05E6E000797A3E /* ProfileSocialAccountGithubCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ProfileSocialAccountGithubCell.xib; path = Views/Cells/ProfileSocialAccount/ProfileSocialAccountGithubCell.xib; sourceTree = "<group>"; };
503A54EC1B328ADB002077BD /* NSLocale+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "NSLocale+Yep.swift"; path = "Extensions/NSLocale+Yep.swift"; sourceTree = "<group>"; };
503CAB421AC00CB100DFE830 /* ConversationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationViewController.swift; path = ViewControllers/Conversation/ConversationViewController.swift; sourceTree = "<group>"; };
5044BF051B5FA74800930ADC /* BubbleMenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BubbleMenuView.swift; path = Views/BubbleMenu/BubbleMenuView.swift; sourceTree = "<group>"; };
504BDC141B4528A500DBD35D /* Badges.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Badges.xcassets; sourceTree = "<group>"; };
504CBA3C1B3D04B800663A90 /* ChatTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatTextView.swift; path = Views/TextViews/ChatTextView.swift; sourceTree = "<group>"; };
504F41241ACE2D1D00FBB19A /* SampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SampleView.swift; path = Views/AudioWaves/SampleView.swift; sourceTree = "<group>"; };
@@ -782,6 +784,7 @@
5019F62F1B39BFCB0000D993 /* ConversationMore */,
500A0B201B451A7400D49358 /* Badge */,
504F85AD1B4D21E400F909BE /* DiscoverFilter */,
5044BF071B5FA75000930ADC /* BubbleMenu */,
);
name = Views;
sourceTree = "<group>";
@@ -1030,6 +1033,14 @@
name = Conversation;
sourceTree = "<group>";
};
5044BF071B5FA75000930ADC /* BubbleMenu */ = {
isa = PBXGroup;
children = (
5044BF051B5FA74800930ADC /* BubbleMenuView.swift */,
);
name = BubbleMenu;
sourceTree = "<group>";
};
504CBA3B1B3D048900663A90 /* TextViews */ = {
isa = PBXGroup;
children = (
@@ -1802,6 +1813,7 @@
5023DE2A1AB685BA00B3EE96 /* ContactsViewController.swift in Sources */,
50CDBE491ACBAD3200459CE0 /* ChatLeftImageCell.swift in Sources */,
5033159C1AC101C80008A209 /* ChatRightTextCell.swift in Sources */,
5044BF061B5FA74800930ADC /* BubbleMenuView.swift in Sources */,
5023DE561AB6CA1000B3EE96 /* AvatarCache.swift in Sources */,
5023DE371AB6979600B3EE96 /* ConversationCell.swift in Sources */,
502AE5351AB8649F005BD199 /* LoginByMobileViewController.swift in Sources */,
+1 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="15A216g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="Ha4-ol-D2e">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="15A226f" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="Ha4-ol-D2e">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
+4
View File
@@ -81,6 +81,10 @@ class YepConfig {
return 100
}
struct Conversation {
static let menuDirectionUpThreshold: CGFloat = 64 + 60
}
struct AudioRecord {
static let shortestDuration: NSTimeInterval = 0.5
static let longestDuration: NSTimeInterval = 60
@@ -109,6 +109,13 @@ class ConversationViewController: BaseViewController {
@IBOutlet weak var takePhotoButton: MessageTypeButton!
@IBOutlet weak var addLocationButton: MessageTypeButton!
var currentMenu: BubbleMenuView?
func removeOldMenu() {
currentMenu?.hide()
currentMenu = nil
}
var originalNavigationControllerDelegate: UINavigationControllerDelegate?
var waverView: YepWaverView!
@@ -525,6 +532,8 @@ class ConversationViewController: BaseViewController {
messageToolbar.stateTransitionAction = { [weak self] (messageToolbar, previousState, currentState) in
self?.removeOldMenu()
if let strongSelf = self {
switch (previousState, currentState) {
@@ -634,8 +643,6 @@ class ConversationViewController: BaseViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
conversationCollectionViewHasBeenMovedToBottomOnce = true
FayeService.sharedManager.delegate = self
@@ -668,6 +675,49 @@ class ConversationViewController: BaseViewController {
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.setNeedsStatusBarAppearanceUpdate()
/*
// test menu
if true {
let copyItem = BubbleMenuView.Item(title: "Copy") {
print("copy\n")
}
let deleteItem = BubbleMenuView.Item(title: "Delete") {
print("delete\n")
}
let menu = BubbleMenuView(items: [copyItem, deleteItem])
menu.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(menu)
let menuCenterY = NSLayoutConstraint(item: menu, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: 0)
let menuCenterX = NSLayoutConstraint(item: menu, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([menuCenterY, menuCenterX])
}
if true {
let copyItem = BubbleMenuView.Item(title: "Copy") {
print("copy\n")
}
let menu = BubbleMenuView(items: [copyItem])
menu.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(menu)
let menuCenterY = NSLayoutConstraint(item: menu, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: -100)
let menuCenterX = NSLayoutConstraint(item: menu, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([menuCenterY, menuCenterX])
}
*/
}
private func markMessageAsReaded(message: Message) {
@@ -700,6 +750,12 @@ class ConversationViewController: BaseViewController {
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
removeOldMenu()
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
@@ -971,6 +1027,8 @@ class ConversationViewController: BaseViewController {
func moreAction() {
removeOldMenu()
messageToolbar.state = .Default
moreView.showProfileAction = { [weak self] in
@@ -1802,6 +1860,7 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
markMessageAsReaded(message)
switch message.mediaType {
case MessageMediaType.Image.rawValue:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatLeftImageCellIdentifier, forIndexPath: indexPath) as! ChatLeftImageCell
@@ -1886,12 +1945,40 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message), collectionView: collectionView, indexPath: indexPath)
cell.longPressAction = { [weak self] cell in
self?.removeOldMenu()
if let strongSelf = self {
let copyItem = BubbleMenuView.Item(type: .Normal, title: NSLocalizedString("Copy", comment: "")) { menu in
print("copy\n")
UIPasteboard.generalPasteboard().string = cell.textContentTextView.text
menu.hide()
self?.currentMenu = nil
}
let bubbleFrame = cell.convertRect(cell.bubbleBodyImageView.frame, toView: strongSelf.view)
let arrowDirection: BubbleMenuView.ArrowDirection = CGRectGetMidY(bubbleFrame) < YepConfig.Conversation.menuDirectionUpThreshold ? .Up : .Down
let menu = BubbleMenuView(arrowDirection: arrowDirection, items: [copyItem])
strongSelf.currentMenu = menu
menu.showInView(strongSelf.view, withBubbleFrame: bubbleFrame)
}
}
return cell
}
} else { // from Me
switch message.mediaType {
case MessageMediaType.Image.rawValue:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatRightImageCellIdentifier, forIndexPath: indexPath) as! ChatRightImageCell
@@ -2063,77 +2150,109 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
}
}, collectionView: collectionView, indexPath: indexPath)
cell.deleteMessageAction = { [weak self] in
dispatch_async(dispatch_get_main_queue()) {
if let strongSelf = self, realm = message.realm {
cell.longPressAction = { [weak self] cell in
var sectionDateMessage: Message?
self?.removeOldMenu()
if let currentMessageIndex = strongSelf.messages.indexOf(message) {
if let strongSelf = self {
let previousMessageIndex = currentMessageIndex - 1
let copyItem = BubbleMenuView.Item(type: .Normal, title: NSLocalizedString("Copy", comment: "")) { menu in
print("copy\n")
if let previousMessage = strongSelf.messages[safe: previousMessageIndex] {
UIPasteboard.generalPasteboard().string = cell.textContentTextView.text
if previousMessage.mediaType == MessageMediaType.SectionDate.rawValue {
sectionDateMessage = previousMessage
}
}
}
let currentIndexPath: NSIndexPath
if let index = strongSelf.messages.indexOf(message) {
currentIndexPath = NSIndexPath(forItem: index - strongSelf.displayedMessagesRange.location, inSection: indexPath.section)
} else {
currentIndexPath = indexPath
}
if let sectionDateMessage = sectionDateMessage {
var canDeleteTwoMessages = false // sectionDate
if strongSelf.displayedMessagesRange.length >= 2 {
strongSelf.displayedMessagesRange.length -= 2
canDeleteTwoMessages = true
} else {
if strongSelf.displayedMessagesRange.location >= 1 {
strongSelf.displayedMessagesRange.location -= 1
}
strongSelf.displayedMessagesRange.length -= 1
}
realm.write {
if let mediaMetaData = sectionDateMessage.mediaMetaData {
realm.delete(mediaMetaData)
}
if let mediaMetaData = message.mediaMetaData {
realm.delete(mediaMetaData)
}
realm.delete(sectionDateMessage)
realm.delete(message)
}
if canDeleteTwoMessages {
let previousIndexPath = NSIndexPath(forItem: currentIndexPath.item - 1, inSection: currentIndexPath.section)
strongSelf.conversationCollectionView.deleteItemsAtIndexPaths([previousIndexPath, currentIndexPath])
} else {
strongSelf.conversationCollectionView.deleteItemsAtIndexPaths([currentIndexPath])
}
} else {
strongSelf.displayedMessagesRange.length -= 1
realm.write {
if let mediaMetaData = message.mediaMetaData {
realm.delete(mediaMetaData)
}
realm.delete(message)
}
strongSelf.conversationCollectionView.deleteItemsAtIndexPaths([currentIndexPath])
}
//
strongSelf.lastTimeMessagesCount = strongSelf.messages.count
menu.hide()
self?.currentMenu = nil
}
let deleteItem = BubbleMenuView.Item(type: .Danger, title: NSLocalizedString("Delete", comment: "")) { menu in
print("delete\n")
dispatch_async(dispatch_get_main_queue()) {
if let strongSelf = self, realm = message.realm {
var sectionDateMessage: Message?
if let currentMessageIndex = strongSelf.messages.indexOf(message) {
let previousMessageIndex = currentMessageIndex - 1
if let previousMessage = strongSelf.messages[safe: previousMessageIndex] {
if previousMessage.mediaType == MessageMediaType.SectionDate.rawValue {
sectionDateMessage = previousMessage
}
}
}
let currentIndexPath: NSIndexPath
if let index = strongSelf.messages.indexOf(message) {
currentIndexPath = NSIndexPath(forItem: index - strongSelf.displayedMessagesRange.location, inSection: indexPath.section)
} else {
currentIndexPath = indexPath
}
if let sectionDateMessage = sectionDateMessage {
var canDeleteTwoMessages = false // sectionDate
if strongSelf.displayedMessagesRange.length >= 2 {
strongSelf.displayedMessagesRange.length -= 2
canDeleteTwoMessages = true
} else {
if strongSelf.displayedMessagesRange.location >= 1 {
strongSelf.displayedMessagesRange.location -= 1
}
strongSelf.displayedMessagesRange.length -= 1
}
realm.write {
if let mediaMetaData = sectionDateMessage.mediaMetaData {
realm.delete(mediaMetaData)
}
if let mediaMetaData = message.mediaMetaData {
realm.delete(mediaMetaData)
}
realm.delete(sectionDateMessage)
realm.delete(message)
}
if canDeleteTwoMessages {
let previousIndexPath = NSIndexPath(forItem: currentIndexPath.item - 1, inSection: currentIndexPath.section)
strongSelf.conversationCollectionView.deleteItemsAtIndexPaths([previousIndexPath, currentIndexPath])
} else {
strongSelf.conversationCollectionView.deleteItemsAtIndexPaths([currentIndexPath])
}
} else {
strongSelf.displayedMessagesRange.length -= 1
realm.write {
if let mediaMetaData = message.mediaMetaData {
realm.delete(mediaMetaData)
}
realm.delete(message)
}
strongSelf.conversationCollectionView.deleteItemsAtIndexPaths([currentIndexPath])
}
//
strongSelf.lastTimeMessagesCount = strongSelf.messages.count
}
}
menu.hide()
self?.currentMenu = nil
}
let bubbleFrame = cell.convertRect(cell.bubbleBodyImageView.frame, toView: strongSelf.view)
let arrowDirection: BubbleMenuView.ArrowDirection = CGRectGetMidY(bubbleFrame) < YepConfig.Conversation.menuDirectionUpThreshold ? .Up : .Down
let menu = BubbleMenuView(arrowDirection: arrowDirection, items: [copyItem, deleteItem])
strongSelf.currentMenu = menu
menu.showInView(strongSelf.view, withBubbleFrame: bubbleFrame)
}
}
@@ -2186,6 +2305,8 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
func scrollViewDidScroll(scrollView: UIScrollView) {
pullToRefreshView.scrollViewDidScroll(scrollView)
removeOldMenu()
}
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
+269
View File
@@ -0,0 +1,269 @@
//
// BubbleMenuView.swift
// Yep
//
// Created by NIX on 15/7/22.
// Copyright (c) 2015 Catch Inc. All rights reserved.
//
import UIKit
class BubbleMenuView: UIView {
enum ArrowDirection {
case Up
case Down
}
struct Item {
enum Type {
case Normal
case Danger
}
let type: Type
let title: String
let action: BubbleMenuView -> Void
}
var arrowDirection: ArrowDirection
var items: [Item]
var buttons = [UIButton]()
init(arrowDirection: ArrowDirection, items: [Item]) {
self.arrowDirection = arrowDirection
self.items = items
super.init(frame: CGRectZero)
makeUI()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: Actions
func tapButton(button: UIButton) {
if let index = find(buttons, button) {
let action = items[index].action
action(self)
}
}
func showInView(view: UIView, withBubbleFrame bubbleFrame: CGRect) {
// position
setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(self)
var vConstant = CGRectGetMidY(bubbleFrame) - CGRectGetMidY(view.frame)
let menuV: NSLayoutConstraint
switch arrowDirection {
case .Up:
vConstant += ceil(CGRectGetHeight(bubbleFrame) * 0.5) - offsetV
menuV = NSLayoutConstraint(item: self, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: vConstant)
case .Down:
vConstant -= ceil(CGRectGetHeight(bubbleFrame) * 0.5) - offsetV
menuV = NSLayoutConstraint(item: self, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: vConstant)
}
let centerXConstant = CGRectGetMidX(bubbleFrame) - CGRectGetMidX(view.frame)
let menuCenterX = NSLayoutConstraint(item: self, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: centerXConstant)
NSLayoutConstraint.activateConstraints([menuV, menuCenterX])
// animation
transform = CGAffineTransformMakeScale(0.0001, 0.0001)
UIView.animateWithDuration(0.25, delay: 0.0, usingSpringWithDamping: 0.65, initialSpringVelocity: 0.0, options: UIViewAnimationOptions(0), animations: { _ in
self.transform = CGAffineTransformMakeScale(1.0, 1.0)
}, completion: { finished in
self.transform = CGAffineTransformIdentity
})
}
func hide() {
UIView.animateWithDuration(0.1, delay: 0.0, options: .CurveEaseInOut, animations: { _ in
self.transform = CGAffineTransformMakeScale(0.0001, 0.0001)
}, completion: { finished in
self.removeFromSuperview()
})
}
// MARK: UI
let arrowHeight: CGFloat = 8
let buttonGap: CGFloat = 16
let offsetV: CGFloat = -1
func makeUI() {
backgroundColor = UIColor.clearColor()
var format = "H:|"
for (index, item) in enumerate(items) {
let button = UIButton()
switch item.type {
case .Normal:
button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
button.setTitleColor(UIColor.lightGrayColor(), forState: .Highlighted)
case .Danger:
let dangerRed = UIColor(red: 1, green: 0.4, blue: 0.4, alpha: 1)
button.setTitleColor(dangerRed, forState: .Normal)
button.setTitleColor(dangerRed.colorWithAlphaComponent(0.3), forState: .Highlighted)
}
button.setTitle(item.title, forState: .Normal)
button.addTarget(self, action: "tapButton:", forControlEvents: .TouchUpInside)
button.setTranslatesAutoresizingMaskIntoConstraints(false)
addSubview(button)
buttons.append(button)
if index == 0 {
format += "-[button\(index)]"
} else {
format += "-(\(buttonGap))-[button\(index)(==button0)]"
}
}
format += "-|"
var views = [NSObject: AnyObject]()
for (index, button) in enumerate(buttons) {
let key = "button\(index)"
views[key] = button
}
let hConstraints = NSLayoutConstraint.constraintsWithVisualFormat(format, options: .AlignAllCenterY, metrics: nil, views: views)
NSLayoutConstraint.activateConstraints(hConstraints)
if let firstButton = buttons.first {
let firstButtonTop = NSLayoutConstraint(item: firstButton, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0)
switch arrowDirection {
case .Up:
let firstButtonTop = NSLayoutConstraint(item: firstButton, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: arrowHeight)
let firstButtonBottom = NSLayoutConstraint(item: firstButton, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([firstButtonTop, firstButtonBottom])
case .Down:
let firstButtonTop = NSLayoutConstraint(item: firstButton, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0)
let firstButtonBottom = NSLayoutConstraint(item: firstButton, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: -arrowHeight)
NSLayoutConstraint.activateConstraints([firstButtonTop, firstButtonBottom])
}
}
}
override func drawRect(rect: CGRect) {
UIColor.blackColor().colorWithAlphaComponent(0.6).setFill()
// bubble
var roundedRect = rect
switch arrowDirection {
case .Up:
roundedRect.origin.y += arrowHeight
roundedRect.size.height -= arrowHeight
case .Down:
roundedRect.size.height -= arrowHeight
}
let bubblePath = UIBezierPath(roundedRect: roundedRect, cornerRadius: 5)
bubblePath.fill()
// arrow
switch arrowDirection {
case .Up:
let topX = CGRectGetMidX(rect)
let topY = CGRectGetMinY(rect)
let arrowPath = UIBezierPath()
arrowPath.moveToPoint(CGPointMake(topX, topY))
arrowPath.addLineToPoint(CGPointMake(topX - (arrowHeight - 1), topY + arrowHeight))
arrowPath.addLineToPoint(CGPointMake(topX + (arrowHeight - 1), topY + arrowHeight))
arrowPath.closePath()
arrowPath.fill()
case .Down:
let buttomX = CGRectGetMidX(rect)
let buttomY = CGRectGetMaxY(rect)
let arrowPath = UIBezierPath()
arrowPath.moveToPoint(CGPointMake(buttomX, buttomY))
arrowPath.addLineToPoint(CGPointMake(buttomX - (arrowHeight - 1), buttomY - arrowHeight))
arrowPath.addLineToPoint(CGPointMake(buttomX + (arrowHeight - 1), buttomY - arrowHeight))
arrowPath.closePath()
arrowPath.fill()
}
// lines
UIColor.whiteColor().colorWithAlphaComponent(0.3).setStroke()
for (index, button) in enumerate(buttons) {
if index > 0 {
let line = UIBezierPath()
let x = CGRectGetMinX(button.frame) - buttonGap * 0.5
let y = CGRectGetMaxY(rect)
line.moveToPoint(CGPoint(x: x , y: 0))
line.addLineToPoint(CGPoint(x: x, y: y))
line.lineWidth = 1
line.stroke()
}
}
}
}
@@ -22,6 +22,8 @@ class ChatLeftTextCell: UICollectionViewCell {
@IBOutlet weak var textContentTextViewLeadingConstraint: NSLayoutConstraint!
@IBOutlet weak var textContentTextViewWidthConstraint: NSLayoutConstraint!
var longPressAction: (ChatLeftTextCell -> Void)?
override func awakeFromNib() {
super.awakeFromNib()
@@ -38,15 +40,6 @@ class ChatLeftTextCell: UICollectionViewCell {
NSUnderlineStyleAttributeName: NSNumber(integer: NSUnderlineStyle.StyleSingle.rawValue),
]
// textContentTextView.editable = false
// if let gestureRecognizers = textContentTextView.gestureRecognizers as? [UIGestureRecognizer] {
// for recognizer in gestureRecognizers {
// if recognizer.isKindOfClass(UILongPressGestureRecognizer.self) {
// recognizer.enabled = false
// }
// }
// }
let longPress = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
textContentTextView.addGestureRecognizer(longPress)
@@ -59,13 +52,7 @@ class ChatLeftTextCell: UICollectionViewCell {
func handleLongPress(longPress: UILongPressGestureRecognizer) {
if longPress.state == .Began {
if let view = longPress.view, superview = view.superview {
view.becomeFirstResponder()
let menu = UIMenuController.sharedMenuController()
menu.setTargetRect(view.frame, inView: superview)
menu.setMenuVisible(true, animated: true)
}
longPressAction?(self)
}
}
@@ -22,10 +22,11 @@ class ChatRightTextCell: ChatRightBaseCell {
@IBOutlet weak var textContentTextViewLeadingConstraint: NSLayoutConstraint!
@IBOutlet weak var textContentTextViewWidthConstraint: NSLayoutConstraint!
var deleteMessageAction: (() -> Void)?
typealias MediaTapAction = () -> Void
var mediaTapAction: MediaTapAction?
var longPressAction: (ChatRightTextCell -> Void)?
override func awakeFromNib() {
super.awakeFromNib()
@@ -43,12 +44,6 @@ class ChatRightTextCell: ChatRightBaseCell {
NSUnderlineStyleAttributeName: NSNumber(integer: NSUnderlineStyle.StyleSingle.rawValue),
]
textContentTextView.deleteEnabled = true
textContentTextView.deleteAction = { [weak self] in
println("delete text message?")
self?.deleteMessageAction?()
}
let longPress = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
textContentTextView.addGestureRecognizer(longPress)
@@ -65,13 +60,7 @@ class ChatRightTextCell: ChatRightBaseCell {
func handleLongPress(longPress: UILongPressGestureRecognizer) {
if longPress.state == .Began {
if let view = longPress.view, superview = view.superview {
view.becomeFirstResponder()
let menu = UIMenuController.sharedMenuController()
menu.setTargetRect(view.frame, inView: superview)
menu.setMenuVisible(true, animated: true)
}
longPressAction?(self)
}
}
+2 -21
View File
@@ -10,26 +10,7 @@ import UIKit
class ChatTextView: UITextView {
// override func canBecomeFirstResponder() -> Bool {
// return false
// }
var deleteEnabled = false
var deleteAction: (() -> Void)?
override func copy(sender: AnyObject?) {
UIPasteboard.generalPasteboard().string = text
}
override func delete(sender: AnyObject?) {
deleteAction?()
}
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if deleteEnabled {
return action == Selector("copy:") || action == Selector("delete:")
} else {
return action == Selector("copy:")
}
override func canBecomeFirstResponder() -> Bool {
return false
}
}