Merge branch 'feature/better_message_preview' into develop

This commit is contained in:
nixzhu
2015-05-07 15:21:31 +08:00
9 changed files with 548 additions and 22 deletions
+8
View File
@@ -103,6 +103,7 @@
506E241E1AE6391A00A33C1A /* TouchZoomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506E241D1AE6391A00A33C1A /* TouchZoomButton.swift */; };
506EC20A1AC95BFC00CE42B3 /* UIFont+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506EC2091AC95BFC00CE42B3 /* UIFont+Yep.swift */; };
5076C56A1AC46B9F00B22952 /* FayeService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5076C5691AC46B9F00B22952 /* FayeService.swift */; };
5076FC5A1AF9DF6B00D7381A /* ConversationMessagePreviewTransitionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5076FC591AF9DF6B00D7381A /* ConversationMessagePreviewTransitionManager.swift */; };
50894CF11AEA4726000981AF /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50894CF01AEA4726000981AF /* SettingsViewController.swift */; };
50894CF51AEA4A01000981AF /* SettingsUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50894CF31AEA4A01000981AF /* SettingsUserCell.swift */; };
50894CF61AEA4A01000981AF /* SettingsUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50894CF41AEA4A01000981AF /* SettingsUserCell.xib */; };
@@ -143,6 +144,7 @@
50CDBE461ACA9F2200459CE0 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CDBE451ACA9F2200459CE0 /* ImageCache.swift */; };
50CDBE491ACBAD3200459CE0 /* ChatLeftImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CDBE471ACBAD3200459CE0 /* ChatLeftImageCell.swift */; };
50CDBE4A1ACBAD3200459CE0 /* ChatLeftImageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50CDBE481ACBAD3200459CE0 /* ChatLeftImageCell.xib */; };
50D4D0DB1AFA825B005AEB6F /* MediaControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D4D0DA1AFA825B005AEB6F /* MediaControlView.swift */; };
50E114F41ABBF9CC00F13000 /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E114F31ABBF9CC00F13000 /* Models.swift */; };
50E114F71ABBFA7000F13000 /* RLMSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E114F61ABBFA7000F13000 /* RLMSupport.swift */; };
50E114F91ABC137A00F13000 /* YepServiceSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E114F81ABC137A00F13000 /* YepServiceSync.swift */; };
@@ -273,6 +275,7 @@
506E241D1AE6391A00A33C1A /* TouchZoomButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TouchZoomButton.swift; path = Views/Buttons/TouchZoomButton.swift; sourceTree = "<group>"; };
506EC2091AC95BFC00CE42B3 /* UIFont+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIFont+Yep.swift"; path = "Extensions/UIFont+Yep.swift"; sourceTree = "<group>"; };
5076C5691AC46B9F00B22952 /* FayeService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FayeService.swift; path = Services/FayeService.swift; sourceTree = "<group>"; };
5076FC591AF9DF6B00D7381A /* ConversationMessagePreviewTransitionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationMessagePreviewTransitionManager.swift; path = ViewControllers/Conversation/ConversationMessagePreviewTransitionManager.swift; sourceTree = "<group>"; };
50894CF01AEA4726000981AF /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SettingsViewController.swift; path = ViewControllers/Settings/SettingsViewController.swift; sourceTree = "<group>"; };
50894CF31AEA4A01000981AF /* SettingsUserCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SettingsUserCell.swift; path = Views/Cells/SettingsUser/SettingsUserCell.swift; sourceTree = "<group>"; };
50894CF41AEA4A01000981AF /* SettingsUserCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SettingsUserCell.xib; path = Views/Cells/SettingsUser/SettingsUserCell.xib; sourceTree = "<group>"; };
@@ -313,6 +316,7 @@
50CDBE451ACA9F2200459CE0 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Caches/ImageCache.swift; sourceTree = "<group>"; };
50CDBE471ACBAD3200459CE0 /* ChatLeftImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftImageCell.swift; path = Views/Cells/ChatLeftImage/ChatLeftImageCell.swift; sourceTree = "<group>"; };
50CDBE481ACBAD3200459CE0 /* ChatLeftImageCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftImageCell.xib; path = Views/Cells/ChatLeftImage/ChatLeftImageCell.xib; sourceTree = "<group>"; };
50D4D0DA1AFA825B005AEB6F /* MediaControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaControlView.swift; path = Views/Media/MediaControlView.swift; sourceTree = "<group>"; };
50E114F31ABBF9CC00F13000 /* Models.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Models.swift; path = Realm/Models.swift; sourceTree = "<group>"; };
50E114F61ABBFA7000F13000 /* RLMSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RLMSupport.swift; path = Realm/RLMSupport.swift; sourceTree = "<group>"; };
50E114F81ABC137A00F13000 /* YepServiceSync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepServiceSync.swift; path = Services/YepServiceSync.swift; sourceTree = "<group>"; };
@@ -769,6 +773,7 @@
children = (
503CAB421AC00CB100DFE830 /* ConversationViewController.swift */,
50165C441AC2860900C7AEBE /* ConversationLayout.swift */,
5076FC591AF9DF6B00D7381A /* ConversationMessagePreviewTransitionManager.swift */,
);
name = Conversation;
sourceTree = "<group>";
@@ -1060,6 +1065,7 @@
isa = PBXGroup;
children = (
50EB8CCA1AEA1AC0001AC1EE /* MediaView.swift */,
50D4D0DA1AFA825B005AEB6F /* MediaControlView.swift */,
);
name = Media;
sourceTree = "<group>";
@@ -1327,6 +1333,7 @@
508D174C1ACCF49B0092D666 /* ChatRightAudioCell.swift in Sources */,
502AE53D1AB87C0D005BD199 /* YepUserDefaults.swift in Sources */,
50EB8CC41AE89F12001AC1EE /* ChatRightVideoCell.swift in Sources */,
50D4D0DB1AFA825B005AEB6F /* MediaControlView.swift in Sources */,
508D17471ACCF4710092D666 /* ChatLeftAudioCell.swift in Sources */,
50894D021AEA6433000981AF /* EditProfileLessInfoCell.swift in Sources */,
50894CFA1AEA54BA000981AF /* SettingsMoreCell.swift in Sources */,
@@ -1362,6 +1369,7 @@
506EC20A1AC95BFC00CE42B3 /* UIFont+Yep.swift in Sources */,
0A0A11821AE389380008772C /* RegisterSkillsLayout.swift in Sources */,
50C0909B1ADE234F00CC6389 /* RegisterPickSkillsViewController.swift in Sources */,
5076FC5A1AF9DF6B00D7381A /* ConversationMessagePreviewTransitionManager.swift in Sources */,
508D16BD1ADCAF0400270E3F /* PullToRefreshView.swift in Sources */,
508D17441ACCDC9F0092D666 /* VoiceRecordButton.swift in Sources */,
502AE5271AB72D06005BD199 /* YepNetworking.swift in Sources */,
+23 -11
View File
@@ -71,15 +71,15 @@
<outlet property="delegate" destination="la3-6z-2aH" id="zOV-zM-2KH"/>
</connections>
</collectionView>
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Q0w-sM-iIR" customClass="MessageToolbar" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="250" width="600" height="50"/>
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Q0w-sM-iIR" customClass="MessageToolbar" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="396" width="600" height="44"/>
<items/>
</toolbar>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="g11-Ob-rEy" userLabel="More Message Types View">
<rect key="frame" x="0.0" y="300" width="600" height="300"/>
<rect key="frame" x="0.0" y="440" width="600" height="300"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="avI-im-eEZ" userLabel="Choose Photo Button" customClass="MessageTypeButton" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="20" y="20" width="173" height="120"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="avI-im-eEZ" userLabel="Choose Photo Button" customClass="MessageTypeButton" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="20" y="20" width="173" height="260"/>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
@@ -88,8 +88,8 @@
<userDefinedRuntimeAttribute type="string" keyPath="title" value="Choose photo"/>
</userDefinedRuntimeAttributes>
</button>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HcA-7v-16m" userLabel="Take Photo Button" customClass="MessageTypeButton" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="213" y="20" width="174" height="120"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="HcA-7v-16m" userLabel="Take Photo Button" customClass="MessageTypeButton" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="213" y="20" width="174" height="260"/>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
@@ -98,8 +98,8 @@
<userDefinedRuntimeAttribute type="string" keyPath="title" value="Take photo"/>
</userDefinedRuntimeAttributes>
</button>
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hg3-Ss-sqD" userLabel="Add Location Button" customClass="MessageTypeButton" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="407" y="20" width="173" height="120"/>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hg3-Ss-sqD" userLabel="Add Location Button" customClass="MessageTypeButton" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="407" y="20" width="173" height="260"/>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
@@ -120,6 +120,7 @@
<constraint firstItem="avI-im-eEZ" firstAttribute="height" secondItem="HcA-7v-16m" secondAttribute="height" id="apZ-ns-N9S"/>
<constraint firstItem="avI-im-eEZ" firstAttribute="top" secondItem="g11-Ob-rEy" secondAttribute="top" constant="20" id="b4S-GW-ghP"/>
<constraint firstAttribute="height" constant="300" id="faf-Xw-b0H"/>
<constraint firstAttribute="bottom" secondItem="avI-im-eEZ" secondAttribute="bottom" constant="20" id="sv1-0R-BYO"/>
<constraint firstItem="Hg3-Ss-sqD" firstAttribute="leading" secondItem="HcA-7v-16m" secondAttribute="trailing" constant="20" id="uSu-cr-saV"/>
<constraint firstItem="avI-im-eEZ" firstAttribute="leading" secondItem="g11-Ob-rEy" secondAttribute="leading" constant="20" id="uke-uY-P5h"/>
<constraint firstItem="Hg3-Ss-sqD" firstAttribute="height" secondItem="HcA-7v-16m" secondAttribute="height" id="xWM-E0-CPp"/>
@@ -133,7 +134,7 @@
<constraint firstItem="g11-Ob-rEy" firstAttribute="leading" secondItem="P6X-OT-hT5" secondAttribute="leading" id="B04-LV-qWQ"/>
<constraint firstItem="Q0w-sM-iIR" firstAttribute="leading" secondItem="P6X-OT-hT5" secondAttribute="leading" id="BYY-lE-CF8"/>
<constraint firstItem="o5R-dM-KPW" firstAttribute="leading" secondItem="P6X-OT-hT5" secondAttribute="leading" id="BjN-8c-WjM"/>
<constraint firstAttribute="bottom" secondItem="Q0w-sM-iIR" secondAttribute="bottom" constant="300" id="Sf8-1u-t01"/>
<constraint firstAttribute="bottom" secondItem="Q0w-sM-iIR" secondAttribute="bottom" constant="160" id="Sf8-1u-t01"/>
<constraint firstItem="g11-Ob-rEy" firstAttribute="top" secondItem="Q0w-sM-iIR" secondAttribute="bottom" id="UC0-RV-osX"/>
<constraint firstAttribute="trailing" secondItem="g11-Ob-rEy" secondAttribute="trailing" id="cB1-tg-2P7"/>
<constraint firstItem="o5R-dM-KPW" firstAttribute="top" secondItem="P6X-OT-hT5" secondAttribute="top" id="d3q-cT-PCM"/>
@@ -243,12 +244,22 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2Cb-zQ-dUZ" customClass="MediaControlView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="540" width="600" height="60"/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="2CO-oT-53G"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<gestureRecognizers/>
<constraints>
<constraint firstItem="eW6-5y-wrX" firstAttribute="top" secondItem="k4j-Ub-3bn" secondAttribute="bottom" id="SD4-Rb-R2n"/>
<constraint firstItem="k4j-Ub-3bn" firstAttribute="top" secondItem="aNq-7S-JK2" secondAttribute="top" id="Zbg-wi-X7W"/>
<constraint firstAttribute="trailing" secondItem="2Cb-zQ-dUZ" secondAttribute="trailing" id="gVX-Fl-qnR"/>
<constraint firstItem="2Cb-zQ-dUZ" firstAttribute="leading" secondItem="aNq-7S-JK2" secondAttribute="leading" id="lDq-Nl-fPd"/>
<constraint firstItem="eW6-5y-wrX" firstAttribute="top" secondItem="2Cb-zQ-dUZ" secondAttribute="bottom" id="o3u-xh-Qwa"/>
<constraint firstItem="k4j-Ub-3bn" firstAttribute="leading" secondItem="aNq-7S-JK2" secondAttribute="leading" id="xJR-ze-vBa"/>
<constraint firstAttribute="trailing" secondItem="k4j-Ub-3bn" secondAttribute="trailing" id="xvH-sO-9MX"/>
</constraints>
@@ -257,6 +268,7 @@
</connections>
</view>
<connections>
<outlet property="mediaControlView" destination="2Cb-zQ-dUZ" id="x2S-Ga-6sm"/>
<outlet property="mediaView" destination="k4j-Ub-3bn" id="RmJ-k6-aF2"/>
</connections>
</viewController>
+14 -4
View File
@@ -1081,15 +1081,25 @@ func sendMessageWithMediaType(mediaType: MessageMediaType, inFilePath filePath:
thumbnailData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality())
}
s3PrivateUploadParams(failureHandler: nil) { s3UploadParams in
uploadFileToS3(inFilePath: nil, orFileData: thumbnailData, mimeType: MessageMediaType.Image.mineType(), s3UploadParams: s3UploadParams) { (result, error) in
s3PrivateUploadParams(failureHandler: nil) { thumbnailS3UploadParams in
uploadFileToS3(inFilePath: nil, orFileData: thumbnailData, mimeType: MessageMediaType.Image.mineType(), s3UploadParams: thumbnailS3UploadParams) { (result, error) in
if let metaData = metaData {
let attachments = ["video": [["file": s3UploadParams.key, "metadata": metaData]], "thumbnail": [["file": s3UploadParams.key]]]
let attachments = [
"video": [
["file": s3UploadParams.key, "metadata": metaData]
],
"thumbnail": [["file": thumbnailS3UploadParams.key]]
]
messageInfo["attachments"] = attachments
} else {
let attachments = ["video": [["file": s3UploadParams.key]], "thumbnail": [["file": s3UploadParams.key]]]
let attachments = [
"video": [
["file": s3UploadParams.key]
],
"thumbnail": [["file": thumbnailS3UploadParams.key]]
]
messageInfo["attachments"] = attachments
}
+1 -1
View File
@@ -70,7 +70,7 @@ func downloadAttachmentOfMessage(message: Message) {
let thumbnailURLString = message.thumbnailURLString
if !thumbnailURLString.isEmpty && message.localThumbnailName.isEmpty {
if let url = NSURL(string: attachmentURLString) {
if let url = NSURL(string: thumbnailURLString) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let data = NSData(contentsOfURL: url)
@@ -0,0 +1,190 @@
//
// ConversationMessagePreviewTransitionManager.swift
// Yep
//
// Created by NIX on 15/5/6.
// Copyright (c) 2015 Catch Inc. All rights reserved.
//
import UIKit
class ConversationMessagePreviewTransitionManager: NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning {
var frame = CGRectZero
var transitionView: UIView? {
didSet {
if let transitionView = transitionView {
transitionViewSnapshot = transitionView.snapshotViewAfterScreenUpdates(false)
}
}
}
var transitionViewSnapshot: UIView?
var isPresentation = false
var transitionContext: UIViewControllerContextTransitioning?
// MARK: UIViewControllerTransitioningDelegate
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresentation = true
return self
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
isPresentation = false
return self
}
// MARK: UIViewControllerAnimatedTransitioning
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
return isPresentation ? 0.5 : 0.5
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
self.transitionContext = transitionContext
if isPresentation {
presentTransition(transitionContext)
} else {
dismissTransition(transitionContext)
}
}
let largerOffset: CGFloat = 80
func presentTransition(transitionContext: UIViewControllerContextTransitioning) {
let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as? ConversationsViewController
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as? MessageMediaViewController
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)
let containerView = transitionContext.containerView()
containerView.addSubview(toView!)
let animatingVC = toVC!
let animatingView = toView!
if let transitionViewSnapshot = transitionViewSnapshot {
animatingView.addSubview(transitionViewSnapshot)
transitionViewSnapshot.frame = frame
animatingVC.view.backgroundColor = UIColor.clearColor()
animatingVC.mediaView.alpha = 0
animatingVC.mediaControlView.alpha = 0
let fullDuration = transitionDuration(transitionContext)
UIView.animateKeyframesWithDuration(fullDuration, delay: 0.0, options: .CalculationModeCubic, animations: { () -> Void in
UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 0.2, animations: { () -> Void in
animatingVC.view.backgroundColor = UIColor.whiteColor()
})
UIView.addKeyframeWithRelativeStartTime(0.2, relativeDuration: 0.5, animations: { () -> Void in
transitionViewSnapshot.center = animatingView.center
})
UIView.addKeyframeWithRelativeStartTime(0.7, relativeDuration: 0.2, animations: { () -> Void in
let targetWidth = animatingView.bounds.width + self.largerOffset
let dw = targetWidth - transitionViewSnapshot.bounds.width
let ratio = targetWidth / transitionViewSnapshot.bounds.width
let height = ratio * transitionViewSnapshot.bounds.height
let dh = height - transitionViewSnapshot.bounds.height
let frame = CGRectInset(transitionViewSnapshot.frame, -dw * 0.5, -dh * 0.5)
transitionViewSnapshot.frame = frame
})
UIView.addKeyframeWithRelativeStartTime(0.9, relativeDuration: 0.0, animations: { () -> Void in
let ratio = (animatingView.bounds.width + self.largerOffset) / animatingView.bounds.width
animatingVC.mediaView.transform = CGAffineTransformMakeScale(ratio, ratio)
animatingVC.mediaView.alpha = 1
animatingVC.mediaControlView.alpha = 1
transitionViewSnapshot.alpha = 0
})
UIView.addKeyframeWithRelativeStartTime(0.9, relativeDuration: 0.1, animations: { () -> Void in
animatingVC.mediaView.transform = CGAffineTransformMakeScale(1.0, 1.0)
})
}, completion: { (finished) -> Void in
transitionViewSnapshot.removeFromSuperview()
transitionContext.completeTransition(true)
})
} else {
transitionContext.completeTransition(false)
}
}
func dismissTransition(transitionContext: UIViewControllerContextTransitioning) {
let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as? MessageMediaViewController
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
let animatingVC = fromVC!
let animatingView = fromView!
let fullDuration = transitionDuration(transitionContext)
if let transitionViewSnapshot = transitionViewSnapshot {
if let transitionView = self.transitionView {
transitionView.alpha = 0
}
UIView.animateKeyframesWithDuration(fullDuration, delay: 0.0, options: .CalculationModeCubic, animations: { () -> Void in
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.2, animations: { () -> Void in
let ratio = (animatingView.bounds.width + self.largerOffset) / animatingView.bounds.width
animatingVC.mediaView.transform = CGAffineTransformMakeScale(ratio, ratio)
animatingVC.mediaControlView.alpha = 0
})
UIView.addKeyframeWithRelativeStartTime(0.2, relativeDuration: 0.0, animations: { () -> Void in
animatingView.addSubview(transitionViewSnapshot)
transitionViewSnapshot.center = animatingView.center
transitionViewSnapshot.alpha = 1
animatingVC.mediaView.alpha = 0
})
UIView.addKeyframeWithRelativeStartTime(0.2, relativeDuration: 0.6, animations: { () -> Void in
transitionViewSnapshot.frame = self.frame
})
UIView.addKeyframeWithRelativeStartTime(0.8, relativeDuration: 0.2, animations: { () -> Void in
animatingVC.view.backgroundColor = UIColor.clearColor()
})
}, completion: { (finished) -> Void in
if let transitionView = self.transitionView {
transitionView.alpha = 1
}
transitionViewSnapshot.removeFromSuperview()
transitionContext.completeTransition(true)
})
} else {
transitionContext.completeTransition(false)
}
}
}
@@ -39,6 +39,8 @@ class ConversationViewController: UIViewController {
return dateFormatter
}()
var messagePreviewTransitionManager: ConversationMessagePreviewTransitionManager?
var conversationCollectionViewHasBeenMovedToBottomOnce = false
@@ -898,11 +900,82 @@ class ConversationViewController: UIViewController {
// MARK: Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "presentMessageMedia" {
let vc = segue.destinationViewController as! MessageMediaViewController
vc.message = sender as? Message
if let message = sender as? Message {
vc.message = message
let indexPath = NSIndexPath(forRow: Int(messages.indexOfObject(message)) - displayedMessagesRange.location , inSection: 0)
if let cell = conversationCollectionView.cellForItemAtIndexPath(indexPath) {
var frame = CGRectZero
var transitionView: UIView?
if let sender = message.fromFriend {
if sender.friendState != UserFriendState.Me.rawValue {
switch message.mediaType {
case MessageMediaType.Image.rawValue:
let cell = cell as! ChatLeftImageCell
transitionView = cell.messageImageView
frame = cell.convertRect(cell.messageImageView.frame, toView: view)
case MessageMediaType.Video.rawValue:
let cell = cell as! ChatLeftVideoCell
transitionView = cell.thumbnailImageView
frame = cell.convertRect(cell.thumbnailImageView.frame, toView: view)
case MessageMediaType.Location.rawValue:
let cell = cell as! ChatLeftLocationCell
transitionView = cell.mapImageView
frame = cell.convertRect(cell.mapImageView.frame, toView: view)
default:
break
}
} else {
switch message.mediaType {
case MessageMediaType.Image.rawValue:
let cell = cell as! ChatRightImageCell
transitionView = cell.messageImageView
frame = cell.convertRect(cell.messageImageView.frame, toView: view)
case MessageMediaType.Video.rawValue:
let cell = cell as! ChatRightVideoCell
transitionView = cell.thumbnailImageView
frame = cell.convertRect(cell.thumbnailImageView.frame, toView: view)
case MessageMediaType.Location.rawValue:
let cell = cell as! ChatRightLocationCell
transitionView = cell.mapImageView
frame = cell.convertRect(cell.mapImageView.frame, toView: view)
default:
break
}
}
}
vc.modalPresentationStyle = UIModalPresentationStyle.Custom
let transitionManager = ConversationMessagePreviewTransitionManager()
transitionManager.frame = frame
transitionManager.transitionView = transitionView
vc.transitioningDelegate = transitionManager
messagePreviewTransitionManager = transitionManager
}
}
} else if segue.identifier == "presentPickLocation" {
let nvc = segue.destinationViewController as! UINavigationController
let vc = nvc.topViewController as! PickLocationViewController
@@ -15,6 +15,12 @@ class MessageMediaViewController: UIViewController {
@IBOutlet weak var mediaView: MediaView!
@IBOutlet weak var mediaControlView: MediaControlView!
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
@@ -23,13 +29,33 @@ class MessageMediaViewController: UIViewController {
switch message.mediaType {
case MessageMediaType.Image.rawValue:
mediaControlView.type = .Image
if
let imageFileURL = NSFileManager.yepMessageImageURLWithName(message.localAttachmentName),
let image = UIImage(contentsOfFile: imageFileURL.path!) {
mediaView.imageView.image = image
mediaControlView.shareAction = {
let presentingViewController = self.presentingViewController
self.dismissViewControllerAnimated(true, completion: { () -> Void in
let activityViewController = UIActivityViewController(activityItems: [image], applicationActivities: nil)
presentingViewController?.presentViewController(activityViewController, animated: true, completion: { () -> Void in
})
})
}
}
case MessageMediaType.Video.rawValue:
mediaControlView.type = .Video
mediaControlView.playState = .Playing
if
let videoFileURL = NSFileManager.yepMessageVideoURLWithName(message.localAttachmentName),
let asset = AVURLAsset(URL: videoFileURL, options: [:]),
@@ -37,15 +63,56 @@ class MessageMediaViewController: UIViewController {
let x = NSFileManager.defaultManager().fileExistsAtPath(videoFileURL.path!)
mediaView.videoPlayerLayer.frame = mediaView.bounds
playerItem.seekToTime(kCMTimeZero)
//mediaView.videoPlayerLayer.player.replaceCurrentItemWithPlayerItem(playerItem)
mediaView.videoPlayerLayer.player = AVPlayer(playerItem: playerItem)
let player = AVPlayer(playerItem: playerItem)
mediaControlView.timeLabel.text = ""
player.addPeriodicTimeObserverForInterval(CMTimeMakeWithSeconds(0.1, Int32(NSEC_PER_SEC)), queue: nil, usingBlock: { time in
if player.currentItem.status == .ReadyToPlay {
let durationSeconds = CMTimeGetSeconds(player.currentItem.duration)
let currentSeconds = CMTimeGetSeconds(time)
let coundDownTime = Double(Int((durationSeconds - currentSeconds) * 10)) / 10
self.mediaControlView.timeLabel.text = "\(coundDownTime)"
}
})
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerItemDidReachEnd:", name: AVPlayerItemDidPlayToEndTimeNotification, object: player.currentItem)
mediaControlView.playAction = { mediaControlView in
player.play()
mediaControlView.playState = .Playing
}
mediaControlView.pauseAction = { mediaControlView in
player.pause()
mediaControlView.playState = .Pause
}
mediaView.videoPlayerLayer.player = player
mediaView.videoPlayerLayer.player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions(0), context: nil)
//mediaView.videoPlayerLayer.player.play()
mediaView.imageView.removeFromSuperview()
mediaControlView.shareAction = {
let presentingViewController = self.presentingViewController
self.dismissViewControllerAnimated(true, completion: { () -> Void in
let activityViewController = UIActivityViewController(activityItems: [videoFileURL], applicationActivities: nil)
presentingViewController?.presentViewController(activityViewController, animated: true, completion: { () -> Void in
})
})
}
}
default:
@@ -54,6 +121,12 @@ class MessageMediaViewController: UIViewController {
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
mediaView.videoPlayerLayer.player.pause()
}
@IBAction func swipeDown(sender: UISwipeGestureRecognizer) {
if let message = message {
@@ -67,16 +140,21 @@ class MessageMediaViewController: UIViewController {
override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
if let player = object as? AVPlayer {
if player == mediaView.videoPlayerLayer.player {
if keyPath == "status" {
switch player.status {
case AVPlayerStatus.Failed:
println("Failed")
case AVPlayerStatus.ReadyToPlay:
println("ReadyToPlay")
dispatch_async(dispatch_get_main_queue()) {
self.mediaView.videoPlayerLayer.player.play()
}
case AVPlayerStatus.Unknown:
println("Unknown")
}
@@ -84,4 +162,12 @@ class MessageMediaViewController: UIViewController {
}
}
}
func playerItemDidReachEnd(notification: NSNotification) {
mediaControlView.playState = .Pause
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seekToTime(kCMTimeZero)
}
}
}
+140
View File
@@ -0,0 +1,140 @@
//
// MediaControlView.swift
// Yep
//
// Created by NIX on 15/5/7.
// Copyright (c) 2015 Catch Inc. All rights reserved.
//
import UIKit
//@IBDesignable
class MediaControlView: UIView {
enum Type {
case Image
case Video
}
var type: Type = .Video {
didSet {
switch type {
case .Image:
timeLabel.hidden = true
playButton.hidden = true
case .Video:
timeLabel.hidden = false
playButton.hidden = false
}
}
}
enum PlayState {
case Playing
case Pause
}
var playState: PlayState = .Pause {
didSet {
switch playState {
case .Playing:
playButton.setImage(UIImage(named: "icon_pause"), forState: .Normal)
case .Pause:
playButton.setImage(UIImage(named: "icon_play"), forState: .Normal)
}
}
}
var playAction: (MediaControlView -> Void)?
var pauseAction: (MediaControlView -> Void)?
var shareAction: (() -> Void)?
lazy var timeLabel: UILabel = {
let label = UILabel()
label.textAlignment = .Center
label.font = UIFont(name: "HelveticaNeue-Light", size: 14)
label.textColor = UIColor.whiteColor()
label.text = "00:42"
return label
}()
lazy var playButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "icon_play"), forState: .Normal)
button.tintColor = UIColor.whiteColor()
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
button.addTarget(self, action: "playOrPause", forControlEvents: UIControlEvents.TouchUpInside)
return button
}()
lazy var shareButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "icon_more"), forState: .Normal)
button.tintColor = UIColor.whiteColor()
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
button.addTarget(self, action: "share", forControlEvents: UIControlEvents.TouchUpInside)
return button
}()
override func didMoveToSuperview() {
super.didMoveToSuperview()
makeUI()
}
func makeUI() {
addSubview(timeLabel)
addSubview(playButton)
addSubview(shareButton)
timeLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
playButton.setTranslatesAutoresizingMaskIntoConstraints(false)
shareButton.setTranslatesAutoresizingMaskIntoConstraints(false)
let viewsDictionary = [
"timeLable": timeLabel,
"playButton": playButton,
"shareButton": shareButton,
]
let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|[timeLable]|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[timeLable]-(>=0)-[playButton]-(>=0)-[shareButton]|", options: .AlignAllCenterY | .AlignAllTop | .AlignAllBottom, metrics: nil, views: viewsDictionary)
NSLayoutConstraint.activateConstraints(constraintsV)
NSLayoutConstraint.activateConstraints(constraintsH)
let playButtonConstraintCenterX = NSLayoutConstraint(item: playButton, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1.0, constant: 0)
NSLayoutConstraint.activateConstraints([playButtonConstraintCenterX])
}
// MARK: Actions
func playOrPause() {
switch playState {
case .Playing:
if let action = pauseAction {
action(self)
}
case .Pause:
if let action = playAction {
action(self)
}
}
}
func share() {
if let action = shareAction {
action()
}
}
}
+10 -3
View File
@@ -13,14 +13,14 @@ class MediaView: UIView {
lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .ScaleAspectFill
imageView.contentMode = .ScaleAspectFit
return imageView
}()
lazy var videoPlayerLayer: AVPlayerLayer = {
let player = AVPlayer()
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect
//playerLayer.backgroundColor = UIColor.darkGrayColor().CGColor
return playerLayer
}()
@@ -31,7 +31,14 @@ class MediaView: UIView {
makeUI()
layer.addSublayer(videoPlayerLayer)
videoPlayerLayer.frame = bounds
println("videoPlayerLayer.frame: \(videoPlayerLayer.frame)")
}
override func layoutSubviews() {
super.layoutSubviews()
videoPlayerLayer.frame = UIScreen.mainScreen().bounds
}
func makeUI() {