Skip to content

Commit

Permalink
- Added a Custom Image Transition case
Browse files Browse the repository at this point in the history
- Modified the completion closure behavior to automatically set the image in all cases now
  • Loading branch information
kcharwood committed Sep 15, 2015
1 parent fa578d5 commit 5501afa
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 32 deletions.
75 changes: 47 additions & 28 deletions Source/UIImageView+AlamofireImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,50 @@ extension UIImageView {
case FlipFromLeft(NSTimeInterval)
case FlipFromRight(NSTimeInterval)
case FlipFromTop(NSTimeInterval)
case Custom(NSTimeInterval, UIViewAnimationOptions, ((UIImageView, Image) -> (Void)), ((Bool) -> Void)?)

private var duration: NSTimeInterval {
switch self {
case None: return 0.0
case CrossDissolve(let duration): return duration
case CurlDown(let duration): return duration
case CurlUp(let duration): return duration
case FlipFromBottom(let duration): return duration
case FlipFromLeft(let duration): return duration
case FlipFromRight(let duration): return duration
case FlipFromTop(let duration): return duration
case None: return 0.0
case CrossDissolve(let duration): return duration
case CurlDown(let duration): return duration
case CurlUp(let duration): return duration
case FlipFromBottom(let duration): return duration
case FlipFromLeft(let duration): return duration
case FlipFromRight(let duration): return duration
case FlipFromTop(let duration): return duration
case Custom(let duration, _, _, _): return duration
}
}

private var animationOptions: UIViewAnimationOptions {
switch self {
case None: return .TransitionNone
case CrossDissolve: return .TransitionCrossDissolve
case CurlDown: return .TransitionCurlDown
case CurlUp: return .TransitionCurlUp
case FlipFromBottom: return .TransitionFlipFromBottom
case FlipFromLeft: return .TransitionFlipFromLeft
case FlipFromRight: return .TransitionFlipFromRight
case FlipFromTop: return .TransitionFlipFromTop
case None: return .TransitionNone
case CrossDissolve: return .TransitionCrossDissolve
case CurlDown: return .TransitionCurlDown
case CurlUp: return .TransitionCurlUp
case FlipFromBottom: return .TransitionFlipFromBottom
case FlipFromLeft: return .TransitionFlipFromLeft
case FlipFromRight: return .TransitionFlipFromRight
case FlipFromTop: return .TransitionFlipFromTop
case Custom(_, let animationOptions, _, _): return animationOptions
}
}

private var animations: ((UIImageView, Image) -> (Void)) {
switch self {
case Custom(_, _,let animations, _): return animations
default:
let animation: ((UIImageView, Image) -> (Void)) = {$0.image = $1}
return animation
}
}

private var completion: ((Bool) -> Void)? {
switch self {
case Custom(_, _, _, let completion): return completion
default:
return nil
}
}
}
Expand Down Expand Up @@ -204,9 +224,11 @@ extension UIImageView {
If the image is cached locally, the image is set immediately. Otherwise the specified placehoder image will be
set immediately, and then the remote image will be set once the image request is finished.

If a `completion` closure is specified, it is the responsibility of the closure to set the image of the image
view before returning. If no `completion` closure is specified, the default behavior of setting the image is
applied.
If an 'imageTransition` other than `None` is specified, the `completion` closure will be called just before the start of transition, and the image will not yet be set on the image view. If `None` is specified, the completion closure will be called after the image has been set on the image view.

Note that it is no longer the responsibility of the `completion` closure to set the image. It will be set automatically.

If you require being notified when the image transition has completion, use a `.Custom` image transition and specify a completion closure. That closure will be called when the transition animation has finished.

- parameter URL: The URL used for the image request.
- parameter placeholderImage: The image to be set initially until the image request finished. If `nil`, the
Expand Down Expand Up @@ -263,26 +285,23 @@ extension UIImageView {

strongSelf.af_activeRequest = nil

guard completion == nil else {
completion?(request, response, result)
return
}

if let image = result.value {
switch imageTransition {
case .None:
strongSelf.image = image
completion?(request, response, result)
default:
completion?(request, response, result)
UIView.transitionWithView(
strongSelf,
duration: imageTransition.duration,
options: imageTransition.animationOptions,
animations: {
strongSelf.image = image
},
completion: nil
animations: {imageTransition.animations(strongSelf, image)},
completion: imageTransition.completion
)
}
} else {
completion?(request, response, result)
}
}
)
Expand Down
59 changes: 55 additions & 4 deletions Tests/UIImageViewTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -326,14 +326,22 @@ class UIImageViewTestCase: BaseTestCase {

let expectation8 = expectationWithDescription("image download should succeed")
imageView.imageObserver = {
imageTransitionsComplete = true
expectation8.fulfill()
}

ImageDownloader.defaultInstance.imageCache?.removeAllImages()
imageView.af_setImageWithURL(URL, imageTransition: .FlipFromTop(0.1))
waitForExpectationsWithTimeout(timeout, handler: nil)

let customTransition = UIImageView.ImageTransition.Custom(0.5, UIViewAnimationOptions(), {$0.image = $1}, nil)
let expectation9 = expectationWithDescription("image download should succeed")
imageView.imageObserver = {
imageTransitionsComplete = true
expectation9.fulfill()
}
ImageDownloader.defaultInstance.imageCache?.removeAllImages()
imageView.af_setImageWithURL(URL, imageTransition: customTransition)
waitForExpectationsWithTimeout(timeout, handler: nil)

// Then
XCTAssertTrue(imageTransitionsComplete, "image transitions complete should be true")
XCTAssertNotNil(imageView.image, "image view image should not be nil")
Expand Down Expand Up @@ -373,7 +381,7 @@ class UIImageViewTestCase: BaseTestCase {

// Then
XCTAssertTrue(completionHandlerCalled, "completion handler called should be true")
XCTAssertNil(imageView.image, "image view image should be nil when completion handler is not nil")
XCTAssertNotNil(imageView.image, "image view image should be not be nil when completion handler is not nil")
XCTAssertTrue(result?.isSuccess ?? false, "result should be a success case")
}

Expand Down Expand Up @@ -408,6 +416,49 @@ class UIImageViewTestCase: BaseTestCase {
XCTAssertTrue(result?.isFailure ?? false, "result should be a failure case")
}

func testThatCompletionHandlerAndCustomTransitionHandlerAreBothCalled() {
// Given
let imageView = UIImageView()

let URLRequest: NSURLRequest = {
let request = NSMutableURLRequest(URL: URL)
request.addValue("image/*", forHTTPHeaderField: "Accept")
return request
}()

let transitionExpectation = expectationWithDescription("image transition should complete")
var transitionCompletionHandlerCalled = false
let customTransition = UIImageView.ImageTransition.Custom(0.5, UIViewAnimationOptions(), {$0.image = $1}, { (_) in
transitionCompletionHandlerCalled = true
transitionExpectation.fulfill()
})

let expectation = expectationWithDescription("image download should succeed")
var completionHandlerCalled = false
var result: Result<UIImage>?

// When
imageView.af_setImageWithURLRequest(
URLRequest,
placeholderImage: nil,
filter: nil,
imageTransition: customTransition,
completion: { _, _, responseResult in
completionHandlerCalled = true
result = responseResult
expectation.fulfill()
}
)

waitForExpectationsWithTimeout(timeout, handler: nil)

// Then
XCTAssertTrue(completionHandlerCalled, "completion handler called should be true")
XCTAssertTrue(transitionCompletionHandlerCalled, "transition completion hanlder called should be true")
XCTAssertNotNil(imageView.image, "image view image should be not be nil when completion handler is not nil")
XCTAssertTrue(result?.isSuccess ?? false, "result should be a success case")
}

// MARK: - Cancellation

func testThatImageDownloadCanBeCancelled() {
Expand Down Expand Up @@ -481,7 +532,7 @@ class UIImageViewTestCase: BaseTestCase {
// Then
XCTAssertFalse(completion1Called, "completion 1 called should be false")
XCTAssertTrue(completion2Called, "completion 2 called should be true")
XCTAssertNil(imageView.image, "image view image should be nil when completion handler is not nil")
XCTAssertNotNil(imageView.image, "image view image should not be nil when completion handler is not nil")
XCTAssertTrue(result?.isSuccess ?? false, "result should be a success case")
}
}

0 comments on commit 5501afa

Please sign in to comment.