Merge branch 'feature/progressive_image_download' into develop

This commit is contained in:
nixzhu
2016-01-30 10:00:49 +08:00
8 changed files with 260 additions and 17 deletions
+4
View File
@@ -246,6 +246,7 @@
50AFBF4B1BB2A44C007D19DA /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF4A1BB2A44C007D19DA /* InfoView.swift */; };
50AFBF4E1BB3A594007D19DA /* bub3.caf in Resources */ = {isa = PBXBuildFile; fileRef = 50AFBF4D1BB3A594007D19DA /* bub3.caf */; };
50AFBF5F1BB4F332007D19DA /* FeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */; };
50B22E461C588B6200219625 /* CGImage+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B22E451C588B6200219625 /* CGImage+Yep.swift */; };
50B2CA921BD0BF3800668AC6 /* FeedTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B2CA911BD0BF3800668AC6 /* FeedTextView.swift */; };
50B2CA941BD0EF7900668AC6 /* MoreMessageTypesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B2CA931BD0EF7900668AC6 /* MoreMessageTypesView.swift */; };
50B38FB71B69CD960064C132 /* Array+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B38FB61B69CD960064C132 /* Array+Yep.swift */; };
@@ -619,6 +620,7 @@
50AFBF4A1BB2A44C007D19DA /* InfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InfoView.swift; path = Views/InfoView/InfoView.swift; sourceTree = "<group>"; };
50AFBF4D1BB3A594007D19DA /* bub3.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = bub3.caf; path = Sounds/bub3.caf; sourceTree = "<group>"; };
50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedsViewController.swift; path = ViewControllers/Feeds/FeedsViewController.swift; sourceTree = "<group>"; };
50B22E451C588B6200219625 /* CGImage+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "CGImage+Yep.swift"; path = "Extensions/CGImage+Yep.swift"; sourceTree = "<group>"; };
50B2CA911BD0BF3800668AC6 /* FeedTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedTextView.swift; path = Views/TextViews/FeedTextView.swift; sourceTree = "<group>"; };
50B2CA931BD0EF7900668AC6 /* MoreMessageTypesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MoreMessageTypesView.swift; path = Views/MoreMessageTypes/MoreMessageTypesView.swift; sourceTree = "<group>"; };
50B38FB61B69CD960064C132 /* Array+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Array+Yep.swift"; path = "Extensions/Array+Yep.swift"; sourceTree = "<group>"; };
@@ -1137,6 +1139,7 @@
50DBDF041C4DD9530044E21B /* CGSize+Yep.swift */,
5023DE511AB6C51300B3EE96 /* CGRect+Yep.swift */,
5023DE581AB6DBC100B3EE96 /* UIImage+Yep.swift */,
50B22E451C588B6200219625 /* CGImage+Yep.swift */,
506EC2091AC95BFC00CE42B3 /* UIFont+Yep.swift */,
0A3E605F1ACCA9C0003DC853 /* String+Yep.swift */,
50A9D44A1ACD37A8000B2599 /* NSFileManager+Yep.swift */,
@@ -2649,6 +2652,7 @@
50CDBE421ACA9B4200459CE0 /* ChatRightImageCell.swift in Sources */,
5099B9C41AB9510D002F940B /* RankView.swift in Sources */,
502AE5311AB85067005BD199 /* RegisterPickMobileViewController.swift in Sources */,
50B22E461C588B6200219625 /* CGImage+Yep.swift in Sources */,
50535D1A1BCD19AE002FE1A4 /* TouchClosuresView.swift in Sources */,
506E241E1AE6391A00A33C1A /* TouchZoomButton.swift in Sources */,
5020B8A51C059662006CE8EA /* VoiceRecordSampleView.swift in Sources */,
+5 -2
View File
@@ -213,11 +213,14 @@ class ImageCache {
let mediaType = message.mediaType
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { progress in
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { progress, image in
dispatch_async(dispatch_get_main_queue()) {
completion(loadingProgress: progress, image: nil)
completion(loadingProgress: progress, image: image)
}
}, imageTransform: { image in
return image.bubbleImageWithTailDirection(tailDirection, size: size).decodedImage()
}, imageFinished: { image in
let messageImage = image.bubbleImageWithTailDirection(tailDirection, size: size).decodedImage()
+52
View File
@@ -0,0 +1,52 @@
//
// CGImage+Yep.swift
// Yep
//
// Created by nixzhu on 16/1/27.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import Foundation
extension CGImage {
var yep_extendedCanvasCGImage: CGImage {
let width = CGImageGetWidth(self)
let height = CGImageGetHeight(self)
guard width > 0 && height > 0 else {
return self
}
func hasAlpha() -> Bool {
let alpha = CGImageGetAlphaInfo(self)
switch alpha {
case .First, .Last, .PremultipliedFirst, .PremultipliedLast:
return true
default:
return false
}
}
var bitmapInfo = CGBitmapInfo.ByteOrder32Little.rawValue
if hasAlpha() {
bitmapInfo |= CGImageAlphaInfo.PremultipliedFirst.rawValue
} else {
bitmapInfo |= CGImageAlphaInfo.NoneSkipFirst.rawValue
}
guard let context = CGBitmapContextCreate(nil, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo) else {
return self
}
CGContextDrawImage(context, CGRect(x: 0, y: 0, width: width, height: height), self)
guard let newCGImage = CGBitmapContextCreateImage(context) else {
return self
}
return newCGImage
}
}
+48
View File
@@ -8,6 +8,8 @@
import UIKit
import Ruler
import ImageIO
import MobileCoreServices
extension UIImage {
@@ -572,3 +574,49 @@ extension UIImage {
}
}
// MARK: Progressive
extension UIImage {
var yep_progressiveImage: UIImage? {
guard let cgImage = CGImage else {
return nil
}
let data = NSMutableData()
guard let distination = CGImageDestinationCreateWithData(data, kUTTypeJPEG, 1, nil) else {
return nil
}
let jfifProperties = [
kCGImagePropertyJFIFIsProgressive as String: kCFBooleanTrue as Bool,
kCGImagePropertyJFIFXDensity as String: 72,
kCGImagePropertyJFIFYDensity as String: 72,
kCGImagePropertyJFIFDensityUnit as String: 1,
]
let properties = [
kCGImageDestinationLossyCompressionQuality as String: 0.9,
kCGImagePropertyJFIFDictionary as String: jfifProperties,
]
CGImageDestinationAddImage(distination, cgImage, properties)
guard CGImageDestinationFinalize(distination) else {
return nil
}
guard data.length > 0 else {
return nil
}
guard let progressiveImage = UIImage(data: data) else {
return nil
}
return progressiveImage
}
}
+141 -13
View File
@@ -8,6 +8,7 @@
import Foundation
import RealmSwift
import ImageIO
class YepDownloader: NSObject {
@@ -47,17 +48,20 @@ class YepDownloader: NSObject {
struct ProgressReporter {
struct Task {
let downloadTask: NSURLSessionDownloadTask
let downloadTask: NSURLSessionDataTask
typealias FinishedAction = NSData -> Void
let finishedAction: FinishedAction
let progress = NSProgress()
let tempData = NSMutableData()
let imageSource = CGImageSourceCreateIncremental(nil)
let imageTransform: (UIImage -> UIImage)?
}
let tasks: [Task]
var finishedTasksCount = 0
typealias ReportProgress = Double -> Void
typealias ReportProgress = (progress: Double, image: UIImage?) -> Void
let reportProgress: ReportProgress?
init(tasks: [Task], reportProgress: ReportProgress?) {
@@ -77,10 +81,10 @@ class YepDownloader: NSObject {
var progressReporters = [ProgressReporter]()
class func downloadAttachmentsOfMessage(message: Message, reportProgress: ProgressReporter.ReportProgress?) {
downloadAttachmentsOfMessage(message, reportProgress: reportProgress, imageFinished: nil)
downloadAttachmentsOfMessage(message, reportProgress: reportProgress, imageTransform: nil, imageFinished: nil)
}
class func downloadAttachmentsOfMessage(message: Message, reportProgress: ProgressReporter.ReportProgress?, imageFinished: (UIImage -> Void)?) {
class func downloadAttachmentsOfMessage(message: Message, reportProgress: ProgressReporter.ReportProgress?, imageTransform: (UIImage -> UIImage)?, imageFinished: (UIImage -> Void)?) {
let downloadState = message.downloadState
@@ -91,14 +95,14 @@ class YepDownloader: NSObject {
let messageID = message.messageID
let mediaType = message.mediaType
var attachmentDownloadTask: NSURLSessionDownloadTask?
var attachmentDownloadTask: NSURLSessionDataTask?
var attachmentFinishedAction: ProgressReporter.Task.FinishedAction?
let attachmentURLString = message.attachmentURLString
if !attachmentURLString.isEmpty && message.localAttachmentName.isEmpty, let URL = NSURL(string: attachmentURLString) {
attachmentDownloadTask = sharedDownloader.session.downloadTaskWithURL(URL)
attachmentDownloadTask = sharedDownloader.session.dataTaskWithURL(URL)
attachmentFinishedAction = { data in
@@ -152,7 +156,7 @@ class YepDownloader: NSObject {
}
}
var thumbnailDownloadTask: NSURLSessionDownloadTask?
var thumbnailDownloadTask: NSURLSessionDataTask?
var thumbnailFinishedAction: ProgressReporter.Task.FinishedAction?
if mediaType == MessageMediaType.Video.rawValue {
@@ -161,7 +165,7 @@ class YepDownloader: NSObject {
if !thumbnailURLString.isEmpty && message.localThumbnailName.isEmpty, let URL = NSURL(string: thumbnailURLString) {
thumbnailDownloadTask = sharedDownloader.session.downloadTaskWithURL(URL)
thumbnailDownloadTask = sharedDownloader.session.dataTaskWithURL(URL)
thumbnailFinishedAction = { data in
@@ -196,11 +200,11 @@ class YepDownloader: NSObject {
var tasks: [ProgressReporter.Task] = []
if let attachmentDownloadTask = attachmentDownloadTask, attachmentFinishedAction = attachmentFinishedAction {
tasks.append(ProgressReporter.Task(downloadTask: attachmentDownloadTask, finishedAction: attachmentFinishedAction))
tasks.append(ProgressReporter.Task(downloadTask: attachmentDownloadTask, finishedAction: attachmentFinishedAction, imageTransform: imageTransform))
}
if let thumbnailDownloadTask = thumbnailDownloadTask, thumbnailFinishedAction = thumbnailFinishedAction {
tasks.append(ProgressReporter.Task(downloadTask: thumbnailDownloadTask, finishedAction: thumbnailFinishedAction))
tasks.append(ProgressReporter.Task(downloadTask: thumbnailDownloadTask, finishedAction: thumbnailFinishedAction, imageTransform: imageTransform))
}
if tasks.count > 0 {
@@ -218,8 +222,8 @@ class YepDownloader: NSObject {
class func downloadDataFromURL(URL: NSURL, reportProgress: ProgressReporter.ReportProgress?, finishedAction: ProgressReporter.Task.FinishedAction) {
let downloadTask = sharedDownloader.session.downloadTaskWithURL(URL)
let task = ProgressReporter.Task(downloadTask: downloadTask, finishedAction: finishedAction)
let downloadTask = sharedDownloader.session.dataTaskWithURL(URL)
let task = ProgressReporter.Task(downloadTask: downloadTask, finishedAction: finishedAction, imageTransform: nil)
let progressReporter = ProgressReporter(tasks: [task], reportProgress: reportProgress)
sharedDownloader.progressReporters.append(progressReporter)
@@ -232,6 +236,130 @@ extension YepDownloader: NSURLSessionDelegate {
}
extension YepDownloader: NSURLSessionDataDelegate {
private func reportProgressAssociatedWithDownloadTask(downloadTask: NSURLSessionDataTask, totalBytes: Int64) {
for progressReporter in progressReporters {
for i in 0..<progressReporter.tasks.count {
if downloadTask == progressReporter.tasks[i].downloadTask {
progressReporter.tasks[i].progress.totalUnitCount = totalBytes
progressReporter.reportProgress?(progress: progressReporter.totalProgress, image: nil)
return
}
}
}
}
private func reportProgressAssociatedWithDownloadTask(downloadTask: NSURLSessionDataTask, didReceiveData data: NSData) -> Bool {
for progressReporter in progressReporters {
for i in 0..<progressReporter.tasks.count {
if downloadTask == progressReporter.tasks[i].downloadTask {
let didReceiveDataBytes = Int64(data.length)
progressReporter.tasks[i].progress.completedUnitCount += didReceiveDataBytes
progressReporter.tasks[i].tempData.appendData(data)
let progress = progressReporter.tasks[i].progress
let final = progress.completedUnitCount == progress.totalUnitCount
progressReporter.reportProgress?(progress: progressReporter.totalProgress, image: nil)
/*
let imageSource = progressReporter.tasks[i].imageSource
let data = progressReporter.tasks[i].tempData
CGImageSourceUpdateData(imageSource, data, final)
var tranformedImage: UIImage?
if let cgImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
let image = UIImage(CGImage: cgImage)
if let imageTransform = progressReporter.tasks[i].imageTransform {
tranformedImage = imageTransform(image)
}
/*
let image = UIImage(CGImage: cgImage.yep_extendedCanvasCGImage)
if progressReporter.totalProgress < 1 {
let blurPercent = CGFloat(1 - progressReporter.totalProgress)
let radius = 5 * blurPercent
let iterations = UInt(10 * blurPercent)
println("radius: \(radius), iterations: \(iterations)")
if let blurredImage = image.blurredImageWithRadius(radius, iterations: iterations, tintColor: UIColor.clearColor()) {
if let imageTransform = progressReporter.tasks[i].imageTransform {
tranformedImage = imageTransform(blurredImage)
/*
if progressReporter.totalProgress > 0.3 {
print("imageTransform")
}*/
}
}
}
*/
}
progressReporter.reportProgress?(progress: progressReporter.totalProgress, image: tranformedImage)
*/
return final
}
}
}
return false
}
private func finishDownloadTask(downloadTask: NSURLSessionDataTask) {
for i in 0..<progressReporters.count {
for j in 0..<progressReporters[i].tasks.count {
if downloadTask == progressReporters[i].tasks[j].downloadTask {
let finishedAction = progressReporters[i].tasks[j].finishedAction
let data = progressReporters[i].tasks[j].tempData
finishedAction(data)
progressReporters[i].finishedTasksCount++
// progressReporter
if progressReporters[i].finishedTasksCount == progressReporters[i].tasks.count {
progressReporters.removeAtIndex(i)
}
return
}
}
}
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
println("YepDownloader begin, expectedContentLength:\(response.expectedContentLength)")
reportProgressAssociatedWithDownloadTask(dataTask, totalBytes: response.expectedContentLength)
completionHandler(.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
println("YepDownloader data.length: \(data.length)")
let finish = reportProgressAssociatedWithDownloadTask(dataTask, didReceiveData: data)
if finish {
println("YepDownloader finish")
finishDownloadTask(dataTask)
}
}
}
/*
extension YepDownloader: NSURLSessionDownloadDelegate {
private func handleData(data: NSData, ofDownloadTask downloadTask: NSURLSessionDownloadTask) {
@@ -300,4 +428,4 @@ extension YepDownloader: NSURLSessionDownloadDelegate {
reportProgressAssociatedWithDownloadTask(downloadTask, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
}
}
*/
@@ -4633,6 +4633,14 @@ extension ConversationViewController: UIImagePickerControllerDelegate, UINavigat
// Do send
let imageData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality())!
/*
var imageData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality())!
if let progressiveImage = UIImage(data: imageData)?.yep_progressiveImage {
imageData = UIImageJPEGRepresentation(progressiveImage, YepConfig.messageImageCompressionQuality())!
}
*/
let messageImageName = NSUUID().UUIDString
@@ -143,7 +143,7 @@ class ChatLeftAudioCell: ChatBaseCell {
self.audioPlayedDuration = audioPlayedDuration
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { [weak self] progress in
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { [weak self] progress, _ in
dispatch_async(dispatch_get_main_queue()) {
self?.loadingWithProgress(progress)
}
@@ -114,7 +114,7 @@ class ChatRightAudioCell: ChatRightBaseCell {
self.audioPlayedDuration = audioPlayedDuration
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { _ in })
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { _, _ in })
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()