iOS开发swift 图片轮播功能实现方法
程序员文章站
2024-01-13 18:36:22
自定义创建轮播(第一步)
import foundation
import uikit
import quartzcore
import coregraphics
import acce...
自定义创建轮播(第一步)
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! { struct staticsharedcache { static var shared: nscache? = 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: https://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: https://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 pisor: 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] * pisor)) } if hasblur { vimagematrixmultiply_argb8888(&effectoutbuffer, &effectinbuffer, saturationmatrix, int32(pisor), nil, nil, vimage_flags(kvimagenoflags)) effectimagebuffersareswapped = true } else { vimagematrixmultiply_argb8888(&effectinbuffer, &effectoutbuffer, saturationmatrix, int32(pisor), 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 = size.height //获取数据 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 { //获取屏幕的宽度 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 } //图片轮播组件接口的方法,获取内部scrollview的尺寸 func galleryscrollerviewsize() -> cgsize { return cgsize(width: screenwidth-20, height: (screenwidth-20)/4*3) } //点击事件响应 @objc func handletapaction(_ tap:uitapgesturerecognizer) -> void { //获取图片的索引值 let index = slidergallery.currentindex print("宝宝你点击了\(index)张图片") } //初始化轮播组件 func initslidergallery(){ //初始化图片轮播组件 slidergallery = slidergallerycontroller() //设置 slidergallerycontrollerdelegate 接口的监听事件 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: "https://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 = "https://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. } }