自定义创建轮播组件(第一步)
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!
}
// MARK: Image from solid color
/**
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()
}
// MARK: Image from gradient colors
/**
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)
// Create gradient
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)!
}
// Apply gradient
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!;
}
// MARK: Image with Text
/**
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()
}
// MARK: Image from UIView
/**
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.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
self.init(cgImage:(UIGraphicsGetImageFromCurrentImageContext()?.cgImage!)!)
UIGraphicsEndImageContext()
}
// MARK: Image with Radial Gradient
// Radial background originally from: http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_shadings/dq_shadings.html
/**
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)
// Normalize the 0-1 ranged inputs to the width of the image
let aCenter = CGPoint(x: radialGradientCenter.x * size.width, y: radialGradientCenter.y * size.height)
let aRadius = CGFloat(min(size.width, size.height)) * CGFloat(radius)
// Draw it
UIGraphicsGetCurrentContext()?.drawRadialGradient(gradient!, startCenter: aCenter, startRadius: 0, endCenter: aCenter, endRadius: aRadius, options: CGGradientDrawingOptions.drawsAfterEndLocation)
self.init(cgImage:(UIGraphicsGetImageFromCurrentImageContext()?.cgImage!)!)
// Clean up
UIGraphicsEndImageContext()
}
// MARK: Alpha
/**
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
// The bitsPerComponent and bitmapInfo values are hard-coded to prevent an "unsupported parameter combination" error
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)
// Draw the image into the context and retrieve the new image, which will now have an alpha layer
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? {
// If the image does not have an alpha layer, add one
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)
// Build a context that's the same dimensions as the new size
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)!)
// Draw the image in the center of the context, leaving a gap around the edges
let imageLocation = CGRect(x: padding, y: padding, width: image!.size.width, height: image!.size.height)
context?.draw(self.cgImage!, in: imageLocation)
// Create a mask to make the border transparent, and combine it with the image
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 {
// Build a context that's the same dimensions as the new size
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)
// Start with a mask that's entirely transparent
context?.setFillColor(UIColor.black.cgColor)
context?.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
// Make the inner part (within the border) opaque
context?.setFillColor(UIColor.white.cgColor)
context?.fill(CGRect(x: padding, y: padding, width: size.width - padding * 2, height: size.height - padding * 2))
// Get an image of the context
let maskImageRef = context?.makeImage()
return maskImageRef!
}
// MARK: Crop
/**
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)
}
// MARK: Resize
/**
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)
// Fix for a colorspace / transparency issue that affects some types of
// images. See here: http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/comment-page-2/#comment-39951
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
// Rotate and/or flip the image if required by its orientation
context?.concatenate(transform);
// Set the quality level to use when rescaling
context!.interpolationQuality = CGInterpolationQuality(rawValue: 3)!
//CGContextSetInterpolationQuality(context, CGInterpolationQuality(kCGInterpolationHigh.value))
// Draw into the context; this scales the image
context?.draw(self.cgImage!, in: rect)
// Get the resized image from the context and a UIImage
let newImage = UIImage(cgImage: (context?.makeImage()!)!, scale: self.scale, orientation: self.imageOrientation)
return newImage;
}
// MARK: Corner Radius
/**
Creates a new image with rounded corners.
- Parameter cornerRadius: The corner radius.
- Returns A new image
*/
func roundCorners(cornerRadius: CGFloat) -> UIImage? {
// If the image does not have an alpha layer, add one
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)
}
// MARK: Border
/**
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
}
// MARK: Image Effects
/**
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 // force radius to be odd so that the three box-blur methodology works.
}
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()
}
// Set up output context.
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
let outputContext = UIGraphicsGetCurrentContext()
outputContext?.scaleBy(x: 1.0, y: -1.0)
outputContext?.translateBy(x: 0, y: -size.height)
// Draw base image.
outputContext?.draw(self.cgImage!, in: imageRect)
// Draw effect image.
if hasBlur {
outputContext?.saveGState()
if let image = maskImage {
outputContext?.clip(to: imageRect, mask: image.cgImage!);
}
outputContext?.draw(effectImage.cgImage!, in: imageRect)
outputContext?.restoreGState()
}
// Add in color tint.
if let color = tintColor {
outputContext?.saveGState()
outputContext?.setFillColor(color.cgColor)
outputContext?.fill(imageRect)
outputContext?.restoreGState()
}
// Output image is ready.
let outputImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return outputImage
}
// MARK: Image From URL
/**
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? {
// From Cache
if shouldCacheImage {
if let image = UIImage.shared.object(forKey: url as AnyObject) as? UIImage {
closure(nil)
return image
}
}
// Fetch 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)
}
}
}
自定义轮播组件(第三步,定义轮播组件控制器)
//
// SliderGalleryController.swift
// hangge_1314
//
// Created by hangge on 2018/2/5.
// 图片轮播的Controller
//
import UIKit
//定义图片轮播组件的接口
protocol SliderGalleryControllerDelegate{
//获取数据源
func galleryDataSource()->[String]
//获取内部scrollerView的宽高尺寸
func galleryScrollerViewSize()->CGSize
}
//图片轮播组件控制器
class SliderGalleryController: UIViewController,UIScrollViewDelegate{
//接口对象
var delegate : SliderGalleryControllerDelegate!
//屏幕宽度
let kScreenWidth = UIScreen.main.bounds.size.width
//当前展示的图片索引
var currentIndex : Int = 0
//数据源
var dataSource : [String]?
//用于轮播的左中右三个image(不管几张图片都是这三个imageView交替使用)
var leftImageView , middleImageView , rightImageView : UIImageView?
//放置imageView的滚动视图
var scrollerView : UIScrollView?
//scrollView的宽和高
var scrollerViewWidth : CGFloat?
var scrollerViewHeight : CGFloat?
//页控制器(小圆点)
var pageControl : UIPageControl?
//加载指示符(用来当iamgeView还没将图片显示出来时,显示的图片)
var placeholderImage:UIImage!
//自动滚动计时器
var autoScrollTimer:Timer?
override func viewDidLoad() {
super.viewDidLoad()
//获取并设置scrollerView尺寸
let size : CGSize = self.delegate.galleryScrollerViewSize()
//设置轮播图片的宽度
self.scrollerViewWidth = size.width
//设置轮播图片的高度
self.scrollerViewHeight = 180
//获取数据
self.dataSource = self.delegate.galleryDataSource()
//设置scrollerView
self.configureScrollerView()
//设置加载指示图片
self.configurePlaceholder()
//设置imageView
self.configureImageView()
//设置页控制器
self.configurePageController()
//设置自动滚动计时器
self.configureAutoScrollTimer()
self.view.backgroundColor = UIColor.black
}
//设置scrollerView
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!)
//滚动视图内容区域向左偏移一个view的宽度
self.scrollerView?.contentOffset = CGPoint(x: self.scrollerViewWidth!, y: 0)
self.scrollerView?.isPagingEnabled = true
self.scrollerView?.bounces = false
self.view.addSubview(self.scrollerView!)
}
//设置加载指示图片(图片还未加载出来时显示的)
func configurePlaceholder(){
//这里我使用ImageHelper将文字转换成图片,作为加载指示符
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)!
}
//设置imageView
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
//设置初始时左中右三个imageView的图片(分别时数据源中最后一张,第一张,第二张图片)
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)
}
//每当滚动后重新设置各个imageView的图片
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)
}
}
//scrollView滚动完毕后触发
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)
//视图索引+1
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)
//视图索引-1
self.currentIndex = self.currentIndex - 1
if self.currentIndex == -1 {
self.currentIndex = (self.dataSource?.count)! - 1
}
}
//重新设置各个imageView的图片
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
//重新设置各个imageView的图片
resetImageViewSource()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
第四步(开始使用)
//
// BannerViewController.swift
// iosTest
//
// Created by 陕西帮你电子科技有限公司 on 2018/5/4.
// Copyright © 2018年 陕西帮你电子科技有限公司. All rights reserved.
// 实现图片轮播
//
import UIKit
import SwiftyJSON
import Alamofire
//实现SliderGalleryControllerDelegate接口
class BannerViewController: UIViewController,SliderGalleryControllerDelegate {
//顶部工具栏
@IBOutlet var mytoolbar: UIToolbar!
//获取屏幕的宽度
let screenWidth = UIScreen.main.bounds.size.width
//自定义的图片轮播的组件
var sliderGallery : SliderGalleryController!
//图片轮播的数据源(String类型的数组)
var imgageData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//请求服务获取轮播图片
getImageData()
}
//图片轮播组件接口获取数据源的方法
func galleryDataSource() -> [String] {
return imgageData
}
//图片轮播组件接口的方法,设置ScrollerView的尺寸
func galleryScrollerViewSize() -> CGSize {
return CGSize(width: screenWidth-20, height: 180)
}
//点击事件响应
@objc func handleTapAction(_ tap:UITapGestureRecognizer) -> Void {
//获取图片的索引值
let index = sliderGallery.currentIndex
print("宝宝你点击了\(index)张图片")
}
//初始化轮播组件
func initSliderGallery() -> Void {
//顶部导航栏的高度
let tolbar_height = mytoolbar.frame.size.height
//总view的高度
//let zongview_height = zongView.frame.size.height
//初始化图片轮播组件对象
sliderGallery = SliderGalleryController()
//设置 SliderGalleryController 接口的监听事件
sliderGallery.delegate = self
//获取状态栏的高度(iosX和其它型号的主要区别就是状态栏的高度,所以一定要加上状态栏的高度)
let state_height = UIApplication.shared.statusBarFrame.height
let slider_height = tolbar_height + state_height
//设置轮播图片的位置
sliderGallery.view.frame = CGRect(x: 10, y: slider_height, width: screenWidth-20, height: 160)
//将图片轮播的Controller添加到当前Controller中
self.addChildViewController(sliderGallery)
//将图片轮播的view添加到当前视图中
self.view.addSubview(sliderGallery.view)
//添加轮播组件的点击事件
let tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.bannerClickAction(_:)))
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 = []
//获取返回的值,转为json对象
let img_json = JSON(value)
//json转字符串
let json_str = img_json.rawString()
let zhu_url = "http://47.92.107.28:8000"
//遍历json数据
for(key,item) in img_json["imgs"] {
//print("src的值:\(item["src"])")
//如果取得的 src 的值为 String类型的话就添加到数组中
if let img_url = item["src"].string{
//将图片路径添加到数组中
self.imgageData.append(zhu_url+img_url)
}
}
let str = self.imgageData.joined()
//print("请求到返回的数据\(json_str)")
//初始化轮播组件
self.initSliderGallery()
}
case false:
print(response.result.error)
UIAlertController.showAlert(message: "网络连接失败")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}