mirror of
https://github.com/wahyd4/Yep.git
synced 2026-08-11 22:06:40 +10:00
Merge branch 'develop' of https://github.com/CatchChat/Yep into develop
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
@@ -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 = ""
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user