mirror of
https://github.com/wahyd4/Yep.git
synced 2026-08-11 05:45:56 +10:00
Merge branch 'feature/new_text_menu' into develop
This commit is contained in:
@@ -96,7 +96,7 @@
|
||||
503A54ED1B328ADB002077BD /* NSLocale+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503A54EC1B328ADB002077BD /* NSLocale+Yep.swift */; };
|
||||
503CAB431AC00CB100DFE830 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503CAB421AC00CB100DFE830 /* ConversationViewController.swift */; };
|
||||
503ED23A1B8ADCE70072187D /* BlackListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503ED2391B8ADCE70072187D /* BlackListViewController.swift */; };
|
||||
5044BF061B5FA74800930ADC /* BubbleMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5044BF051B5FA74800930ADC /* BubbleMenuView.swift */; };
|
||||
503ED23D1B8C12100072187D /* ChatTextContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503ED23C1B8C12100072187D /* ChatTextContainerView.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 */; };
|
||||
@@ -345,7 +345,7 @@
|
||||
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>"; };
|
||||
503ED2391B8ADCE70072187D /* BlackListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BlackListViewController.swift; path = ViewControllers/BlackList/BlackListViewController.swift; sourceTree = "<group>"; };
|
||||
5044BF051B5FA74800930ADC /* BubbleMenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BubbleMenuView.swift; path = Views/BubbleMenu/BubbleMenuView.swift; sourceTree = "<group>"; };
|
||||
503ED23C1B8C12100072187D /* ChatTextContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatTextContainerView.swift; path = Views/ChatTextContainer/ChatTextContainerView.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>"; };
|
||||
@@ -826,6 +826,7 @@
|
||||
504CBA3B1B3D048900663A90 /* TextViews */,
|
||||
50F2B9791B2EEAE300F840CC /* Labels */,
|
||||
50A9D4581ACD3943000B2599 /* AudioWaves */,
|
||||
503ED23E1B8C13280072187D /* ChatTextContainer */,
|
||||
5023DE3A1AB697A700B3EE96 /* Cells */,
|
||||
502AE5221AB71C82005BD199 /* Buttons */,
|
||||
502AE52F1AB8387D005BD199 /* TextFields */,
|
||||
@@ -843,7 +844,6 @@
|
||||
5019F62F1B39BFCB0000D993 /* ConversationMore */,
|
||||
500A0B201B451A7400D49358 /* Badge */,
|
||||
504F85AD1B4D21E400F909BE /* DiscoverFilter */,
|
||||
5044BF071B5FA75000930ADC /* BubbleMenu */,
|
||||
8409137E1B7DB178001C89D6 /* FriendRequest */,
|
||||
);
|
||||
name = Views;
|
||||
@@ -1104,12 +1104,12 @@
|
||||
name = BlackList;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5044BF071B5FA75000930ADC /* BubbleMenu */ = {
|
||||
503ED23E1B8C13280072187D /* ChatTextContainer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5044BF051B5FA74800930ADC /* BubbleMenuView.swift */,
|
||||
503ED23C1B8C12100072187D /* ChatTextContainerView.swift */,
|
||||
);
|
||||
name = BubbleMenu;
|
||||
name = ChatTextContainer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
504CBA3B1B3D048900663A90 /* TextViews */ = {
|
||||
@@ -1925,6 +1925,7 @@
|
||||
508C2AA41AD661B3002B8097 /* UserStateCell.swift in Sources */,
|
||||
0A5D83941AC5D3C8000045BF /* YepTabBarController.swift in Sources */,
|
||||
50E114FC1ABC549300F13000 /* ContactsCell.swift in Sources */,
|
||||
503ED23D1B8C12100072187D /* ChatTextContainerView.swift in Sources */,
|
||||
50F2B9781B2EDF3D00F840CC /* BorderTextField.swift in Sources */,
|
||||
50CDBE461ACA9F2200459CE0 /* ImageCache.swift in Sources */,
|
||||
505EC81C1ADFBE1B001D27E0 /* SkillSelectionCell.swift in Sources */,
|
||||
@@ -1984,7 +1985,6 @@
|
||||
50CDBE491ACBAD3200459CE0 /* ChatLeftImageCell.swift in Sources */,
|
||||
5033159C1AC101C80008A209 /* ChatRightTextCell.swift in Sources */,
|
||||
50E1D7291B870D7500D90A82 /* NSParagraphStyle+Yep.swift in Sources */,
|
||||
5044BF061B5FA74800930ADC /* BubbleMenuView.swift in Sources */,
|
||||
5023DE561AB6CA1000B3EE96 /* AvatarCache.swift in Sources */,
|
||||
5023DE371AB6979600B3EE96 /* ConversationCell.swift in Sources */,
|
||||
502AE5351AB8649F005BD199 /* LoginByMobileViewController.swift in Sources */,
|
||||
|
||||
@@ -84,18 +84,8 @@ class ConversationViewController: BaseViewController {
|
||||
@IBOutlet weak var swipeUpView: UIView!
|
||||
@IBOutlet weak var swipeUpPromptLabel: UILabel!
|
||||
|
||||
lazy var menuDirectionUpThreshold: CGFloat = {
|
||||
return self.topBarsHeight + 60
|
||||
}()
|
||||
var currentMenu: BubbleMenuView?
|
||||
|
||||
var isTryingShowFriendRequestView = false
|
||||
|
||||
func removeOldMenu() {
|
||||
currentMenu?.hide()
|
||||
currentMenu = nil
|
||||
}
|
||||
|
||||
var originalNavigationControllerDelegate: UINavigationControllerDelegate?
|
||||
|
||||
var waverView: YepWaverView!
|
||||
@@ -557,8 +547,6 @@ class ConversationViewController: BaseViewController {
|
||||
|
||||
messageToolbar.stateTransitionAction = { [weak self] (messageToolbar, previousState, currentState) in
|
||||
|
||||
self?.removeOldMenu()
|
||||
|
||||
if let strongSelf = self {
|
||||
|
||||
switch (previousState, currentState) {
|
||||
@@ -751,12 +739,6 @@ class ConversationViewController: BaseViewController {
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
removeOldMenu()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
@@ -1202,8 +1184,6 @@ class ConversationViewController: BaseViewController {
|
||||
|
||||
func moreAction() {
|
||||
|
||||
removeOldMenu()
|
||||
|
||||
messageToolbar.state = .Default
|
||||
|
||||
moreView.showProfileAction = { [weak self] in
|
||||
@@ -1973,33 +1953,6 @@ 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) < strongSelf.menuDirectionUpThreshold ? .Up : .Down
|
||||
|
||||
let menu = BubbleMenuView(arrowDirection: arrowDirection, items: [copyItem])
|
||||
|
||||
strongSelf.currentMenu = menu
|
||||
|
||||
menu.showInView(strongSelf.view, withBubbleFrame: bubbleFrame)
|
||||
}
|
||||
}
|
||||
|
||||
cell.tapAvatarAction = { [weak self] user in
|
||||
self?.performSegueWithIdentifier("showProfile", sender: user)
|
||||
}
|
||||
@@ -2182,109 +2135,77 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
|
||||
}
|
||||
}, collectionView: collectionView, indexPath: indexPath)
|
||||
|
||||
cell.longPressAction = { [weak self] cell in
|
||||
cell.longPressAction = { [weak self] in
|
||||
dispatch_async(dispatch_get_main_queue()) {
|
||||
if let strongSelf = self, realm = message.realm {
|
||||
|
||||
self?.removeOldMenu()
|
||||
var sectionDateMessage: Message?
|
||||
|
||||
if let strongSelf = self {
|
||||
if let currentMessageIndex = strongSelf.messages.indexOf(message) {
|
||||
|
||||
let copyItem = BubbleMenuView.Item(type: .Normal, title: NSLocalizedString("Copy", comment: "")) { menu in
|
||||
print("copy\n")
|
||||
let previousMessageIndex = currentMessageIndex - 1
|
||||
|
||||
UIPasteboard.generalPasteboard().string = cell.textContentTextView.text
|
||||
if let previousMessage = strongSelf.messages[safe: previousMessageIndex] {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
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 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
|
||||
}
|
||||
|
||||
let bubbleFrame = cell.convertRect(cell.bubbleBodyImageView.frame, toView: strongSelf.view)
|
||||
|
||||
let arrowDirection: BubbleMenuView.ArrowDirection = CGRectGetMidY(bubbleFrame) < strongSelf.menuDirectionUpThreshold ? .Up : .Down
|
||||
|
||||
let menu = BubbleMenuView(arrowDirection: arrowDirection, items: [copyItem, deleteItem])
|
||||
|
||||
strongSelf.currentMenu = menu
|
||||
|
||||
menu.showInView(strongSelf.view, withBubbleFrame: bubbleFrame)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2340,8 +2261,6 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
|
||||
func scrollViewDidScroll(scrollView: UIScrollView) {
|
||||
|
||||
pullToRefreshView.scrollViewDidScroll(scrollView)
|
||||
|
||||
removeOldMenu()
|
||||
}
|
||||
|
||||
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
//
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,13 +15,12 @@ class ChatLeftTextCell: ChatBaseCell {
|
||||
@IBOutlet weak var bubbleBodyImageView: UIImageView!
|
||||
@IBOutlet weak var bubbleTailImageView: UIImageView!
|
||||
|
||||
@IBOutlet weak var textContainerView: ChatTextContainerView!
|
||||
@IBOutlet weak var textContentTextView: ChatTextView!
|
||||
@IBOutlet weak var textContentTextViewTrailingConstraint: NSLayoutConstraint!
|
||||
@IBOutlet weak var textContentTextViewLeadingConstraint: NSLayoutConstraint!
|
||||
//@IBOutlet weak var textContentTextViewWidthConstraint: NSLayoutConstraint!
|
||||
|
||||
var longPressAction: (ChatLeftTextCell -> Void)?
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
@@ -38,7 +37,12 @@ class ChatLeftTextCell: ChatBaseCell {
|
||||
]
|
||||
|
||||
let longPress = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
|
||||
textContentTextView.addGestureRecognizer(longPress)
|
||||
textContainerView.addGestureRecognizer(longPress)
|
||||
longPress.delegate = self
|
||||
|
||||
textContainerView.copyTextAction = { [weak self] in
|
||||
UIPasteboard.generalPasteboard().string = self?.textContentTextView.text
|
||||
}
|
||||
|
||||
textContentTextViewTrailingConstraint.constant = YepConfig.chatTextGapBetweenWallAndContentLabel() - YepConfig.ChatCell.magicWidth
|
||||
textContentTextViewLeadingConstraint.constant = YepConfig.chatCellGapBetweenTextContentLabelAndAvatar()
|
||||
@@ -49,7 +53,17 @@ class ChatLeftTextCell: ChatBaseCell {
|
||||
|
||||
func handleLongPress(longPress: UILongPressGestureRecognizer) {
|
||||
if longPress.state == .Began {
|
||||
longPressAction?(self)
|
||||
|
||||
if let view = longPress.view, superview = view.superview {
|
||||
|
||||
view.becomeFirstResponder()
|
||||
|
||||
let menu = UIMenuController.sharedMenuController()
|
||||
let copyItem = UIMenuItem(title: NSLocalizedString("Copy", comment: ""), action:"copyText")
|
||||
menu.menuItems = [copyItem]
|
||||
menu.setTargetRect(view.frame, inView: superview)
|
||||
menu.setMenuVisible(true, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,3 +89,16 @@ class ChatLeftTextCell: ChatBaseCell {
|
||||
}
|
||||
}
|
||||
|
||||
extension ChatLeftTextCell: UIGestureRecognizerDelegate {
|
||||
|
||||
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOfGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,32 +34,44 @@
|
||||
<constraint firstAttribute="width" secondItem="hYu-LI-zcX" secondAttribute="height" multiplier="1:1" id="LI4-Sm-y72"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<textView clipsSubviews="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" text="Text Content" translatesAutoresizingMaskIntoConstraints="NO" id="o2v-vm-ItZ" customClass="ChatTextView" customModule="Yep" customModuleProvider="target">
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ieh-wh-12u" customClass="ChatTextContainerView" customModule="Yep" customModuleProvider="target">
|
||||
<rect key="frame" x="75" y="3" width="92" height="123"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<dataDetectorType key="dataDetectorTypes" phoneNumber="YES" link="YES" address="YES" calendarEvent="YES"/>
|
||||
</textView>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" text="Text Content" translatesAutoresizingMaskIntoConstraints="NO" id="o2v-vm-ItZ" customClass="ChatTextView" customModule="Yep" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="92" height="123"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<dataDetectorType key="dataDetectorTypes" phoneNumber="YES" link="YES" address="YES" calendarEvent="YES"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="o2v-vm-ItZ" secondAttribute="trailing" id="751-18-ycS"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="leading" secondItem="Ieh-wh-12u" secondAttribute="leading" id="LvO-5D-arq"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="top" secondItem="Ieh-wh-12u" secondAttribute="top" id="NiX-G4-RiT"/>
|
||||
<constraint firstAttribute="bottom" secondItem="o2v-vm-ItZ" secondAttribute="bottom" id="RvB-S3-Pgd"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="o2v-vm-ItZ" secondAttribute="bottom" constant="3" id="4HG-w9-FvJ"/>
|
||||
<constraint firstItem="hYu-LI-zcX" firstAttribute="centerY" secondItem="ppr-iG-zxj" secondAttribute="centerY" id="CqX-BA-AO1"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="top" secondItem="Yfx-Em-Job" secondAttribute="top" constant="3" id="MHY-GR-4fS"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="bottom" secondItem="Yfx-Em-Job" secondAttribute="bottom" constant="-3" id="MIb-vK-cmz"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="o2v-vm-ItZ" secondAttribute="trailing" constant="50" id="Tpu-kA-CsQ"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="3" id="aOB-ao-yJA"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Ieh-wh-12u" secondAttribute="trailing" constant="40" id="R7d-pr-61M"/>
|
||||
<constraint firstItem="Ieh-wh-12u" firstAttribute="leading" secondItem="hYu-LI-zcX" secondAttribute="trailing" constant="20" id="SFJ-md-0X6"/>
|
||||
<constraint firstItem="Yfx-Em-Job" firstAttribute="top" secondItem="hYu-LI-zcX" secondAttribute="top" id="cCW-R1-CFn"/>
|
||||
<constraint firstItem="Ieh-wh-12u" firstAttribute="top" secondItem="Yfx-Em-Job" secondAttribute="top" constant="3" id="cHR-67-SWw"/>
|
||||
<constraint firstItem="Ieh-wh-12u" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="3" id="ezx-XB-nhW"/>
|
||||
<constraint firstItem="Ieh-wh-12u" firstAttribute="leading" secondItem="Yfx-Em-Job" secondAttribute="leading" constant="12" id="gj0-ap-nOp"/>
|
||||
<constraint firstItem="hYu-LI-zcX" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="15" id="hM8-00-nUm"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="leading" secondItem="Yfx-Em-Job" secondAttribute="leading" constant="12" id="nPH-jo-SPd"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="trailing" secondItem="Yfx-Em-Job" secondAttribute="trailing" constant="-12" id="ohu-dH-zSM"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="leading" secondItem="ppr-iG-zxj" secondAttribute="trailing" constant="5" id="uyS-e2-1DM"/>
|
||||
<constraint firstItem="o2v-vm-ItZ" firstAttribute="leading" secondItem="hYu-LI-zcX" secondAttribute="trailing" constant="20" id="x4c-mB-0Ui"/>
|
||||
<constraint firstItem="Ieh-wh-12u" firstAttribute="bottom" secondItem="Yfx-Em-Job" secondAttribute="bottom" constant="-3" id="lAw-xK-S4p"/>
|
||||
<constraint firstItem="Ieh-wh-12u" firstAttribute="trailing" secondItem="Yfx-Em-Job" secondAttribute="trailing" constant="-12" id="uOJ-OF-AyG"/>
|
||||
<constraint firstItem="Yfx-Em-Job" firstAttribute="leading" secondItem="ppr-iG-zxj" secondAttribute="trailing" constant="-7" id="vNs-HM-uGF"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Ieh-wh-12u" secondAttribute="bottom" constant="3" id="z32-pR-mtC"/>
|
||||
</constraints>
|
||||
<size key="customSize" width="350" height="129"/>
|
||||
<connections>
|
||||
@@ -68,9 +80,10 @@
|
||||
<outlet property="avatarImageViewWidthConstraint" destination="JV9-zT-1At" id="fe4-74-Gyw"/>
|
||||
<outlet property="bubbleBodyImageView" destination="Yfx-Em-Job" id="bQk-7x-1RF"/>
|
||||
<outlet property="bubbleTailImageView" destination="ppr-iG-zxj" id="kxh-jI-4L8"/>
|
||||
<outlet property="textContainerView" destination="Ieh-wh-12u" id="VVJ-yk-Twz"/>
|
||||
<outlet property="textContentTextView" destination="o2v-vm-ItZ" id="lj4-7V-6TH"/>
|
||||
<outlet property="textContentTextViewLeadingConstraint" destination="x4c-mB-0Ui" id="LPr-oY-JOg"/>
|
||||
<outlet property="textContentTextViewTrailingConstraint" destination="Tpu-kA-CsQ" id="FYH-1w-mbJ"/>
|
||||
<outlet property="textContentTextViewLeadingConstraint" destination="SFJ-md-0X6" id="Icw-33-Beu"/>
|
||||
<outlet property="textContentTextViewTrailingConstraint" destination="R7d-pr-61M" id="GFh-QJ-Fg8"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="338" y="346.5"/>
|
||||
</collectionViewCell>
|
||||
|
||||
@@ -15,6 +15,7 @@ class ChatRightTextCell: ChatRightBaseCell {
|
||||
@IBOutlet weak var bubbleBodyImageView: UIImageView!
|
||||
@IBOutlet weak var bubbleTailImageView: UIImageView!
|
||||
|
||||
@IBOutlet weak var textContainerView: ChatTextContainerView!
|
||||
@IBOutlet weak var textContentTextView: ChatTextView!
|
||||
@IBOutlet weak var textContentTextViewTrailingConstraint: NSLayoutConstraint!
|
||||
@IBOutlet weak var textContentTextViewLeadingConstraint: NSLayoutConstraint!
|
||||
@@ -23,7 +24,7 @@ class ChatRightTextCell: ChatRightBaseCell {
|
||||
typealias MediaTapAction = () -> Void
|
||||
var mediaTapAction: MediaTapAction?
|
||||
|
||||
var longPressAction: (ChatRightTextCell -> Void)?
|
||||
var longPressAction: (() -> Void)?
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
@@ -42,7 +43,16 @@ class ChatRightTextCell: ChatRightBaseCell {
|
||||
]
|
||||
|
||||
let longPress = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
|
||||
textContentTextView.addGestureRecognizer(longPress)
|
||||
textContainerView.addGestureRecognizer(longPress)
|
||||
longPress.delegate = self
|
||||
|
||||
textContainerView.copyTextAction = { [weak self] in
|
||||
UIPasteboard.generalPasteboard().string = self?.textContentTextView.text
|
||||
}
|
||||
|
||||
textContainerView.deleteTextMessageAction = { [weak self] in
|
||||
self?.longPressAction?()
|
||||
}
|
||||
|
||||
textContentTextViewTrailingConstraint.constant = YepConfig.chatCellGapBetweenTextContentLabelAndAvatar()
|
||||
textContentTextViewLeadingConstraint.constant = YepConfig.chatTextGapBetweenWallAndContentLabel() - YepConfig.ChatCell.magicWidth
|
||||
@@ -57,7 +67,18 @@ class ChatRightTextCell: ChatRightBaseCell {
|
||||
|
||||
func handleLongPress(longPress: UILongPressGestureRecognizer) {
|
||||
if longPress.state == .Began {
|
||||
longPressAction?(self)
|
||||
|
||||
if let view = longPress.view, superview = view.superview {
|
||||
|
||||
view.becomeFirstResponder()
|
||||
|
||||
let menu = UIMenuController.sharedMenuController()
|
||||
let copyItem = UIMenuItem(title: NSLocalizedString("Copy", comment: ""), action:"copyText")
|
||||
let deleteItem = UIMenuItem(title: NSLocalizedString("Delete", comment: ""), action:"deleteTextMessage")
|
||||
menu.menuItems = [copyItem, deleteItem]
|
||||
menu.setTargetRect(view.frame, inView: superview)
|
||||
menu.setMenuVisible(true, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,3 +110,17 @@ class ChatRightTextCell: ChatRightBaseCell {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ChatRightTextCell: UIGestureRecognizerDelegate {
|
||||
|
||||
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOfGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,14 +34,6 @@
|
||||
<constraint firstAttribute="width" constant="40" id="hm6-ty-e5H"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<textView clipsSubviews="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" text="Text Content" translatesAutoresizingMaskIntoConstraints="NO" id="em7-jr-W4a" customClass="ChatTextView" customModule="Yep" customModuleProvider="target">
|
||||
<rect key="frame" x="333" y="3" width="92" height="125"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<dataDetectorType key="dataDetectorTypes" phoneNumber="YES" link="YES" address="YES" calendarEvent="YES"/>
|
||||
</textView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_dot_sending" translatesAutoresizingMaskIntoConstraints="NO" id="tB7-GG-3Zd" userLabel="Dot Image View">
|
||||
<rect key="frame" x="310" y="62.5" width="6" height="6"/>
|
||||
<animations/>
|
||||
@@ -50,25 +42,45 @@
|
||||
<constraint firstAttribute="width" constant="6" id="ncl-qf-tmv"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6da-H4-jC2" customClass="ChatTextContainerView" customModule="Yep" customModuleProvider="target">
|
||||
<rect key="frame" x="333" y="3" width="92" height="125"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" text="Text Content" translatesAutoresizingMaskIntoConstraints="NO" id="em7-jr-W4a" customClass="ChatTextView" customModule="Yep" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="92" height="125"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
<dataDetectorType key="dataDetectorTypes" phoneNumber="YES" link="YES" address="YES" calendarEvent="YES"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="em7-jr-W4a" firstAttribute="top" secondItem="6da-H4-jC2" secondAttribute="top" id="Ipq-Cf-Cqw"/>
|
||||
<constraint firstItem="em7-jr-W4a" firstAttribute="leading" secondItem="6da-H4-jC2" secondAttribute="leading" id="K1B-jz-hNU"/>
|
||||
<constraint firstAttribute="trailing" secondItem="em7-jr-W4a" secondAttribute="trailing" id="ZAC-Ol-sRC"/>
|
||||
<constraint firstAttribute="bottom" secondItem="em7-jr-W4a" secondAttribute="bottom" id="pzv-BH-Rot"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<animations/>
|
||||
<constraints>
|
||||
<constraint firstItem="em7-jr-W4a" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="50" id="ABk-EQ-Edt"/>
|
||||
<constraint firstItem="RKK-8d-9vO" firstAttribute="leading" secondItem="em7-jr-W4a" secondAttribute="trailing" constant="20" id="Dsx-Jg-HtG"/>
|
||||
<constraint firstItem="em7-jr-W4a" firstAttribute="top" secondItem="73l-kE-1KG" secondAttribute="top" constant="3" id="Nzn-Kc-TWM"/>
|
||||
<constraint firstItem="em7-jr-W4a" firstAttribute="bottom" secondItem="73l-kE-1KG" secondAttribute="bottom" constant="-3" id="Pz2-Yd-kF9"/>
|
||||
<constraint firstAttribute="bottom" secondItem="6da-H4-jC2" secondAttribute="bottom" constant="3" id="0MI-n7-CQK"/>
|
||||
<constraint firstItem="6da-H4-jC2" firstAttribute="bottom" secondItem="73l-kE-1KG" secondAttribute="bottom" constant="-3" id="8U8-ia-5w1"/>
|
||||
<constraint firstItem="RKK-8d-9vO" firstAttribute="leading" secondItem="6da-H4-jC2" secondAttribute="trailing" constant="20" id="ET9-4m-yXe"/>
|
||||
<constraint firstItem="1JC-jy-KNh" firstAttribute="leading" secondItem="73l-kE-1KG" secondAttribute="trailing" constant="-7" id="Ewb-Xc-fpj"/>
|
||||
<constraint firstItem="6da-H4-jC2" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="40" id="IFK-hl-cQK"/>
|
||||
<constraint firstItem="6da-H4-jC2" firstAttribute="leading" secondItem="73l-kE-1KG" secondAttribute="leading" constant="12" id="OBi-9l-myf"/>
|
||||
<constraint firstItem="73l-kE-1KG" firstAttribute="top" secondItem="RKK-8d-9vO" secondAttribute="top" id="QzP-5b-ovG"/>
|
||||
<constraint firstItem="73l-kE-1KG" firstAttribute="leading" secondItem="tB7-GG-3Zd" secondAttribute="trailing" constant="5" id="UIT-W7-WO6"/>
|
||||
<constraint firstItem="1JC-jy-KNh" firstAttribute="centerY" secondItem="RKK-8d-9vO" secondAttribute="centerY" id="Upo-MS-aQJ"/>
|
||||
<constraint firstItem="em7-jr-W4a" firstAttribute="leading" secondItem="73l-kE-1KG" secondAttribute="leading" constant="12" id="WkS-Oi-AjM"/>
|
||||
<constraint firstAttribute="bottom" secondItem="em7-jr-W4a" secondAttribute="bottom" constant="3" id="dXH-j4-Dn8"/>
|
||||
<constraint firstItem="6da-H4-jC2" firstAttribute="trailing" secondItem="73l-kE-1KG" secondAttribute="trailing" constant="-12" id="dMB-tv-isj"/>
|
||||
<constraint firstItem="6da-H4-jC2" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="3" id="gu0-N0-n98"/>
|
||||
<constraint firstItem="tB7-GG-3Zd" firstAttribute="centerY" secondItem="73l-kE-1KG" secondAttribute="centerY" id="h7l-0p-SWm"/>
|
||||
<constraint firstItem="1JC-jy-KNh" firstAttribute="leading" secondItem="em7-jr-W4a" secondAttribute="trailing" constant="5" id="iAD-hS-cmr"/>
|
||||
<constraint firstItem="73l-kE-1KG" firstAttribute="trailing" secondItem="em7-jr-W4a" secondAttribute="trailing" constant="12" id="sFr-bX-lfC"/>
|
||||
<constraint firstItem="em7-jr-W4a" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="3" id="vdd-9g-iCq"/>
|
||||
<constraint firstItem="6da-H4-jC2" firstAttribute="top" secondItem="73l-kE-1KG" secondAttribute="top" constant="3" id="p2y-Oi-B1W"/>
|
||||
<constraint firstAttribute="trailing" secondItem="RKK-8d-9vO" secondAttribute="trailing" constant="15" id="vs0-4M-TaV"/>
|
||||
</constraints>
|
||||
<size key="customSize" width="417" height="131"/>
|
||||
@@ -80,9 +92,10 @@
|
||||
<outlet property="bubbleTailImageView" destination="1JC-jy-KNh" id="MGo-KB-Kb3"/>
|
||||
<outlet property="dotImageView" destination="tB7-GG-3Zd" id="odg-hQ-nba"/>
|
||||
<outlet property="gapBetweenDotImageViewAndBubbleConstraint" destination="UIT-W7-WO6" id="3P5-Dl-2GX"/>
|
||||
<outlet property="textContainerView" destination="6da-H4-jC2" id="jNs-2X-eia"/>
|
||||
<outlet property="textContentTextView" destination="em7-jr-W4a" id="Pch-yz-67H"/>
|
||||
<outlet property="textContentTextViewLeadingConstraint" destination="ABk-EQ-Edt" id="ZFU-Y1-eXX"/>
|
||||
<outlet property="textContentTextViewTrailingConstraint" destination="Dsx-Jg-HtG" id="YPC-jA-wZK"/>
|
||||
<outlet property="textContentTextViewLeadingConstraint" destination="IFK-hl-cQK" id="2gq-ft-jvH"/>
|
||||
<outlet property="textContentTextViewTrailingConstraint" destination="ET9-4m-yXe" id="aCa-1k-pBI"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="339" y="326.5"/>
|
||||
</collectionViewCell>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// ChatTextContainerView.swift
|
||||
// Yep
|
||||
//
|
||||
// Created by nixzhu on 15/8/25.
|
||||
// Copyright (c) 2015年 Catch Inc. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ChatTextContainerView: UIView {
|
||||
|
||||
override func canBecomeFirstResponder() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
|
||||
if action == "copyText" {
|
||||
return true
|
||||
} else if action == "deleteTextMessage" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var copyTextAction: (() -> Void)?
|
||||
var deleteTextMessageAction: (() -> Void)?
|
||||
|
||||
func copyText() {
|
||||
println("copyText")
|
||||
copyTextAction?()
|
||||
}
|
||||
|
||||
func deleteTextMessage() {
|
||||
println("deleteTextMessage")
|
||||
deleteTextMessageAction?()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user