美文网首页
iOS 简单放大(视图)图片工具

iOS 简单放大(视图)图片工具

作者: 姬歌 | 来源:发表于2018-07-12 19:23 被阅读218次

    之前用过不错的第三方的放大图片,可以一次浏览多张图片的工具,但懒得找了,反正闲着自己花了大半天时间,做了下面这么个简陋的东西。

    • 暂时只支持每次放大(屏幕大小)一个视图/图片,且不能手动缩放。
    • 如果是UIImageView,且包含图片,则自动计算长宽比例,否则按原始frame比例放大。
    • 最恶心的是处理约束关系!因为需要把原始视图从其superView中移除,会破坏原有的约束关系,所以需要考虑约束的各种可能!因为感觉有点复杂,所以暂时没有做一次浏览多张图片功能。
    • 注意,如果如果被放大的视图showView内部有subviews,那么其subviews的布局是未知的(不符合期望的)
    zoomOut_infinite.gif

    Demo地址:https://github.com/jacksgithub99/JacksZoomOut.git
    使用方法:

    @IBAction func btnClick(_ sender: UIButton) {
            
            if index == 0 {
                JKZoomViewsManager.sharedInstance.show(zoomView: greenView)
            }else if index == 1 {
                JKZoomViewsManager.sharedInstance.show(zoomView: innerView)
            }else if index == 2 {
                JKZoomViewsManager.sharedInstance.show(zoomView: redView)
            }else if index == 3 {
                JKZoomViewsManager.sharedInstance.show(zoomView: frameView)
                index = -1
            }
            index += 1
            
        }
    
    
    
    //Hello This is Jack!
    
    import UIKit
    
    /*
     * 实现图片(任意View)的放大查看。
     1.可以是约束布局的View,也可以是坐标(frame)布局的View
     2.通过superView.insertSubview(zView, belowSubview: self.replaceView) 和 superView.insertSubview(replaceView, aboveSubview: zView)
     保证查看结束后,zView可以恢复原来在父视图中的对应层级(与兄弟视图之间的层级)
     3.目前只实现了一次查看一张图片(View)。
     4.~~~~~~~~~~~~~
     注意,如果如果被放大的视图showView内部有subviews,那么其subviews的布局是未知的(不符合期望的)
     ~~~~~~~~~~~~~~~
     */
    
    typealias jacksZoomManagerCallback = () -> Void
    class JKZoomViewsManager: NSObject {
        //图片(视图缩放)
        
        static let zoomViewsManager = JKZoomViewsManager()
        
        class var sharedInstance: JKZoomViewsManager {
            return zoomViewsManager
        }
        
        //contentView将被添加到keyWindow中全屏展示。把需要显示的showView显示在其上
        private let contentView: UIView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
        
        private var showView: UIView?   //show(zoomView: UIView)传入的视图,即需要放大的视图
        private let replaceView = UIView()      //占位用的空白View(当superView对showView有约束时,移除showView之后,需要另一个视图填补其superView缺失的约束)
        private var savedConstraints: [NSLayoutConstraint]?
        private var isShowing: Bool = false
        private var lastOperationDate = Date(timeIntervalSinceNow: -2)
        private let lockInterval = 0.4  //防止一次操作未完成就进行另一次操作!(值越大,锁定时间越久,越安全。必须大于执行动画所需的0.25,且要考虑其他运算的时间、卡顿的时间等)
        
        var zoomOutStart: jacksZoomManagerCallback?
        var zoomOutEnd: jacksZoomManagerCallback?
        
        override init() {
            super.init()
            
            replaceView.backgroundColor = UIColor.clear
            contentView.backgroundColor = UIColor.black
            contentView.accessibilityIdentifier = "jk_zoom_content_id"  //单元测试UITest用到
            
            let tap = UITapGestureRecognizer(target: self, action: #selector(contentViewTap))
            contentView.addGestureRecognizer(tap)
        }
        
        func show(zoomView: UIView) {
            
            if showView != nil {
                return
            }
            
            if lastOperationDate.timeIntervalSinceNow > -lockInterval {
                return
            }
            
            if isShowing {
                return
            }else {
                isShowing = true
            }
            
            guard let keyWindow = UIApplication.shared.keyWindow else {
                return
            }
            
            guard let superView = zoomView.superview else {
                return
            }
            
            if zoomOutStart != nil {
                zoomOutStart!()
            }
            
            showView = zoomView
            
            savedConstraints = superView.constraints
            
            replace(zView: zoomView)        //占位
            
            keyWindow.addSubview(contentView)
            contentView.isHidden = true
            
            var viewW = zoomView.bounds.size.width
            var viewH = zoomView.bounds.size.height
            
            let screenW = keyWindow.bounds.size.width
            let screenH = keyWindow.bounds.size.height
            
            var finalViewW: CGFloat = 0
            var finalViewH: CGFloat = 0
            
            if let imgView = showView as? UIImageView, let image = imgView.image {
                let imageSize = image.size
                viewW = imageSize.width
                viewH = imageSize.height
            }
            
            if viewW/viewH > screenW/screenH  {
                finalViewW = screenW
                finalViewH = viewH*finalViewW/viewW
            }else{
                finalViewH = screenH// - 40.0 //status bar height * 2
                finalViewW = viewW*finalViewH/viewH
            }
            
            let finalX = (screenW - finalViewW)/2.0
            let finalY = (screenH - finalViewH)/2.0
            
            let orgFrame = superView.convert(zoomView.frame, to: keyWindow)//为了动画效果添加的额外代码(从'原始'位置放大!)
            contentView.addSubview(zoomView)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {//必须有延时!!否则无法正确渲染。(约束会覆盖frame!)
                zoomView.frame = orgFrame//为了动画效果添加的额外代码(从'原始'位置放大!)
                self.contentView.isHidden = false
                UIView.animate(withDuration: 0.2, animations: {
                    zoomView.frame = CGRect(x: finalX, y: finalY, width: finalViewW, height: finalViewH)
                })
            }
            
            lastOperationDate = Date()
        }
        
        func hide() {
            
            if lastOperationDate.timeIntervalSinceNow > -lockInterval {
                return
            }
            
            guard let keyWindow = UIApplication.shared.keyWindow else {
                return
            }
            
            guard let zView = showView else {
                return
            }
            
            guard let superView = replaceView.superview else {
                return
            }
            
            /**********额外动画效果**********/
            let orgFrame = superView.convert(self.replaceView.frame, to: keyWindow)
            UIView.animate(withDuration: 0.2, animations: {
                zView.frame = orgFrame
            })
            /**********额外动画效果**********/
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {//动画结束后
                
                superView.insertSubview(zView, belowSubview: self.replaceView) //注意,replaceView被加在showView的前面!见replace()
                
                zView.frame = self.replaceView.frame
                
                for constraint in self.savedConstraints! {
                    
                    var isZoomViewConstraint = false
                    if let first = constraint.firstItem, first.hash == zView.hash {
                        isZoomViewConstraint = true
                    }else if let sencond = constraint.secondItem, sencond.hash == zView.hash {
                        isZoomViewConstraint = true
                    }
                    if !isZoomViewConstraint {
                        continue
                    }
                    
                    if superView.constraints.contains(constraint) {
                        //不可能的事情!
                    }else {
                        superView.addConstraint(constraint)
                    }
                }
                
                self.replaceView.removeFromSuperview()
                self.contentView.removeFromSuperview()
                
                self.showView = nil
                
                self.isShowing = false
                self.lastOperationDate = Date()
                
                if self.zoomOutEnd != nil {
                    self.zoomOutEnd!()
                }
            }
        }
        
        
        //用一个空白View替换要放大的zView,保证zView父视图的约束没有被破坏!
        private func replace(zView: UIView) {
            guard let superView = zView.superview else {
                return
            }
            superView.insertSubview(replaceView, aboveSubview: zView)   //注意,replaceView被加在showView的前面!见hide()
            
            //如果没有这句,非约束、纯frame的情况会崩溃(因为replaceView初始化为UIView(),没有frame;会导致1、hide()之后zoomView不能显示,2、第二次执行show()内的zoomView.frame = CGRect(x: finalX, y: finalY, width: finalViewW, height: finalViewH)会崩溃)
            replaceView.frame = zView.frame
            
            for constraint in savedConstraints! {
                
                var isZoomViewConstraint = false
                if let first = constraint.firstItem, first.hash == zView.hash {
                    isZoomViewConstraint = true
                }else if let sencond = constraint.secondItem, sencond.hash == zView.hash {
                    isZoomViewConstraint = true
                }
                if !isZoomViewConstraint {
                    continue
                }
                
                var firstItem: Any!
                var secondItem: Any?
                
                if let first = constraint.firstItem, first.hash == zView.hash {
                    firstItem = replaceView
                    secondItem = constraint.secondItem
                }else if let sencond = constraint.secondItem, sencond.hash == zView.hash {
                    firstItem = constraint.firstItem
                    secondItem = replaceView
                }
                
                let cpConstraint = NSLayoutConstraint(item: firstItem, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: secondItem, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant)
                superView.addConstraint(cpConstraint)
            }
            
            let cWidth = NSLayoutConstraint(item: replaceView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: zView.frame.size.width)
            let cHeight = NSLayoutConstraint(item: replaceView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: zView.frame.size.height)
            
            replaceView.addConstraint(cWidth)
            replaceView.addConstraint(cHeight)
        }
        
        @objc private func contentViewTap() {
            hide()
        }
        
    }
    
    
    
    
    

    相关文章

      网友评论

          本文标题:iOS 简单放大(视图)图片工具

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