美文网首页
高仿QQ讨论组多图像效果

高仿QQ讨论组多图像效果

作者: 恋_时光 | 来源:发表于2018-04-11 11:08 被阅读0次

    最近UI出了一个效果图, 多人图像展示效果和qq讨论组一样, 循环且部分重叠的效果。在网上搜了一下,其效果都不理想, 就自己写了一个。

    效果图:


    屏幕快照 2018-04-11 11.05.28.png

    思路如下:

    图片的边缘裁剪一个弧形, 然后旋转相应角度,算出每个图标的位置, 最后将得到的多个图像绘制在一起,得到一张图像。

    代码也很简单, 直接上代码吧!

    import Foundation
    import UIKit
    
    struct YQ {
        
    }
    
    extension YQ {
        struct CircleImage {
            let maxCount = 4
            var images: [UIImage]?
            var size: CGFloat?
            var boardWidth: CGFloat = 3
            var boardColor: UIColor = UIColor.white
            var crossWidth: CGFloat = 20
            var direction: Direction = .clockwise
            
            init(images aImages: [UIImage]?, aSize: CGFloat?) {
                images = aImages
                size = aSize
            }
            
            private func subImageRect(at index: Int) -> CGRect? {
                guard let size = size else {
                    return nil
                }
                
                let count = min(images?.count ?? 0, maxCount)
                guard count > 0 else {
                    return nil
                }
                
                guard index >= 0 && index < count else {
                    return nil
                }
                
                if count == 1 {
                    return CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: size, height: size))
                } else if count == 2 {
                    let subSize = size / 2 + crossWidth / 2
                    let y = (size - subSize) / 2
                    switch index {
                    case 0:
                        return CGRect.init(origin: CGPoint.init(x: 0, y: y), size: CGSize.init(width: subSize, height: subSize))
                    case 1:
                        let x = (size - crossWidth) / 2
                        return CGRect.init(origin: CGPoint.init(x: x, y: y), size: CGSize.init(width: subSize, height: subSize))
                    default:
                        break
                    }
                }else if count == 3 {
                    let subSize = (2 * CGFloat(sqrtf(3)) - 3) * size + (1 / (2 + CGFloat(sqrtf(3)))) * crossWidth * 2
                    var original = CGPoint.zero
                    switch index {
                    case 0:
                        original = CGPoint.init(x: (size - subSize) / 2, y: 0)
                    case 1:
                        original = CGPoint.init(x: (CGFloat(sqrtf(3)) + 2) / 4 * size - (2 + CGFloat(sqrtf(3))) / 2 * subSize / 2, y: 0.75 * size - 1.5 * subSize / 2)
                    case 2:
                        original = CGPoint.init(x:(2 - CGFloat(sqrtf(3))) / 4 * size + (CGFloat(sqrtf(3)) - 2) / 2 * subSize / 2 , y: 0.75 * size - 1.5 * subSize / 2)
                    default:
                        return nil
                    }
                    return CGRect.init(origin: original, size: CGSize.init(width: subSize, height: subSize))
                }else if count == 4 {
                    let subSize = size / 2 + crossWidth / 2
                    let behindO = (size - crossWidth) / 2
                    switch index {
                    case 0:
                        return CGRect.init(x: 0, y: 0, width: subSize, height: subSize)
                    case 1:
                        return CGRect.init(x: behindO, y: 0, width: subSize, height: subSize)
                    case 2:
                        return CGRect.init(x: behindO, y: behindO, width: subSize, height: subSize)
                    case 3:
                        return CGRect.init(x: 0, y: behindO, width: subSize, height: subSize)
                    default:
                        break
                    }
                }
                return nil
            }
            
            private func subImageAngleClockwise(at index: Int) -> SubAngle?{
                guard let rect = subImageRect(at: index) else {
                    return nil
                }
                
                let angle = acos(1 - crossWidth / rect.width)
                let start = -angle
                let end = angle
                
                let count = min(images!.count, maxCount)
                if count == 1 {
                    return SubAngle.init(start: 0, end: CGFloat.pi * 2, rotate: 0)
                } else if count == 2 {
                    switch index {
                    case 0:
                        return SubAngle.init(start: 0, end: CGFloat.pi * 2, rotate: 0)
                    case 1:
                        return SubAngle.init(start: start, end: end, rotate: CGFloat.pi)
                    default:
                        break
                    }
                } else if count == 3 {
                    var rote: CGFloat = 0
                    switch index {
                    case 0:
                        rote = -CGFloat.pi * 2 / 3
                    case 1:
                        rote = CGFloat.pi * 2 / 3
                    case 2:
                        rote = 0
                    default:
                        return nil
                    }
                    return SubAngle.init(start: start, end: end, rotate: rote)
                } else if count == 4 {
                    var rote: CGFloat = 0
                    switch index {
                    case 0:
                        rote = -CGFloat.pi / 2
                    case 1:
                        rote = CGFloat.pi
                    case 2:
                        rote = CGFloat.pi / 2
                    case 3:
                        rote = 0
                    default:
                        return nil
                    }
                    return SubAngle.init(start: start, end: end, rotate: rote)
                }
                return nil
            }
            
            
            private func subImageAngleClockwise(at index: Int, direction: Direction) -> SubAngle? {
                if direction == .clockwise {
                    return subImageAngleClockwise(at: index)
                }
                
                // 暂时只支持顺时针
                return nil
            }
            
            fileprivate func subImage(at index: Int) -> UIImage? {
                guard let rect = subImageRect(at: index) else {
                    return nil
                }
                
                guard let angle = subImageAngleClockwise(at: index, direction: direction) else {
                    return nil
                }
                
                let size = rect.height
                let radius = size / 2
                
                UIGraphicsBeginImageContextWithOptions(rect.size, false, 1)
                let context = UIGraphicsGetCurrentContext()
                
                context?.saveGState()
                context?.translateBy(x: radius, y: radius)
                context?.rotate(by: -angle.rotate)
                
                context?.addArc(center: CGPoint.init(x: 0, y: 0), radius: radius, startAngle: angle.start, endAngle: angle.end, clockwise: true)
                if angle.start != 0 {
                    context?.addArc(center: CGPoint.init(x: size * 2 - crossWidth - radius * 2, y: 0), radius: radius, startAngle: angle.start + CGFloat.pi, endAngle: angle.end + CGFloat.pi, clockwise: false)
                }
                context?.clip()
                
                context?.rotate(by: angle.rotate)
                let image = images![index]
                image.draw(in: CGRect.init(origin: CGPoint.init(x: -radius, y: -radius), size: rect.size))
                
                context?.rotate(by: -angle.rotate)
                context?.addArc(center: CGPoint.init(x: 0, y: 0), radius: radius - boardWidth / 2, startAngle: angle.start, endAngle: angle.end, clockwise: true)
                if angle.start != 0 {
                    context?.addArc(center: CGPoint.init(x: size * 2 - crossWidth - radius * 2, y: 0), radius: radius - boardWidth / 2, startAngle: angle.start + CGFloat.pi, endAngle: angle.end + CGFloat.pi, clockwise: false)
                }
                
                context?.setStrokeColor(boardColor.cgColor)
                context?.setLineWidth(boardWidth)
                context?.strokePath()
                
                context?.restoreGState()
                
                let resultImage = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                
                return resultImage
            }
            
            var circleImage: UIImage?{
                guard let images = images, !images.isEmpty else {
                    return nil
                }
                
                guard let size = size, size != 0 else {
                    return nil
                }
                
                UIGraphicsBeginImageContextWithOptions(CGSize.init(width: size, height: size), false, 1)
                for index in 0...min(maxCount, images.count) {
                    let image = subImage(at: index)
                    image?.draw(in: subImageRect(at: index)!)
                }
                let resultImage = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                
                return resultImage
            }
            
        }
    }
    
    extension YQ.CircleImage {
        enum Direction {
            case clockwise      // 顺时针
            case anticlockwise  // 逆时针
        }
        
        private struct SubAngle {
            var start: CGFloat = 0
            var end: CGFloat = 0
            var rotate: CGFloat = 0
        }
    }
    

    使用:

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            
            let image = #imageLiteral(resourceName: "img.jpg")
            let size: CGFloat = 120
            let screenW = UIScreen.main.bounds.width
            let space = (screenW - (2 * size)) / 3
            
            var images = [image]
            var cirImage = YQ.CircleImage.init(images: images, aSize: size * UIScreen.main.scale)
            var imageView = UIImageView.init(frame: CGRect.init(x:space , y: 50, width: size, height: size))
            imageView.image = cirImage.circleImage
            view.addSubview(imageView)
            
            images.append(image)
            cirImage = YQ.CircleImage.init(images: images, aSize: size * UIScreen.main.scale)
            imageView = UIImageView.init(frame: CGRect.init(x:space * 2 + size , y: 50, width: size, height: size))
            imageView.image = cirImage.circleImage
            view.addSubview(imageView)
            
            images.append(image)
            cirImage = YQ.CircleImage.init(images: images, aSize: size * UIScreen.main.scale)
            imageView = UIImageView.init(frame: CGRect.init(x:space , y: 100 + size, width: size, height: size))
            imageView.image = cirImage.circleImage
            view.addSubview(imageView)
            
            images.append(image)
            cirImage = YQ.CircleImage.init(images: images, aSize: size * UIScreen.main.scale)
            imageView = UIImageView.init(frame: CGRect.init(x:space * 2 + size , y: 100 + size, width: size, height: size))
            imageView.image = cirImage.circleImage
            view.addSubview(imageView)
            
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    
    }
    

    如果你不想了解如何实现的, 拷贝代码即可使用啦

    暂时只支持4个头像, 要支持更多的图像, 按相同的思路扩展即可!

    相关文章

      网友评论

          本文标题:高仿QQ讨论组多图像效果

          本文链接:https://www.haomeiwen.com/subject/mqjahftx.html