自定义创建轮播组件(第一步)
import Foundation
import UIKit
import QuartzCore
import CoreGraphics
import Accelerate
public enum UIImageContentMode {
case scaleToFill, scaleAspectFit, scaleAspectFill
}
public extension UIImage {
/**
A singleton shared NSURL cache used for images from URL
*/
static var shared: NSCache<AnyObject, AnyObject>! {
struct StaticSharedCache {
static var shared: NSCache<AnyObject, AnyObject>? = NSCache()
}
return StaticSharedCache.shared!
}
/**
Creates a new solid color image.
- Parameter color: The color to fill the image with.
- Parameter size: Image size (defaults: 10x10)
- Returns A new image
*/
convenience init?(color: UIColor, size: CGSize = CGSize(width: 10, height: 10)) {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(color.cgColor)
context?.fill(rect)
self.init(cgImage:(UIGraphicsGetImageFromCurrentImageContext()?.cgImage!)!)
UIGraphicsEndImageContext()
}
/**
Creates a gradient color image.
- Parameter gradientColors: An array of colors to use for the gradient.
- Parameter size: Image size (defaults: 10x10)
- Returns A new image
*/
convenience init?(gradientColors:[UIColor], size:CGSize = CGSize(width: 10, height: 10), locations: [Float] = [] )
{
UIGraphicsBeginImageContextWithOptions(size, false, 0)
let context = UIGraphicsGetCurrentContext()
let colorSpace = CGColorSpaceCreateDeviceRGB()
let colors = gradientColors.map {(color: UIColor) -> AnyObject! in return color.cgColor as AnyObject! } as NSArray
let gradient: CGGradient
if locations.count > 0 {
let cgLocations = locations.map { CGFloat($0) }
gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: cgLocations)!
} else {
gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: nil)!
}
context!.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: size.height), options: CGGradientDrawingOptions(rawValue: 0))
self.init(cgImage:(UIGraphicsGetImageFromCurrentImageContext()?.cgImage!)!)
UIGraphicsEndImageContext()
}
/**
Applies gradient color overlay to an image.
- Parameter gradientColors: An array of colors to use for the gradient.
- Parameter locations: An array of locations to use for the gradient.
- Parameter blendMode: The blending type to use.
- Returns A new image
*/
func apply(gradientColors: [UIColor], locations: [Float] = [], blendMode: CGBlendMode = CGBlendMode.normal) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let context = UIGraphicsGetCurrentContext()
context?.translateBy(x: 0, y: size.height)
context?.scaleBy(x: 1.0, y: -1.0)
context?.setBlendMode(blendMode)
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
context?.draw(self.cgImage!, in: rect)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let colors = gradientColors.map {(color: UIColor) -> AnyObject! in return color.cgColor as AnyObject! } as NSArray
let gradient: CGGradient
if locations.count > 0 {
let cgLocations = locations.map { CGFloat($0) }
gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: cgLocations)!
} else {
gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: nil)!
}
context?.clip(to: rect, mask: self.cgImage!)
context?.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: size.height), options: CGGradientDrawingOptions(rawValue: 0))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
return image!;
}
/**
Creates a text label image.
- Parameter text: The text to use in the label.
- Parameter font: The font (default: System font of size 18)
- Parameter color: The text color (default: White)
- Parameter backgroundColor: The background color (default:Gray).
- Parameter size: Image size (default: 10x10)
- Parameter offset: Center offset (default: 0x0)
- Returns A new image
*/
convenience init?(text: String, font: UIFont = UIFont.systemFont(ofSize: 18), color: UIColor = UIColor.white, backgroundColor: UIColor = UIColor.gray, size: CGSize = CGSize(width: 100, height: 100), offset: CGPoint = CGPoint(x: 0, y: 0)) {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
label.font = font
label.text = text
label.textColor = color
label.textAlignment = .center
label.backgroundColor = backgroundColor
let image = UIImage(fromView: label)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
image?.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
self.init(cgImage:(UIGraphicsGetImageFromCurrentImageContext()?.cgImage!)!)
UIGraphicsEndImageContext()
}
/**
Creates an image from a UIView.
- Parameter fromView: The source view.
- Returns A new image
*/
convenience init?(fromView view: UIView) {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
self.init(cgImage:(UIGraphicsGetImageFromCurrentImageContext()?.cgImage!)!)
UIGraphicsEndImageContext()
}
/**
Creates a radial gradient.
- Parameter startColor: The start color
- Parameter endColor: The end color
- Parameter radialGradientCenter: The gradient center (default:0.5,0.5).
- Parameter radius: Radius size (default: 0.5)
- Parameter size: Image size (default: 100x100)
- Returns A new image
*/
convenience init?(startColor: UIColor, endColor: UIColor, radialGradientCenter: CGPoint = CGPoint(x: 0.5, y: 0.5), radius: Float = 0.5, size: CGSize = CGSize(width: 100, height: 100)) {
UIGraphicsBeginImageContextWithOptions(size, true, 0)
let num_locations: Int = 2
let locations: [CGFloat] = [0.0, 1.0] as [CGFloat]
let startComponents = startColor.cgColor.components!
let endComponents = endColor.cgColor.components!
let components: [CGFloat] = [startComponents[0], startComponents[1], startComponents[2], startComponents[3], endComponents[0], endComponents[1], endComponents[2], endComponents[3]]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorSpace: colorSpace, colorComponents: components, locations: locations, count: num_locations)
let aCenter = CGPoint(x: radialGradientCenter.x * size.width, y: radialGradientCenter.y * size.height)
let aRadius = CGFloat(min(size.width, size.height)) * CGFloat(radius)
UIGraphicsGetCurrentContext()?.drawRadialGradient(gradient!, startCenter: aCenter, startRadius: 0, endCenter: aCenter, endRadius: aRadius, options: CGGradientDrawingOptions.drawsAfterEndLocation)
self.init(cgImage:(UIGraphicsGetImageFromCurrentImageContext()?.cgImage!)!)
UIGraphicsEndImageContext()
}
/**
Returns true if the image has an alpha layer.
*/
var hasAlpha: Bool {
let alpha: CGImageAlphaInfo = self.cgImage!.alphaInfo
switch alpha {
case .first, .last, .premultipliedFirst, .premultipliedLast:
return true
default:
return false
}
}
/**
Returns a copy of the given image, adding an alpha channel if it doesn't already have one.
*/
func applyAlpha() -> UIImage? {
if hasAlpha {
return self
}
let imageRef = self.cgImage;
let width = imageRef?.width;
let height = imageRef?.height;
let colorSpace = imageRef?.colorSpace
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo().rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
let offscreenContext = CGContext(data: nil, width: width!, height: height!, bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace!, bitmapInfo: bitmapInfo.rawValue)
let rect: CGRect = CGRect(x: 0, y: 0, width: CGFloat(width!), height: CGFloat(height!))
offscreenContext?.draw(imageRef!, in: rect)
let imageWithAlpha = UIImage(cgImage: (offscreenContext?.makeImage()!)!)
return imageWithAlpha
}
/**
Returns a copy of the image with a transparent border of the given size added around its edges. i.e. For rotating an image without getting jagged edges.
- Parameter padding: The padding amount.
- Returns A new image.
*/
func apply(padding: CGFloat) -> UIImage? {
let image = self.applyAlpha()
if image == nil {
return nil
}
let rect = CGRect(x: 0, y: 0, width: size.width + padding * 2, height: size.height + padding * 2)
let colorSpace = self.cgImage?.colorSpace
let bitmapInfo = self.cgImage?.bitmapInfo
let bitsPerComponent = self.cgImage?.bitsPerComponent
let context = CGContext(data: nil, width: Int(rect.size.width), height: Int(rect.size.height), bitsPerComponent: bitsPerComponent!, bytesPerRow: 0, space: colorSpace!, bitmapInfo: (bitmapInfo?.rawValue)!)
let imageLocation = CGRect(x: padding, y: padding, width: image!.size.width, height: image!.size.height)
context?.draw(self.cgImage!, in: imageLocation)
let transparentImage = UIImage(cgImage: (context?.makeImage()?.masking(imageRef(withPadding: padding, size: rect.size))!)!)
return transparentImage
}
/**
Creates a mask that makes the outer edges transparent and everything else opaque. The size must include the entire mask (opaque part + transparent border).
- Parameter padding: The padding amount.
- Parameter size: The size of the image.
- Returns A Core Graphics Image Ref
*/
fileprivate func imageRef(withPadding padding: CGFloat, size: CGSize) -> CGImage {
let colorSpace = CGColorSpaceCreateDeviceGray()
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo().rawValue | CGImageAlphaInfo.none.rawValue)
let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
context?.setFillColor(UIColor.black.cgColor)
context?.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
context?.setFillColor(UIColor.white.cgColor)
context?.fill(CGRect(x: padding, y: padding, width: size.width - padding * 2, height: size.height - padding * 2))
let maskImageRef = context?.makeImage()
return maskImageRef!
}
/**
Creates a cropped copy of an image.
- Parameter bounds: The bounds of the rectangle inside the image.
- Returns A new image
*/
func crop(bounds: CGRect) -> UIImage? {
return UIImage(cgImage: (self.cgImage?.cropping(to: bounds)!)!,
scale: 0.0, orientation: self.imageOrientation)
}
func cropToSquare() -> UIImage? {
let size = CGSize(width: self.size.width * self.scale, height: self.size.height * self.scale)
let shortest = min(size.width, size.height)
let left: CGFloat = (size.width > shortest) ? (size.width - shortest) / 2 : 0
let top: CGFloat = (size.height > shortest) ? (size.height - shortest) / 2 : 0
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
let insetRect = rect.insetBy(dx: left, dy: top)
return crop(bounds: insetRect)
}
/**
Creates a resized copy of an image.
- Parameter size: The new size of the image.
- Parameter contentMode: The way to handle the content in the new size.
- Returns A new image
*/
func resize(toSize: CGSize, contentMode: UIImageContentMode = .scaleToFill) -> UIImage? {
let horizontalRatio = size.width / self.size.width;
let verticalRatio = size.height / self.size.height;
var ratio: CGFloat!
switch contentMode {
case .scaleToFill:
ratio = 1
case .scaleAspectFill:
ratio = max(horizontalRatio, verticalRatio)
case .scaleAspectFit:
ratio = min(horizontalRatio, verticalRatio)
}
let rect = CGRect(x: 0, y: 0, width: size.width * ratio, height: size.height * ratio)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
let context = CGContext(data: nil, width: Int(rect.size.width), height: Int(rect.size.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
let transform = CGAffineTransform.identity
context?.concatenate(transform);
context!.interpolationQuality = CGInterpolationQuality(rawValue: 3)!
context?.draw(self.cgImage!, in: rect)
let newImage = UIImage(cgImage: (context?.makeImage()!)!, scale: self.scale, orientation: self.imageOrientation)
return newImage;
}
/**
Creates a new image with rounded corners.
- Parameter cornerRadius: The corner radius.
- Returns A new image
*/
func roundCorners(cornerRadius: CGFloat) -> UIImage? {
let imageWithAlpha = applyAlpha()
if imageWithAlpha == nil {
return nil
}
UIGraphicsBeginImageContextWithOptions(size, false, 0)
let width = imageWithAlpha?.cgImage?.width
let height = imageWithAlpha?.cgImage?.height
let bits = imageWithAlpha?.cgImage?.bitsPerComponent
let colorSpace = imageWithAlpha?.cgImage?.colorSpace
let bitmapInfo = imageWithAlpha?.cgImage?.bitmapInfo
let context = CGContext(data: nil, width: width!, height: height!, bitsPerComponent: bits!, bytesPerRow: 0, space: colorSpace!, bitmapInfo: (bitmapInfo?.rawValue)!)
let rect = CGRect(x: 0, y: 0, width: CGFloat(width!)*scale, height: CGFloat(height!)*scale)
context?.beginPath()
if (cornerRadius == 0) {
context?.addRect(rect)
} else {
context?.saveGState()
context?.translateBy(x: rect.minX, y: rect.minY)
context?.scaleBy(x: cornerRadius, y: cornerRadius)
let fw = rect.size.width / cornerRadius
let fh = rect.size.height / cornerRadius
context?.move(to: CGPoint(x: fw, y: fh/2))
context?.addArc(tangent1End: CGPoint(x: fw, y: fh), tangent2End: CGPoint(x: fw/2, y: fh), radius: 1)
context?.addArc(tangent1End: CGPoint(x: 0, y: fh), tangent2End: CGPoint(x: 0, y: fh/2), radius: 1)
context?.addArc(tangent1End: CGPoint(x: 0, y: 0), tangent2End: CGPoint(x: fw/2, y: 0), radius: 1)
context?.addArc(tangent1End: CGPoint(x: fw, y: 0), tangent2End: CGPoint(x: fw, y: fh/2), radius: 1)
context?.restoreGState()
}
context?.closePath()
context?.clip()
context?.draw(imageWithAlpha!.cgImage!, in: rect)
let image = UIImage(cgImage: (context?.makeImage()!)!, scale:scale, orientation: .up)
UIGraphicsEndImageContext()
return image
}
/**
Creates a new image with rounded corners and border.
- Parameter cornerRadius: The corner radius.
- Parameter border: The size of the border.
- Parameter color: The color of the border.
- Returns A new image
*/
func roundCorners(cornerRadius: CGFloat, border: CGFloat, color: UIColor) -> UIImage? {
return roundCorners(cornerRadius: cornerRadius)?.apply(border: border, color: color)
}
/**
Creates a new circle image.
- Returns A new image
*/
func roundCornersToCircle() -> UIImage? {
let shortest = min(size.width, size.height)
return cropToSquare()?.roundCorners(cornerRadius: shortest/2)
}
/**
Creates a new circle image with a border.
- Parameter border :CGFloat The size of the border.
- Parameter color :UIColor The color of the border.
- Returns UIImage?
*/
func roundCornersToCircle(withBorder border: CGFloat, color: UIColor) -> UIImage? {
let shortest = min(size.width, size.height)
return cropToSquare()?.roundCorners(cornerRadius: shortest/2, border: border, color: color)
}
/**
Creates a new image with a border.
- Parameter border: The size of the border.
- Parameter color: The color of the border.
- Returns A new image
*/
func apply(border: CGFloat, color: UIColor) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, 0)
let width = self.cgImage?.width
let height = self.cgImage?.height
let bits = self.cgImage?.bitsPerComponent
let colorSpace = self.cgImage?.colorSpace
let bitmapInfo = self.cgImage?.bitmapInfo
let context = CGContext(data: nil, width: width!, height: height!, bitsPerComponent: bits!, bytesPerRow: 0, space: colorSpace!, bitmapInfo: (bitmapInfo?.rawValue)!)
var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
context?.setStrokeColor(red: red, green: green, blue: blue, alpha: alpha)
context?.setLineWidth(border)
let rect = CGRect(x: 0, y: 0, width: size.width*scale, height: size.height*scale)
let inset = rect.insetBy(dx: border*scale, dy: border*scale)
context?.strokeEllipse(in: inset)
context?.draw(self.cgImage!, in: inset)
let image = UIImage(cgImage: (context?.makeImage()!)!)
UIGraphicsEndImageContext()
return image
}
/**
Applies a light blur effect to the image
- Returns New image or nil
*/
func applyLightEffect() -> UIImage? {
return applyBlur(withRadius: 30, tintColor: UIColor(white: 1.0, alpha: 0.3), saturationDeltaFactor: 1.8)
}
/**
Applies a extra light blur effect to the image
- Returns New image or nil
*/
func applyExtraLightEffect() -> UIImage? {
return applyBlur(withRadius: 20, tintColor: UIColor(white: 0.97, alpha: 0.82), saturationDeltaFactor: 1.8)
}
/**
Applies a dark blur effect to the image
- Returns New image or nil
*/
func applyDarkEffect() -> UIImage? {
return applyBlur(withRadius: 20, tintColor: UIColor(white: 0.11, alpha: 0.73), saturationDeltaFactor: 1.8)
}
/**
Applies a color tint to an image
- Parameter color: The tint color
- Returns New image or nil
*/
func applyTintEffect(tintColor: UIColor) -> UIImage? {
let effectColorAlpha: CGFloat = 0.6
var effectColor = tintColor
let componentCount = tintColor.cgColor.numberOfComponents
if componentCount == 2 {
var b: CGFloat = 0
if tintColor.getWhite(&b, alpha: nil) {
effectColor = UIColor(white: b, alpha: effectColorAlpha)
}
} else {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
if tintColor.getRed(&red, green: &green, blue: &blue, alpha: nil) {
effectColor = UIColor(red: red, green: green, blue: blue, alpha: effectColorAlpha)
}
}
return applyBlur(withRadius: 10, tintColor: effectColor, saturationDeltaFactor: -1.0)
}
/**
Applies a blur to an image based on the specified radius, tint color saturation and mask image
- Parameter blurRadius: The radius of the blur.
- Parameter tintColor: The optional tint color.
- Parameter saturationDeltaFactor: The detla for saturation.
- Parameter maskImage: The optional image for masking.
- Returns New image or nil
*/
func applyBlur(withRadius blurRadius: CGFloat, tintColor: UIColor?, saturationDeltaFactor: CGFloat, maskImage: UIImage? = nil) -> UIImage? {
guard size.width > 0 && size.height > 0 && cgImage != nil else {
return nil
}
if maskImage != nil {
guard maskImage?.cgImage != nil else {
return nil
}
}
let imageRect = CGRect(origin: CGPoint.zero, size: size)
var effectImage = self
let hasBlur = blurRadius > CGFloat(FLT_EPSILON)
let hasSaturationChange = fabs(saturationDeltaFactor - 1.0) > CGFloat(FLT_EPSILON)
if (hasBlur || hasSaturationChange) {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let effectInContext = UIGraphicsGetCurrentContext()
effectInContext?.scaleBy(x: 1.0, y: -1.0)
effectInContext?.translateBy(x: 0, y: -size.height)
effectInContext?.draw(cgImage!, in: imageRect)
var effectInBuffer = vImage_Buffer(
data: effectInContext?.data,
height: UInt((effectInContext?.height)!),
width: UInt((effectInContext?.width)!),
rowBytes: (effectInContext?.bytesPerRow)!)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0);
let effectOutContext = UIGraphicsGetCurrentContext()
var effectOutBuffer = vImage_Buffer(
data: effectOutContext?.data,
height: UInt((effectOutContext?.height)!),
width: UInt((effectOutContext?.width)!),
rowBytes: (effectOutContext?.bytesPerRow)!)
if hasBlur {
let inputRadius = blurRadius * UIScreen.main.scale
let sqrtPi: CGFloat = CGFloat(sqrt(M_PI * 2.0))
var radius = UInt32(floor(inputRadius * 3.0 * sqrtPi / 4.0 + 0.5))
if radius % 2 != 1 {
radius += 1
}
let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
}
var effectImageBuffersAreSwapped = false
if hasSaturationChange {
let s: CGFloat = saturationDeltaFactor
let floatingPointSaturationMatrix: [CGFloat] = [
0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
0, 0, 0, 1
]
let divisor: CGFloat = 256
let matrixSize = floatingPointSaturationMatrix.count
var saturationMatrix = [Int16](repeating: 0, count: matrixSize)
for i: Int in 0 ..< matrixSize {
saturationMatrix[i] = Int16(round(floatingPointSaturationMatrix[i] * divisor))
}
if hasBlur {
vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, Int32(divisor), nil, nil, vImage_Flags(kvImageNoFlags))
effectImageBuffersAreSwapped = true
} else {
vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, Int32(divisor), nil, nil, vImage_Flags(kvImageNoFlags))
}
}
if !effectImageBuffersAreSwapped {
effectImage = UIGraphicsGetImageFromCurrentImageContext()!
}
UIGraphicsEndImageContext()
if effectImageBuffersAreSwapped {
effectImage = UIGraphicsGetImageFromCurrentImageContext()!
}
UIGraphicsEndImageContext()
}
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
let outputContext = UIGraphicsGetCurrentContext()
outputContext?.scaleBy(x: 1.0, y: -1.0)
outputContext?.translateBy(x: 0, y: -size.height)
outputContext?.draw(self.cgImage!, in: imageRect)
if hasBlur {
outputContext?.saveGState()
if let image = maskImage {
outputContext?.clip(to: imageRect, mask: image.cgImage!);
}
outputContext?.draw(effectImage.cgImage!, in: imageRect)
outputContext?.restoreGState()
}
if let color = tintColor {
outputContext?.saveGState()
outputContext?.setFillColor(color.cgColor)
outputContext?.fill(imageRect)
outputContext?.restoreGState()
}
let outputImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return outputImage
}
/**
Creates a new image from a URL with optional caching. If cached, the cached image is returned. Otherwise, a place holder is used until the image from web is returned by the closure.
- Parameter url: The image URL.
- Parameter placeholder: The placeholder image.
- Parameter shouldCacheImage: Weather or not we should cache the NSURL response (default: true)
- Parameter closure: Returns the image from the web the first time is fetched.
- Returns A new image
*/
class func image(fromURL url: String, placeholder: UIImage, shouldCacheImage: Bool = true, closure: @escaping (_ image: UIImage?) -> ()) -> UIImage? {
if shouldCacheImage {
if let image = UIImage.shared.object(forKey: url as AnyObject) as? UIImage {
closure(nil)
return image
}
}
let session = URLSession(configuration: URLSessionConfiguration.default)
if let nsURL = URL(string: url) {
session.dataTask(with: nsURL, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
DispatchQueue.main.async {
closure(nil)
}
}
if let data = data, let image = UIImage(data: data) {
if shouldCacheImage {
UIImage.shared.setObject(image, forKey: url as AnyObject)
}
DispatchQueue.main.async {
closure(image)
}
}
session.finishTasksAndInvalidate()
}).resume()
}
return placeholder
}
}
自定义创建轮播组件(第二步,扩展UIImageView)
import Foundation
import UIKit
import QuartzCore
public extension UIImageView {
/**
Loads an image from a URL. If cached, the cached image is returned. Otherwise, a place holder is used until the image from web is returned by the closure.
- Parameter url: The image URL.
- Parameter placeholder: The placeholder image.
- Parameter fadeIn: Weather the mage should fade in.
- Parameter closure: Returns the image from the web the first time is fetched.
- Returns A new image
*/
func imageFromURL(_ url: String, placeholder: UIImage, fadeIn: Bool = true, shouldCacheImage: Bool = true, closure: ((_ image: UIImage?) -> ())? = nil)
{
self.image = UIImage.image(fromURL: url, placeholder: placeholder, shouldCacheImage: shouldCacheImage) {
(image: UIImage?) in
if image == nil {
return
}
self.image = image
if fadeIn {
let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
self.layer.add(transition, forKey: nil)
}
closure?(image)
}
}
}
自定义轮播组件(第三步,定义轮播组件控制器)
import UIKit
protocol SliderGalleryControllerDelegate{
func galleryDataSource()->[String]
func galleryScrollerViewSize()->CGSize
}
class SliderGalleryController: UIViewController,UIScrollViewDelegate{
var delegate : SliderGalleryControllerDelegate!
let kScreenWidth = UIScreen.main.bounds.size.width
var currentIndex : Int = 0
var dataSource : [String]?
var leftImageView , middleImageView , rightImageView : UIImageView?
var scrollerView : UIScrollView?
var scrollerViewWidth : CGFloat?
var scrollerViewHeight : CGFloat?
var pageControl : UIPageControl?
var placeholderImage:UIImage!
var autoScrollTimer:Timer?
override func viewDidLoad() {
super.viewDidLoad()
let size : CGSize = self.delegate.galleryScrollerViewSize()
self.scrollerViewWidth = size.width
self.scrollerViewHeight = 180
self.dataSource = self.delegate.galleryDataSource()
self.configureScrollerView()
self.configurePlaceholder()
self.configureImageView()
self.configurePageController()
self.configureAutoScrollTimer()
self.view.backgroundColor = UIColor.black
}
func configureScrollerView(){
self.scrollerView = UIScrollView(frame: CGRect(x: 0,y: 0,
width: self.scrollerViewWidth!,
height: self.scrollerViewHeight!))
self.scrollerView?.backgroundColor = UIColor.red
self.scrollerView?.delegate = self
self.scrollerView?.contentSize = CGSize(width: self.scrollerViewWidth! * 3,
height: self.scrollerViewHeight!)
self.scrollerView?.contentOffset = CGPoint(x: self.scrollerViewWidth!, y: 0)
self.scrollerView?.isPagingEnabled = true
self.scrollerView?.bounces = false
self.view.addSubview(self.scrollerView!)
}
func configurePlaceholder(){
let font = UIFont.systemFont(ofSize: 17.0, weight: UIFont.Weight.medium)
let size = CGSize(width: self.scrollerViewWidth!, height: self.scrollerViewHeight!)
placeholderImage = UIImage(text: "图片加载中...", font:font,
color:UIColor.white, size:size)!
}
func configureImageView(){
self.leftImageView = UIImageView(frame: CGRect(x: 0, y: 0,
width: self.scrollerViewWidth!,
height: self.scrollerViewHeight!))
self.middleImageView = UIImageView(frame: CGRect(x: self.scrollerViewWidth!, y: 0,
width: self.scrollerViewWidth!,
height: self.scrollerViewHeight! ))
self.rightImageView = UIImageView(frame: CGRect(x: 2*self.scrollerViewWidth!, y: 0,
width: self.scrollerViewWidth!,
height: self.scrollerViewHeight!))
self.scrollerView?.showsHorizontalScrollIndicator = false
if(self.dataSource?.count != 0){
resetImageViewSource()
}
self.scrollerView?.addSubview(self.leftImageView!)
self.scrollerView?.addSubview(self.middleImageView!)
self.scrollerView?.addSubview(self.rightImageView!)
}
func configurePageController() {
self.pageControl = UIPageControl(frame: CGRect(x: kScreenWidth/2-60,
y: self.scrollerViewHeight! - 20, width: 120, height: 20))
self.pageControl?.numberOfPages = (self.dataSource?.count)!
self.pageControl?.isUserInteractionEnabled = false
self.view.addSubview(self.pageControl!)
}
func configureAutoScrollTimer() {
autoScrollTimer = Timer.scheduledTimer(timeInterval: 3, target: self,
selector: #selector(SliderGalleryController.letItScroll),
userInfo: nil, repeats: true)
}
@objc func letItScroll(){
let offset = CGPoint(x: 2*scrollerViewWidth!, y: 0)
self.scrollerView?.setContentOffset(offset, animated: true)
}
func resetImageViewSource() {
if self.currentIndex == 0 {
self.leftImageView?.imageFromURL(self.dataSource!.last!,
placeholder: placeholderImage)
self.middleImageView?.imageFromURL(self.dataSource!.first!,
placeholder: placeholderImage)
let rightImageIndex = (self.dataSource?.count)!>1 ? 1 : 0
self.rightImageView?.imageFromURL(self.dataSource![rightImageIndex],
placeholder: placeholderImage)
}
else if self.currentIndex == (self.dataSource?.count)! - 1 {
self.leftImageView?.imageFromURL(self.dataSource![self.currentIndex-1],
placeholder: placeholderImage)
self.middleImageView?.imageFromURL(self.dataSource!.last!,
placeholder: placeholderImage)
self.rightImageView?.imageFromURL(self.dataSource!.first!,
placeholder: placeholderImage)
}
else{
self.leftImageView?.imageFromURL(self.dataSource![self.currentIndex-1],
placeholder: placeholderImage)
self.middleImageView?.imageFromURL(self.dataSource![self.currentIndex],
placeholder: placeholderImage)
self.rightImageView?.imageFromURL(self.dataSource![self.currentIndex+1],
placeholder: placeholderImage)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.x
if(self.dataSource?.count != 0){
if(offset >= self.scrollerViewWidth!*2){
scrollView.contentOffset = CGPoint(x: self.scrollerViewWidth!, y: 0)
self.currentIndex = self.currentIndex + 1
if self.currentIndex == self.dataSource?.count {
self.currentIndex = 0
}
}
if(offset <= 0){
scrollView.contentOffset = CGPoint(x: self.scrollerViewWidth!, y: 0)
self.currentIndex = self.currentIndex - 1
if self.currentIndex == -1 {
self.currentIndex = (self.dataSource?.count)! - 1
}
}
resetImageViewSource()
self.pageControl?.currentPage = self.currentIndex
}
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
autoScrollTimer?.invalidate()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView,
willDecelerate decelerate: Bool) {
configureAutoScrollTimer()
}
func reloadData() {
self.currentIndex = 0
self.dataSource = self.delegate.galleryDataSource()
self.pageControl?.numberOfPages = (self.dataSource?.count)!
self.pageControl?.currentPage = 0
resetImageViewSource()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
第四步(开始使用)
import UIKit
import SwiftyJSON
import Alamofire
class BannerViewController: UIViewController,SliderGalleryControllerDelegate {
let screenWidth = UIScreen.main.bounds.size.width
var sliderGallery : SliderGalleryController!
var imgageData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
getImageData()
}
func galleryDataSource() -> [String] {
return imgageData
}
func galleryScrollerViewSize() -> CGSize {
return CGSize(width: screenWidth-20, height: 180)
}
@objc func handleTapAction(_ tap:UITapGestureRecognizer) -> Void {
let index = sliderGallery.currentIndex
print("宝宝你点击了\(index)张图片")
}
func initSliderGallery(){
sliderGallery = SliderGalleryController()
sliderGallery.delegate = self
sliderGallery.view.frame = CGRect(x: 10, y: 40, width: screenWidth-20, height: (screenWidth-20)/4*3)
self.addChildViewController(sliderGallery)
self.view.addSubview(sliderGallery.view)
let tap = UITapGestureRecognizer(target: self, action: #selector(BannerViewController.handleTapAction(_:)))
sliderGallery.view.addGestureRecognizer(tap)
}
func getImageData(){
let now = Date()
let timeInterval:TimeInterval = now.timeIntervalSince1970
let timeStamp = String(timeInterval)
let url = URL(string: "http://47.92.107.28:8000/static/banner.f?_=\(timeStamp)")!
Alamofire.request(url,method: .get,parameters: nil,encoding: URLEncoding.default,headers:nil).responseJSON { response
in
switch response.result.isSuccess {
case true:
if let value = response.result.value{
self.imgageData = []
let img_json = JSON(value)
let json_str = img_json.rawString()
let zhu_url = "http://47.92.107.28:8000"
for(key,item) in img_json["imgs"] {
if let img_url = item["src"].string{
self.imgageData.append(zhu_url+img_url)
}
}
let str = self.imgageData.joined()
self.initSliderGallery()
}
case false:
print(response.result.error)
UIAlertController.showAlert(message: "网络连接失败")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}