iOS 一鍵哀悼模式(灰度色盲模式)

前陣子清明節(jié)為哀悼疫情中去世去的同胞和戰(zhàn)士們屈溉,全國(guó)娛樂(lè)設(shè)施停業(yè)一天们镜,各大廠商的 app 也都啟動(dòng)了灰度色盲模式的

類(lèi)似效果


QQ20200425-102153-HD.gif

下面是本文要實(shí)現(xiàn)的效果


QQ20200425-102526.gif

因此方案并不完全成熟缴渊,本文只講思路不提供demo

類(lèi)似效果在安卓里有多種實(shí)現(xiàn)方案
第一種是拿系統(tǒng)色盲模式(iOS 拿不到)
第二種就是遍歷 view 了仑乌,此文方案是這種思路

一、一個(gè)全局控制的單例
單利需要的內(nèi)容有:是否置灰的狀態(tài)亮瓷、開(kāi)啟灰色配置的方法琴拧、 多個(gè)weakMemory 的NSHashTable保存要處理的 view 們

// 啟動(dòng)方法,要處理所有視圖交換方法
public func launch() {
        isGray = true
        UIImageView.ZHGrayStyleImageViewSwizzleMethord
        UIView.ZHGrayStyleUIViewswizzleMethord
        CALayer.ZHGrayStyleUIViewswizzleMethord
        UIImage.ZHGrayStyleImageSwizzleMethord
        UITabBar.ZHGrayStyleTabbarSwizzleMethord
        UILabel.ZHGrayStyleLabelSwizzleMethord
}

交換方法代碼

extension NSObject {
    
    @objc dynamic func zh_setAssociatedObject(key: UnsafeRawPointer, value: Any?) {
        
        objc_setAssociatedObject(self, key, value, .OBJC_ASSOCIATION_RETAIN)
    }
    
    @objc dynamic func zh_getAssociatedObject(key: UnsafeRawPointer) -> Any? {
        
        return objc_getAssociatedObject(self, key)
    }
    
    static func zh_swizzle(originalSelector: Selector, swizzledSelector: Selector) {
        
        let originalMethod = class_getInstanceMethod(self, originalSelector)
        let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
        guard (originalMethod != nil && swizzledMethod != nil) else {
            return
        }
        if class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!)) {
            class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
        } else {
            method_exchangeImplementations(originalMethod!, swizzledMethod!)
        }
    }
}

二寺庄、交換方法艾蓝,只展示一個(gè)例子(要處理的有 View的 backgroundColor、layer 的 backgroundColor 和 shadowColor斗塘、label 的 textColor赢织、tabBar 的 tintColor 等)

// static let xxx: Void = {}()保證只執(zhí)行一次
static let ZHGrayStyleUIViewswizzleMethord: Void = {
        
        zh_swizzle(originalSelector: #selector(getter: UIView.backgroundColor), swizzledSelector: #selector(getter: UIView.myBackgroundColor))
        zh_swizzle(originalSelector: #selector(setter: UIView.backgroundColor), swizzledSelector: #selector(setter: UIView.myBackgroundColor))
    }()

var cacheBackgroundColor: UIColor? {
        set {
            zh_setAssociatedObject(key: &ZHGrayStlyeBackgroundColorKey, value: newValue)
        }
        get {
            return zh_getAssociatedObject(key: &ZHGrayStlyeBackgroundColorKey) as? UIColor
        }
    }

    @objc
    open var myBackgroundColor: UIColor? {
        set {
            ZHGrayStyle.instance.append(self)
            if ZHGrayStyle.instance.isGray, let newValue = newValue {
                cacheBackgroundColor = newValue
                self.myBackgroundColor = newValue.getGrayColor()
            } else {
                cacheBackgroundColor = newValue?.getGrayColor() ?? nil
                self.myBackgroundColor = newValue
            }
        }
        get {
            return self.myBackgroundColor
        }
    }

三、Color 的 grayColor 算法(CGColor 類(lèi)似)

public func getGrayColor() -> UIColor {
        let rgb = getRGB()
        guard rgb.r != rgb.g && rgb.r != rgb.b else { return self }
        let rColor = rgb.r * 0.299
        let gColor = rgb.g * 0.587
        let bColor = rgb.b * 0.114
        
        let gray = rColor + gColor + bColor
        return .init(red: gray, green: gray, blue: gray, alpha: 1)
    }
    
    public func getGrayColor(r: CGFloat, g: CGFloat, b: CGFloat) -> UIColor {
        // 不處理浮點(diǎn)數(shù)也可以*299,最后 gray 的和 / 1000這樣算
        let rColor = r * 0.299
        let gColor = g * 0.587
        let bColor = b * 0.114
        
        let gray = rColor + gColor + bColor
        return .init(red: gray, green: gray, blue: gray, alpha: 1)
    }
    
    public func getRGB() -> (r: CGFloat, g: CGFloat, b: CGFloat) {
        var RColor: CGFloat = 0
        var GColor: CGFloat = 0
        var BColor: CGFloat = 0
        if let components = self.cgColor.components, components.count == 4 {
            RColor = components[0]
            GColor = components[1]
            BColor = components[2]
        }
        return (RColor, GColor, BColor)
    }

四馍盟、到此于置,我們已經(jīng)完成了大部分工作,剩下的也是最難啃的骨頭--UIImage
4.1贞岭、要處理 UIImage八毯,首先需要了解位圖的格式搓侄,在此不做闡述,推薦地址:
http://www.reibang.com/p/362c2f03d378
4.2话速、在了解了位圖之后讶踪,我們可以處理位圖

public func grayImage() -> UIImage? {
        
        //        獲得寬度和高度數(shù)值
        let width = Int(size.width)
        let height = Int(size.height)
        
        //        創(chuàng)建灰度色彩空間對(duì)象,各種設(shè)備對(duì)待顏色的方式都不一樣泊交,顏色必須有一個(gè)相關(guān)的色彩空間
        let spaceRef = CGColorSpaceCreateDeviceGray()
        //        參數(shù)data指向渲染的繪制內(nèi)存的地址乳讥,bitsOerComponent表示內(nèi)存中像素的每個(gè)組件的位數(shù),bytesPerRow表示每一行在內(nèi)存中占的比特?cái)?shù),space表示使用的顏色空間廓俭,bitmapInfo表示是否包含alpha通道
        //        CGBitmapInfo().rawValue
        guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: spaceRef, bitmapInfo: CGBitmapInfo().rawValue) else {
            return nil
        }
        //        然后創(chuàng)建一個(gè)和原視圖同樣尺寸的空間
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
        //        在灰度上下文中畫(huà)入圖片
        //        context.alpha
        guard let cgImage = cgImage else {
            return nil
        }
        context.draw(cgImage, in: rect)
        //        從上下文中獲取并生成專為灰度的圖片
        
        if let cgImage = context.makeImage() {
            let grayImage = UIImage.init(cgImage: cgImage)
            return grayImage
        }
        return nil
    }

4.3云石、圖片灰度處理后會(huì)發(fā)現(xiàn)png的透明底會(huì)變成黑色,如圖


image.png

時(shí)間原因研乒,在此只提供 oc 處理方法汹忠,把黑底轉(zhuǎn)為透明:

+ (UIImage*)imageToTransparent:(UIImage*) image
{
    // 分配內(nèi)存
    const int imageWidth = image.size.width;
    const int imageHeight = image.size.height;
    size_t bytesPerRow = imageWidth * 4;
    uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);

    // 創(chuàng)建context
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,
                                                 kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
    CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);

    // 遍歷像素
    int pixelNum = imageWidth * imageHeight;
    uint32_t* pCurPtr = rgbImageBuf;
    for (int i = 0; i < pixelNum; i++, pCurPtr++)
    {
        uint8_t* ptr = (uint8_t*)pCurPtr;
        if (ptr[3] < 50 && ptr[3] < 50 && ptr[1] < 50) {
            ptr[0] = 0;
        } else {
// 灰度算法
            uint8_t gray = ptr[3] * 0.299 + ptr[2] * 0.587 + ptr[1] * 0.114;
            ptr[3] = gray; //0~255
            ptr[2] = gray;
            ptr[1] = gray;
        }
    }

    // 將內(nèi)存轉(zhuǎn)成image
    CGDataProviderRef dataProvider =CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, ProviderReleaseData);

    CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight,8, 32, bytesPerRow, colorSpace,
                                        kCGImageAlphaLast |kCGBitmapByteOrder32Little, dataProvider,
                                        NULL, true,kCGRenderingIntentDefault);
    CGDataProviderRelease(dataProvider);

    UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef];

    // 釋放
    CGImageRelease(imageRef);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    return resultUIImage;
}

void ProviderReleaseData (void *info, const void *data, size_t size)
{
    free((void*)data);
}

五、總結(jié)
本文主要是運(yùn)用了 runtime 去 hook 各種要處理的視圖的相關(guān)方法雹熬,例如 init宽菜,例如 backgroundColor,去進(jìn)行灰度處理操作橄唬,還要對(duì) bitmap 位圖有一定的了解去處理圖片(大多數(shù)是圖片置灰)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末赋焕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子仰楚,更是在濱河造成了極大的恐慌,老刑警劉巖犬庇,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僧界,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡臭挽,警方通過(guò)查閱死者的電腦和手機(jī)捂襟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)欢峰,“玉大人葬荷,你說(shuō)我怎么就攤上這事∨μ” “怎么了宠漩?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)懊直。 經(jīng)常有香客問(wèn)我扒吁,道長(zhǎng),這世上最難降的妖魔是什么室囊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任雕崩,我火速辦了婚禮魁索,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盼铁。我一直安慰自己粗蔚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布饶火。 她就那樣靜靜地躺著支鸡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趁窃。 梳的紋絲不亂的頭發(fā)上牧挣,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音醒陆,去河邊找鬼瀑构。 笑死,一個(gè)胖子當(dāng)著我的面吹牛刨摩,可吹牛的內(nèi)容都是我干的寺晌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼澡刹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呻征!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起罢浇,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤陆赋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后嚷闭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體攒岛,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年胞锰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灾锯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嗅榕,死狀恐怖顺饮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凌那,我是刑警寧澤兼雄,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站案怯,受9級(jí)特大地震影響君旦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一金砍、第九天 我趴在偏房一處隱蔽的房頂上張望局蚀。 院中可真熱鬧,春花似錦恕稠、人聲如沸琅绅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)千扶。三九已至,卻和暖如春骆捧,著一層夾襖步出監(jiān)牢的瞬間澎羞,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工敛苇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妆绞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓枫攀,卻偏偏與公主長(zhǎng)得像括饶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子来涨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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

  • 轉(zhuǎn)自http://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de...
    嚴(yán)木木閱讀 1,535評(píng)論 1 8
  • 馬上進(jìn)入六月图焰,離畢業(yè)答辯還有大概一周時(shí)間,實(shí)驗(yàn)室里幾乎見(jiàn)不到快要畢業(yè)的面孔蹦掐。都是研究生的緣故技羔,年紀(jì)不小,又是第一次...
    紅顏逝發(fā)如雪閱讀 212評(píng)論 0 0
  • 先生每次煮面笤闯,都是作為快餐的形式堕阔。但作為西北人,煮面颗味,我卻每次都會(huì)做足工序和儀式感。 就拿一碗簡(jiǎn)單的西紅柿雞蛋面來(lái)...
    一雙人的世界閱讀 1,164評(píng)論 1 4
  • 在閱讀《正面管教》關(guān)于鼓勵(lì)一章的時(shí)候牺弹,我有一種堆砌了一堆有道理的工具浦马,但是又打不通的感受。反復(fù)看了兩遍尋找一條線來(lái)...
    洛言1900閱讀 211評(píng)論 0 1
  • 這是依賴《算法導(dǎo)論》的內(nèi)容編寫(xiě)的代碼张漂。這個(gè)數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)晶默,我認(rèn)為是一個(gè)從非專業(yè)的程序員到專業(yè)程序員的轉(zhuǎn)折點(diǎn),這個(gè)數(shù)...
    IT孤獨(dú)者閱讀 2,620評(píng)論 0 1