此控件搭配Kingfisher使用,你也可以自己替换成SD。继承UIControl,可以不用再去加手势,直接调用UIControl 的addTarget...方法实现点击事件
直接上代码
class ImageView: UIControl {
/// 图像类型
///
/// - none: 矩形直角
/// - round: 圆形
/// - radius: 圆角
enum AvatarType {
case none, round, radius(CGFloat)
}
var image: UIImage? {
didSet {
if image != oldValue {
setNeedsDisplay()
}
}
}
var defaultImage: UIImage? {
didSet {
if defaultImage != oldValue {
setNeedsDisplay()
}
}
}
var highlightedImage: UIImage? {
didSet {
if highlightedImage != oldValue {
setNeedsDisplay()
}
}
}
override var isHighlighted: Bool {
didSet {
setNeedsDisplay()
}
}
override var isSelected: Bool {
didSet {
setNeedsDisplay()
}
}
init(frame: CGRect, type: AvatarType = .round) {
self.type = type
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
self.type = .none
super.init(coder: aDecoder)
setup()
}
private let type: AvatarType
private var cornerRadius: CGFloat = 0
override func draw(_ rect: CGRect) {
guard bounds.width > 0, bounds.height > 0 else {
return
}
let context = UIGraphicsGetCurrentContext()
let img = UIImageView()
context?.saveGState()
defer {
context?.restoreGState()
}
if cornerRadius > 0 {
// 防止中途改变size
if case .round = type {
cornerRadius = bounds.size.width * 0.5
}
context?.addPath(UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath)
context?.clip()
}
if let image = showImage(), image.size.height > 0, image.size.width > 0, let cgImage = image.cgImage {
//ScaleAspectFill模式
let newCenter = CGPoint(x: bounds.width * 0.5, y: bounds.height * 0.5)
//哪个小按哪个缩
let scaleW = image.size.width / bounds.width
let scaleH = image.size.height / bounds.height
let scale = min(scaleW, scaleH)
let newSize = CGSize(width: image.size.width / scale, height: image.size.height / scale)
let drawRect = CGRect(x: newCenter.x - newSize.width / 2, y: newCenter.y - newSize.height / 2, width: newSize.width, height: newSize.height)
context?.draw(cgImage, in: drawRect)
}
}
}
extension ImageView {
fileprivate func setup() {
backgroundColor = UIColor.clear
layer.isGeometryFlipped = true
switch type {
case .none: cornerRadius = 0.0
case .round: cornerRadius = bounds.size.width * 0.5
case .radius(let radius): cornerRadius = radius
}
}
fileprivate func showImage() -> UIImage? {
let current = (state == .highlighted || state == .selected)
let img = image ?? defaultImage
return current ? (highlightedImage != nil) ? highlightedImage : img : img
}
}
下面是控件调用url,网络请求赋值的方法
extension ImageView {
func image(for url: String?) {
guard let url = url else { return }
let Url = URL(string: url)
image(for: Url)
}
@discardableResult
func image(for resource: Resource?,
placeholder: UIImage? = nil,
options: KingfisherOptionsInfo? = nil,
progressBlock: DownloadProgressBlock? = nil,
completionHandler: CompletionHandler? = nil) -> RetrieveImageTask {
guard let resource = resource else {
image = placeholder
setWebURL(nil)
completionHandler?(nil, nil, .none, nil)
return .empty
}
var options = KingfisherManager.shared.defaultOptions + (options ?? [])
if !options.keepCurrentImageWhileLoading {
image = placeholder
}
setWebURL(resource.downloadURL)
if shouldPreloadAllAnimation() {
options.append(.preloadAllAnimationData)
}
let task = KingfisherManager.shared.retrieveImage(with: resource, options: options, progressBlock: { receivedSize, totalSize in
guard resource.downloadURL == self.webURL else { return }
if let progressBlock = progressBlock {
progressBlock(receivedSize, totalSize)
}
}) { [weak self] image, error, cacheType, imageURL in
DispatchQueue.main.safeAsync {
guard let strongSelf = self, imageURL == strongSelf.webURL else {
completionHandler?(image, error, cacheType, imageURL)
return
}
self?.setImageTask(nil)
guard let img = image else {
completionHandler?(nil, error, cacheType, imageURL)
return
}
strongSelf.image = img
completionHandler?(img, error, cacheType, imageURL)
}
}
setImageTask(task)
return task
}
}
取消网络请求的方法
func cancelDownloadTask() {
imageTask?.cancel()
}
使用runtime在网络请求的时候为控件的属性赋值
private var lastURLKey: Void?
private var imageTaskKey: Void?
extension ImageView {
var webURL: URL? {
return objc_getAssociatedObject(self, &lastURLKey) as? URL
}
fileprivate func setWebURL(_ url: URL?) {
objc_setAssociatedObject(self, &lastURLKey, url, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
fileprivate var imageTask: RetrieveImageTask? {
return objc_getAssociatedObject(self, &imageTaskKey) as? RetrieveImageTask
}
fileprivate func setImageTask(_ task: RetrieveImageTask?) {
objc_setAssociatedObject(self, &imageTaskKey, task, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
fileprivate func shouldPreloadAllAnimation() -> Bool { return true }
}