Merge branch 'develop' of https://github.com/CatchChat/Yep into develop

This commit is contained in:
kevinzhow
2015-07-24 13:33:16 +08:00
23 changed files with 689 additions and 173 deletions
+16 -11
View File
@@ -2,14 +2,19 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'Ruler'
pod 'Proposer'
pod 'APAddressBook/Swift'
pod 'FXBlurView'
pod 'Kingfisher'
pod 'TPKeyboardAvoiding', '~> 1.2'
#pod 'WebViewJavascriptBridge'
pod 'pop'
pod 'AFNetworking'
pod 'MZFayeClient', :path => '../CatchLib-iOS/MZFayeClient/'
pod 'RealmSwift'
def pods
pod 'Ruler'
pod 'Proposer'
pod 'APAddressBook/Swift'
pod 'FXBlurView'
pod 'Kingfisher'
pod 'TPKeyboardAvoiding', '~> 1.2'
pod 'pop'
pod 'AFNetworking'
pod 'MZFayeClient', :path => '../CatchLib-iOS/MZFayeClient/'
pod 'RealmSwift'
end
target 'Yep' do
pods
end
+24 -4
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 */; };
@@ -210,6 +211,7 @@
50EB8CCE1AEA1B38001AC1EE /* MessageMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8CCD1AEA1B38001AC1EE /* MessageMediaViewController.swift */; };
50F2B9781B2EDF3D00F840CC /* BorderTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F2B9771B2EDF3D00F840CC /* BorderTextField.swift */; };
50F2B97B1B2EEAF800F840CC /* NavigationTitleLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F2B97A1B2EEAF800F840CC /* NavigationTitleLabel.swift */; };
B00500D7E943DDD27CF9C562 /* Pods_Yep.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3146F0F60F2A38BC6E0FA66 /* Pods_Yep.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -271,6 +273,7 @@
0AEB1CC31B0E6A4B00178C9C /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = "<group>"; };
0AEB1CC51B0E742400178C9C /* Double+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Double+Yep.swift"; path = "Extensions/Double+Yep.swift"; sourceTree = "<group>"; };
2268251A75FF6766DC31CCFB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
4EEAF88E6C6D61E595249DF5 /* Pods-Yep.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Yep.release.xcconfig"; path = "Pods/Target Support Files/Pods-Yep/Pods-Yep.release.xcconfig"; sourceTree = "<group>"; };
500A0B1B1B450C2100D49358 /* EditNicknameAndBadgeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EditNicknameAndBadgeViewController.swift; path = ViewControllers/EditNicknameAndBadge/EditNicknameAndBadgeViewController.swift; sourceTree = "<group>"; };
500A0B1E1B451A6F00D49358 /* BadgeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BadgeView.swift; path = Views/Badge/BadgeView.swift; sourceTree = "<group>"; };
501560D61B427FD6000933F0 /* Results+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Results+Yep.swift"; path = "Extensions/Results+Yep.swift"; sourceTree = "<group>"; };
@@ -321,6 +324,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>"; };
@@ -434,7 +438,9 @@
50EB8CCD1AEA1B38001AC1EE /* MessageMediaViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageMediaViewController.swift; path = ViewControllers/MessageMedia/MessageMediaViewController.swift; sourceTree = "<group>"; };
50F2B9771B2EDF3D00F840CC /* BorderTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BorderTextField.swift; path = Views/TextFields/BorderTextField.swift; sourceTree = "<group>"; };
50F2B97A1B2EEAF800F840CC /* NavigationTitleLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NavigationTitleLabel.swift; path = Views/Labels/NavigationTitleLabel.swift; sourceTree = "<group>"; };
88C96122C478C4D134F634F6 /* Pods-Yep.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Yep.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Yep/Pods-Yep.debug.xcconfig"; sourceTree = "<group>"; };
B33BE6A6016453F6673C8317 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
C3146F0F60F2A38BC6E0FA66 /* Pods_Yep.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Yep.framework; sourceTree = BUILT_PRODUCTS_DIR; };
FD88F6095DB46DFAD41AB5DB /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -458,6 +464,7 @@
0AEB1CC21B0E69B500178C9C /* Fabric.framework in Frameworks */,
0AD7EA5E1AC3ED1200617758 /* CFNetwork.framework in Frameworks */,
15606C18E1487982869C0A42 /* Pods.framework in Frameworks */,
B00500D7E943DDD27CF9C562 /* Pods_Yep.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -782,6 +789,7 @@
5019F62F1B39BFCB0000D993 /* ConversationMore */,
500A0B201B451A7400D49358 /* Badge */,
504F85AD1B4D21E400F909BE /* DiscoverFilter */,
5044BF071B5FA75000930ADC /* BubbleMenu */,
);
name = Views;
sourceTree = "<group>";
@@ -1030,6 +1038,14 @@
name = Conversation;
sourceTree = "<group>";
};
5044BF071B5FA75000930ADC /* BubbleMenu */ = {
isa = PBXGroup;
children = (
5044BF051B5FA74800930ADC /* BubbleMenuView.swift */,
);
name = BubbleMenu;
sourceTree = "<group>";
};
504CBA3B1B3D048900663A90 /* TextViews */ = {
isa = PBXGroup;
children = (
@@ -1455,6 +1471,8 @@
children = (
2268251A75FF6766DC31CCFB /* Pods.debug.xcconfig */,
B33BE6A6016453F6673C8317 /* Pods.release.xcconfig */,
88C96122C478C4D134F634F6 /* Pods-Yep.debug.xcconfig */,
4EEAF88E6C6D61E595249DF5 /* Pods-Yep.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
@@ -1478,6 +1496,7 @@
0AD7EA5B1AC3ECD700617758 /* libPushSDK-1.8.3.a */,
0AD7EA511AC3EA6F00617758 /* JPushSDK.framework */,
FD88F6095DB46DFAD41AB5DB /* Pods.framework */,
C3146F0F60F2A38BC6E0FA66 /* Pods_Yep.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -1657,7 +1676,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Yep/Pods-Yep-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
67B15DACB809FA624B07B937 /* Copy Pods Resources */ = {
@@ -1672,7 +1691,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Yep/Pods-Yep-resources.sh\"\n";
showEnvVarsInLog = 0;
};
6F60E0EF8ADE99106F3718B9 /* Check Pods Manifest.lock */ = {
@@ -1802,6 +1821,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 */,
@@ -1971,7 +1991,7 @@
};
0A8F54CB1AB67D11004AD60E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 2268251A75FF6766DC31CCFB /* Pods.debug.xcconfig */;
baseConfigurationReference = 88C96122C478C4D134F634F6 /* Pods-Yep.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
@@ -1998,7 +2018,7 @@
};
0A8F54CC1AB67D11004AD60E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B33BE6A6016453F6673C8317 /* Pods.release.xcconfig */;
baseConfigurationReference = 4EEAF88E6C6D61E595249DF5 /* Pods-Yep.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
+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
+24
View File
@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "gradient_art.pdf",
"resizing" : {
"mode" : "3-part-horizontal",
"center" : {
"mode" : "fill",
"width" : 1
},
"capInsets" : {
"right" : 187,
"left" : 187
}
}
}
],
"info" : {
"version" : 1,
"author" : "xcode",
"template-rendering-intent" : "original"
}
}
Binary file not shown.
@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "gradient_life.pdf",
"resizing" : {
"mode" : "3-part-horizontal",
"center" : {
"mode" : "fill",
"width" : 1
},
"capInsets" : {
"right" : 187,
"left" : 187
}
}
}
],
"info" : {
"version" : 1,
"author" : "xcode",
"template-rendering-intent" : "original"
}
}
Binary file not shown.
@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "gradient_sport.pdf",
"resizing" : {
"mode" : "3-part-horizontal",
"center" : {
"mode" : "fill",
"width" : 1
},
"capInsets" : {
"right" : 187,
"left" : 187
}
}
}
],
"info" : {
"version" : 1,
"author" : "xcode",
"template-rendering-intent" : "original"
}
}
Binary file not shown.
@@ -0,0 +1,24 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "gradient_tech.pdf",
"resizing" : {
"mode" : "3-part-horizontal",
"center" : {
"mode" : "fill",
"width" : 1
},
"capInsets" : {
"right" : 187,
"left" : 187
}
}
}
],
"info" : {
"version" : 1,
"author" : "xcode",
"template-rendering-intent" : "original"
}
}
Binary file not shown.
+7
View File
@@ -48,6 +48,13 @@ class UserSkill: Object {
dynamic var category: UserSkillCategory?
var skillCategory: SkillCell.Skill.Category? {
if let category = category {
return SkillCell.Skill.Category(rawValue: category.name)
}
return nil
}
dynamic var skillID: String = ""
dynamic var name: String = ""
dynamic var localName: String = ""
+7
View File
@@ -157,6 +157,13 @@ struct Skill: Hashable {
let category: SkillCategory?
var skillCategory: SkillCell.Skill.Category? {
if let category = category {
return SkillCell.Skill.Category(rawValue: category.name)
}
return nil
}
let id: String
let name: String
let localName: String
@@ -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>) {
@@ -433,9 +433,7 @@ class ProfileViewController: UIViewController {
vc.preferedState = SkillHomeState(rawValue: preferedState)
}
vc.skillID = skillInfo["skillID"] as? String
vc.skillLocalName = skillInfo["skillLocalName"] as? String
vc.skillCoverURLString = skillInfo["skillCoverURLString"] as? String
vc.skill = skillInfo["skill"] as? SkillCell.Skill
vc.afterUpdatedSkillCoverAction = { [weak self] in
self?.updateProfileCollectionView()
@@ -828,20 +826,16 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel
switch profileUser {
case .DiscoveredUserType(let discoveredUser):
let skill = discoveredUser.masterSkills[indexPath.item]
cell.skillID = skill.id
cell.skillLocalName = skill.localName
cell.skillCoverURLString = skill.coverURLString
cell.skill = SkillCell.Skill(ID: skill.id, localName: skill.localName, coverURLString: skill.coverURLString, category: skill.skillCategory)
case .UserType(let user):
let userSkill = user.masterSkills[indexPath.item]
cell.skillID = userSkill.skillID
cell.skillLocalName = userSkill.localName
cell.skillCoverURLString = userSkill.coverURLString
cell.skill = SkillCell.Skill(ID: userSkill.skillID, localName: userSkill.localName, coverURLString: userSkill.coverURLString, category: userSkill.skillCategory)
}
}
cell.tapAction = { [weak self] skillID, skillLocalName, skillCoverURLString in
self?.performSegueWithIdentifier("showSkillHome", sender: ["preferedState": SkillHomeState.Master.rawValue, "skillID": skillID, "skillLocalName": skillLocalName, "skillCoverURLString": skillCoverURLString])
cell.tapAction = { [weak self] skill in
self?.performSegueWithIdentifier("showSkillHome", sender: ["skill": skill, "preferedState": SkillHomeState.Master.rawValue])
}
return cell
@@ -853,20 +847,16 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel
switch profileUser {
case .DiscoveredUserType(let discoveredUser):
let skill = discoveredUser.learningSkills[indexPath.item]
cell.skillID = skill.id
cell.skillLocalName = skill.localName
cell.skillCoverURLString = skill.coverURLString
cell.skill = SkillCell.Skill(ID: skill.id, localName: skill.localName, coverURLString: skill.coverURLString, category: skill.skillCategory)
case .UserType(let user):
let userSkill = user.learningSkills[indexPath.item]
cell.skillID = userSkill.skillID
cell.skillLocalName = userSkill.localName
cell.skillCoverURLString = userSkill.coverURLString
cell.skill = SkillCell.Skill(ID: userSkill.skillID, localName: userSkill.localName, coverURLString: userSkill.coverURLString, category: userSkill.skillCategory)
}
}
cell.tapAction = { [weak self] skillID, skillLocalName, skillCoverURLString in
self?.performSegueWithIdentifier("showSkillHome", sender: ["preferedState": SkillHomeState.Learning.rawValue, "skillID": skillID, "skillLocalName": skillLocalName, "skillCoverURLString": skillCoverURLString])
cell.tapAction = { [weak self] skill in
self?.performSegueWithIdentifier("showSkillHome", sender: ["skill": skill, "preferedState": SkillHomeState.Learning.rawValue])
}
return cell
@@ -31,19 +31,16 @@ class SkillHomeViewController: CustomNavigationBarViewController {
return tempTableView;
}()
var skillID: String?
var skillLocalName: String? {
var skill: SkillCell.Skill? {
willSet {
title = newValue
title = newValue?.localName
skillCoverURLString = newValue?.coverURLString
}
}
var skillCoverURLString: String? {
willSet {
if let coverURLString = newValue {
headerView?.skillCoverURLString = coverURLString
}
headerView?.skillCoverURLString = newValue
}
}
@@ -102,6 +99,9 @@ class SkillHomeViewController: CustomNavigationBarViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let skillCategory = skill?.category {
headerView.skillCategory = skillCategory
}
headerView.skillCoverURLString = skillCoverURLString
masterTableView.separatorColor = UIColor.yepCellSeparatorColor()
@@ -124,7 +124,7 @@ class SkillHomeViewController: CustomNavigationBarViewController {
learningtTableView.delegate = self
learningtTableView.tag = SkillHomeState.Learning.hashValue
if let skillID = skillID {
if let skillID = skill?.ID {
discoverUserBySkillID(skillID)
}
@@ -242,7 +242,7 @@ class SkillHomeViewController: CustomNavigationBarViewController {
NSFontAttributeName: UIFont.skillHomeTextLargeFont()
]
let titleAttr = NSMutableAttributedString(string: skillLocalName ?? "", attributes:textAttributes)
let titleAttr = NSMutableAttributedString(string: skill?.localName ?? "", attributes:textAttributes)
titleLabel.attributedText = titleAttr
titleLabel.textAlignment = NSTextAlignment.Center
@@ -369,7 +369,7 @@ extension SkillHomeViewController: UIImagePickerControllerDelegate, UINavigation
let data = UIImageJPEGRepresentation(fixedImage, 0.7)
if let skillID = skillID {
if let skillID = skill?.ID {
YepHUD.showActivityIndicator()
+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)
}
}
+45 -9
View File
@@ -31,15 +31,47 @@ class SkillCell: UICollectionViewCell {
}
}
var skillID: String?
var skillLocalName: String? {
willSet {
skillLabel.text = newValue
class Skill: NSObject {
let ID: String
let localName: String
let coverURLString: String?
enum Category: String {
case Art = "Art"
case Technology = "Technology"
case Sport = "Sport"
case LifeStyle = "Life Style"
var gradientImage: UIImage? {
switch self {
case .Art:
return UIImage(named: "gradient_art")
case .Technology:
return UIImage(named: "gradient_tech")
case .Sport:
return UIImage(named: "gradient_sport")
case .LifeStyle:
return UIImage(named: "gradient_life")
}
}
}
let category: Category
init(ID: String, localName: String, coverURLString: String?, category: Category?) {
self.ID = ID
self.localName = localName
self.coverURLString = coverURLString
self.category = category ?? .Art
}
}
var skillCoverURLString: String?
var tapAction: ((skillID: String, skillLocalName: String, skillCoverURLString: String) -> Void)?
var skill: Skill? {
willSet {
skillLabel.text = newValue?.localName
}
}
var tapAction: ((skill: Skill) -> Void)?
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
tapped = true
@@ -48,10 +80,14 @@ class SkillCell: UICollectionViewCell {
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
delay(0.15) { [weak self] in
self?.tapped = false
if let skillID = self?.skillID, skillLocalName = self?.skillLocalName, skillCoverURLString = self?.skillCoverURLString {
self?.tapAction?(skillID: skillID, skillLocalName: skillLocalName, skillCoverURLString: skillCoverURLString)
if let strongSelf = self {
strongSelf.tapped = false
if let skill = strongSelf.skill {
strongSelf.tapAction?(skill: skill)
}
}
}
}
@@ -11,10 +11,14 @@ import Kingfisher
class SkillHomeHeaderView: UIView {
var skillCategory: SkillCell.Skill.Category = .Art
var skillCoverURLString: String? {
willSet {
if let coverURLString = newValue, URL = NSURL(string: coverURLString) {
headerImageView.kf_setImageWithURL(URL, placeholderImage: UIImage(named: "Cover3"))
headerImageView.kf_setImageWithURL(URL, placeholderImage: skillCategory.gradientImage)
} else {
headerImageView.image = skillCategory.gradientImage
}
}
}
+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
}
}