大圖顯示內(nèi)存優(yōu)化

背景:最近做一個(gè)游戲化的項(xiàng)目病曾,項(xiàng)目中會(huì)加載許多比較大的場(chǎng)景圖蹬刷。經(jīng)過(guò)測(cè)試一張9000*7000高清的png場(chǎng)景圖加載到內(nèi)存會(huì)消耗200M以上的內(nèi)存空間,就這一點(diǎn)就是開(kāi)發(fā)所不可接受的。還有什么加載時(shí)間長(zhǎng)鳄虱、卡頓等問(wèn)題就不說(shuō)了。

接下來(lái)介紹一下我的優(yōu)化歷程:
1凭峡、當(dāng)時(shí)趕項(xiàng)目進(jìn)度拙已,我就直接把圖片等比壓縮成4000*3111像素,內(nèi)存降到了50M摧冀,也就暫且接受了倍踪。
缺點(diǎn):大家都清楚了,圖片清晰度得不到保證了索昂,還需要進(jìn)行縮放建车。

2、我直接交UI幫我把PNG圖片轉(zhuǎn)換成了JPG椒惨,這樣圖片大小得到了縮減缤至,而且加載到內(nèi)存中也消耗很少的內(nèi)存資源。對(duì)于圖片透明度等無(wú)要求的需求框产,這樣也是一種不錯(cuò)的方式凄杯。
缺點(diǎn):不適合需要保留圖片透明度的場(chǎng)景。

3秉宿、我把大圖用PS拆分成了54張小圖戒突。在scrollViewDidScroll代理方法中計(jì)算當(dāng)前應(yīng)該顯示的圖片,然后手動(dòng)釋放掉不需要顯示的圖片描睦。這種方式內(nèi)存消耗確實(shí)非常小膊存,而且加載的是PNG圖片。
缺點(diǎn):滑動(dòng)時(shí)scrollViewDidScroll方法調(diào)用非常頻繁忱叭,所以會(huì)非常頻繁的遍歷這些圖片哪些會(huì)顯示隔崎,哪些需要釋放,頻繁的讀寫文件也帶來(lái)了性能問(wèn)題韵丑。

注:以上三種方式均通過(guò)UIImage.init(contentsOfFile: filePath)方式加載圖片(保證內(nèi)存可以及時(shí)的釋放掉)

相關(guān)代碼如下:

let deviceWidth: CGFloat = UIScreen.main.bounds.width
let deviceHeight: CGFloat = UIScreen.main.bounds.height
func initFrameView() {
        self.scrollView.frame = CGRect(x: 0, y: 0, width: deviceWidth, height: deviceHeight)
        //設(shè)置滑動(dòng)范圍
        self.scrollView.contentSize = CGSize(width: 9514, height: 6388)
        //設(shè)置最大縮放比例
        self.scrollView.maximumZoomScale = 1.0
        //設(shè)置最小縮放比例
        self.scrollView.minimumZoomScale = 0.3
        //關(guān)閉縮放反彈
        self.scrollView.bouncesZoom = false
        //關(guān)閉遇邊框反彈
        self.scrollView.bounces = false
        self.scrollView.delegate = self
        self.view.addSubview(self.scrollView)
        
        imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 9514, height: 6388))
        self.scrollView.addSubview(imageView)
        
        for i in 0..<54 { //拆分成小圖片的張數(shù)
            let imageV = UIImageView.init(frame: CGRect(x: CGFloat(i%6)*imageWidth, y: CGFloat(i/9)*imageHeight, width: imageWidth, height: imageHeight))
            imageV.tag = i+1
            imageView.addSubview(imageV)
        }
    }

UIScrollViewDelegate的代理方法:

func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView //縮放的View
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let views = self.imageView.subviews //取出UIImageView上的所有小塊UIimageView
        for view in views {
            if view is UIImageView {
                //通過(guò)該方法判斷這個(gè)View是否在UIScrollView的當(dāng)前可視范圍內(nèi)
                let contain = self.calculateFrame(view: view)
                let imageV = view as! UIImageView
                if contain == true { //如果在就顯示出來(lái)
                    //根據(jù)該View的tag值找到它對(duì)應(yīng)該顯示的圖片進(jìn)行顯示
                    self.imageOfFile(tag: imageV.tag, imageV: imageV)
                } else { //不在就釋放掉
                    imageV.image = nil
                }
            }
        }
    }
func imageOfFile(tag:Int,imageV:UIImageView) {
        DispatchQueue.global().async { //在子線程中進(jìn)行文件的讀取操作
            let filePath = Bundle.main.path(forResource: String.init(format: "image_%d",tag), ofType: "jpg")!
            DispatchQueue.main.async { //在主線程中進(jìn)行顯示操作
                imageV.image = UIImage.init(contentsOfFile: filePath)
            }
        }
    }
    
    //判斷該view是否在scrollView的可視范圍內(nèi)
    func calculateFrame(view:UIView) -> Bool {
        let scrollX = self.scrollView.contentOffset.x
        let scrollY = self.scrollView.contentOffset.y
        let scale = self.scrollView.zoomScale //scrollView當(dāng)前的縮放系數(shù)
        
        if fabsf(Float((scrollX+deviceWidth/2) - view.center.x*scale)) <= Float(view.width/2*scale + deviceWidth/2) &&
            fabsf(Float((scrollY+deviceHeight/2)-view.center.y*scale)) <= Float(view.height/2*scale + deviceHeight/2) {
            return true
        }
        return false
    }

4爵卒、最后發(fā)現(xiàn)了一種更加簡(jiǎn)便的方式來(lái)顯示大圖。-----CATiledLayer
蘋果推薦的大圖顯示方式撵彻,API會(huì)自動(dòng)把大圖拆分成若干小貼片钓株,在需要顯示的時(shí)候才會(huì)進(jìn)行加載实牡。自帶漸入的效果。
缺點(diǎn):滑動(dòng)的時(shí)候比較消耗CPU轴合,通過(guò)CPU操作減小了內(nèi)存的損耗创坞。

func addTiledLayer() {
        scrollView = UIScrollView.init(frame: self.view.bounds)
        self.scrollView.contentSize = CGSize(width: 9514, height: 6388)
        self.scrollView.maximumZoomScale = 1.0
        self.scrollView.minimumZoomScale = 0.3
        self.scrollView.bouncesZoom = false
        self.scrollView.bounces = false
        self.scrollView.delegate = self
        self.view.addSubview(scrollView)
        
        imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 9514, height: 6388))
        self.scrollView.addSubview(imageView)
        
        tiledLayer.frame = CGRect(x: 0, y: 0, width: 9514, height: 6388)
        tiledLayer.delegate = self
        //tiledLayer.tileSize的默認(rèn)大小是256*256
        scrollView.contentSize = tiledLayer.frame.size
        imageView.layer.addSublayer(tiledLayer)
        tiledLayer.setNeedsDisplay()
    }

CALayerDelegate的代理方法:

//我使用PS把大圖切割成了950張小圖
func draw(_ layer: CALayer, in ctx: CGContext) {
        let layer = layer as! CATiledLayer
        let bounds = ctx.boundingBoxOfClipPath
        let x:Int = Int(floor(bounds.origin.x / layer.tileSize.width))//列數(shù)
        let y:Int = Int(floor(bounds.origin.y / layer.tileSize.height))//行數(shù)
        //加載貼圖
        let tileImage = UIImage.init(named: String.init(format: "test_%02ld.png",38*y+x))  //38是大圖切割的最大列數(shù)
        UIGraphicsPushContext(ctx)
        tileImage?.draw(in: bounds)
        UIGraphicsPopContext()
    }

我通過(guò)PS進(jìn)行的圖片切割,當(dāng)然也可以通過(guò)代理來(lái)實(shí)現(xiàn)受葛,然后從沙盒中把圖片拷貝出來(lái),代碼如下:

//切圖保存在沙河中
    func cutImageAndSave() {
        let filePath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last
        let imageName = String.init(format: "%@/test-00-00.png", filePath!)
        let tileImage = UIImage.init(contentsOfFile: imageName)
        if tileImage != nil {
            return
        }
        let image = UIImage.init(named: "city") //大圖
        let imageV = UIImageView.init(image: image)
        let WH:CGFloat = imageWidth
        let HH:CGFloat = imageHeight
        let size = image?.size
        
        //ceil 向上取整
        let rows:Int = Int(ceil((size?.height)! / HH))
        let cols:Int = Int(ceil((size?.width)! / WH))
        
        for y in 0..<rows {
            for x in 0..<cols {
                let subImage = self.captureView(theView: imageV, fra: CGRect(x: x*Int(WH), y: y*Int(HH), width: Int(WH), height: Int(HH)))
                let path = String.init(format: "%@/test-%02ld-%02ld.png", filePath!,x,y)
                do {
                    try UIImagePNGRepresentation(subImage)?.write(to: URL.init(fileURLWithPath: path))
                } catch {
                    print("error")
                }
            }
        }
    }

    //切圖
    func captureView(theView:UIView, fra:CGRect) -> UIImage {
        UIGraphicsBeginImageContext(theView.frame.size)
        let context = UIGraphicsGetCurrentContext()
        theView.layer.render(in: context!)
        
        //獲取圖片
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        let ref = img?.cgImage?.cropping(to: fra)
        let i = UIImage.init(cgImage: ref!)
        return i
    }

如有不對(duì)的地方歡迎指正题涨。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市总滩,隨后出現(xiàn)的幾起案子纲堵,更是在濱河造成了極大的恐慌,老刑警劉巖咳秉,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婉支,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡澜建,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蝌以,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)炕舵,“玉大人,你說(shuō)我怎么就攤上這事跟畅⊙式睿” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵徊件,是天一觀的道長(zhǎng)奸攻。 經(jīng)常有香客問(wèn)我,道長(zhǎng)虱痕,這世上最難降的妖魔是什么睹耐? 我笑而不...
    開(kāi)封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮部翘,結(jié)果婚禮上硝训,老公的妹妹穿的比我還像新娘。我一直安慰自己新思,他們只是感情好窖梁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著夹囚,像睡著了一般纵刘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荸哟,一...
    開(kāi)封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天假哎,我揣著相機(jī)與錄音瞬捕,去河邊找鬼。 笑死位谋,一個(gè)胖子當(dāng)著我的面吹牛山析,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掏父,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼笋轨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了赊淑?” 一聲冷哼從身側(cè)響起爵政,我...
    開(kāi)封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陶缺,沒(méi)想到半個(gè)月后钾挟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饱岸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年掺出,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苫费。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汤锨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出百框,到底是詐尸還是另有隱情闲礼,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布铐维,位于F島的核電站柬泽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嫁蛇。R本人自食惡果不足惜锨并,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棠众。 院中可真熱鬧琳疏,春花似錦、人聲如沸闸拿。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)新荤。三九已至揽趾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苛骨,已是汗流浹背篱瞎。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工苟呐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俐筋。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓牵素,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親澄者。 傳聞我的和親對(duì)象是個(gè)殘疾皇子笆呆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,133評(píng)論 25 707
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司粱挡,掛了不少赠幕,但最終還是拿到小米、百度询筏、阿里榕堰、京東、新浪嫌套、CVTE逆屡、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,246評(píng)論 11 349
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件踱讨、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,103評(píng)論 4 62
  • 內(nèi)存管理 簡(jiǎn)述OC中內(nèi)存管理機(jī)制康二。與retain配對(duì)使用的方法是dealloc還是release,為什么扁凛?需要與a...
    丶逐漸閱讀 1,964評(píng)論 1 16
  • 2016.9.4 小時(shí)候锦亦,從鄉(xiāng)下看城市的燈火 總覺(jué)得是個(gè)神話 那么多五光十色的路燈 還有川流不息的車 能走在城市的...
    風(fēng)木吟閱讀 263評(píng)論 0 1