成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

針尖上帶著腳鐐跳舞的widget

移動(dòng)開(kāi)發(fā) iOS
自從iOS 10蘋(píng)果給widget做了一次大改版后,很多人都開(kāi)發(fā)了自己的widget。網(wǎng)上也有很多教程,來(lái)告訴你怎么快速開(kāi)發(fā)一個(gè)widget。我的這篇文章也不會(huì)重復(fù)那些簡(jiǎn)單的創(chuàng)建extension添加證書(shū)之類(lèi)的東西。我們要深入地看一下widget到底應(yīng)該做成什么樣子。

自從iOS 10蘋(píng)果給widget做了一次大改版后,很多人都開(kāi)發(fā)了自己的widget。網(wǎng)上也有很多教程,來(lái)告訴你怎么快速開(kāi)發(fā)一個(gè)widget。我的這篇文章也不會(huì)重復(fù)那些簡(jiǎn)單的創(chuàng)建extension添加證書(shū)之類(lèi)的東西。我們要深入地看一下widget到底應(yīng)該做成什么樣子。

你真的了解widget的尺寸嗎

首先widget由兩種狀態(tài)

  1. typedef NS_ENUM(NSInteger, NCWidgetDisplayMode) { 
  2.     NCWidgetDisplayModeCompact, // Fixed height 
  3.     NCWidgetDisplayModeExpanded, // Variable height 
  4.  

大部分網(wǎng)上的教程都會(huì)告訴你,如果你想改widget的高度,都是在下面這個(gè)方法中這么寫(xiě)

  1. - (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize { 
  2.     if (activeDisplayMode == NCWidgetDisplayModeCompact) { 
  3.         self.preferredContentSize = CGSizeMake(maxSize.width, 110); 
  4.     } else { 
  5.         self.preferredContentSize = CGSizeMake(maxSize.width, 300); 
  6.     } 
  7.  

這個(gè)意思就算折疊狀態(tài)110,展開(kāi)狀態(tài)300。因?yàn)槿绻阏郫B狀態(tài)就算寫(xiě)120,也一樣是110的高度,這個(gè)高度不會(huì)變化。展開(kāi)狀態(tài)下,當(dāng)然要取比maxSize.height小的一個(gè)值。那么maxSize這個(gè)值是多少?

然而我要告訴你,高度根本就不是一個(gè)固定值!并且可以認(rèn)為是無(wú)規(guī)律的!!!

因?yàn)椋麄€(gè)widget的maxSize限制的***規(guī)則是根據(jù)系統(tǒng)字體大小變化。

無(wú)論是折疊狀態(tài)還是展開(kāi)狀態(tài)。也就是說(shuō),直接寫(xiě)死110是錯(cuò)誤的。因?yàn)槟J(rèn)系統(tǒng)字體下,的確折疊高度是110。但是一旦系統(tǒng)字體改為最小,widget折疊狀態(tài)的高度僅為95,而在系統(tǒng)字體***的情況下,widget折疊狀態(tài)的高度是140。而系統(tǒng)字體大小一共有7檔。也就是說(shuō),折疊高度和字體大小相關(guān),但不是線性相關(guān)。

可以驗(yàn)證,折疊的高度是95-100-105-110-120-135-140這七檔。且不可修改。

光折疊高度也就算了。展開(kāi)***高度也是一個(gè)非線性相關(guān)的高度(并且是在折疊高度統(tǒng)一的情況下)。

以下對(duì)于展開(kāi)高度的討論,都固定系統(tǒng)字體大小為默認(rèn)大小,控制變量(最終得出的尺寸結(jié)果,理論上乘以7就是所有可能的高度)。

首先就是機(jī)型的差異,當(dāng)然手機(jī)屏幕越小,展開(kāi)***高度也就越小,這個(gè)其實(shí)尚可以接受。大不了我們按照最小屏幕的情況開(kāi)發(fā)唄。然而,我要告訴你,widget***高度還是會(huì)變!

這個(gè)是我們最常見(jiàn)的widget入口,就是屏幕左滑的Today頁(yè) 

 

 

 

然而其實(shí)還有另外一個(gè)入口,就是下拉通知頁(yè)的左滑,也會(huì)有入口 

 

 

 

這兩個(gè)入口進(jìn)來(lái),widget展開(kāi)狀態(tài)的***高度,后者會(huì)比前者小很多!

打斷點(diǎn)看maxSize很容易就可以驗(yàn)證,iPhone7默認(rèn)字體大小,展開(kāi)狀態(tài)下。***個(gè)入口的maxSize.height是616,而另外一個(gè)情況下,這個(gè)數(shù)值變成了528。

此時(shí)真想問(wèn)一聲蘋(píng)果爸爸,這到底是想搞啥?

其實(shí)還有第三個(gè)入口,就是3D touch app圖標(biāo),也會(huì)出現(xiàn)widget,但是那個(gè)只有折疊狀態(tài) 

 

 

 

也就是說(shuō),目前來(lái)看,折疊狀態(tài)是7種尺寸,而每種屏幕大小的展開(kāi)狀態(tài)下就是7*2種,也就是說(shuō),4吋,4.7吋,5.5吋這三種主流屏幕尺寸都要適配的話,展開(kāi)狀態(tài)是7*2*3=42種尺寸。

看到這你可以說(shuō),沒(méi)關(guān)系,我就取4吋設(shè)備最小的高度。這個(gè)就要看你的設(shè)計(jì)師能不能同意了。

你以為完了嗎?別說(shuō)iPad呢,那個(gè)咱們就不考慮了,iPhone能放下,iPad當(dāng)然也放得下。

但是你真的想不到,5.5吋也就是Plus機(jī)型的橫屏狀態(tài),也是不同尺寸的。Plus橫屏下的展開(kāi)模式,***個(gè)入口***高度僅有352,第二入口的***高度僅264……

意味著什么,***字體情況下的折疊狀態(tài)都有140高度,展開(kāi)還不到折疊高度的兩倍。

如果你對(duì)widget的尺寸適配感興趣,并且有解決方案,請(qǐng)聯(lián)系我,必有重謝。

有沒(méi)有感覺(jué)被閃瞎了

你如果添加了很多個(gè)widget就會(huì)發(fā)現(xiàn),就單單在列表里上下滑動(dòng)都能把眼睛閃瞎。 

 

 

 

Widget 自身的更新機(jī)制,是進(jìn)入到 Widget 后,先執(zhí)行 viewDidLoad 方法,然后是 viewWillAppear 方法。

但是經(jīng)測(cè)驗(yàn),每當(dāng)某一個(gè)Widget在上下滑動(dòng),滑出屏幕后,再把這個(gè)widget劃回來(lái),就走上面那一套刷新機(jī)制。

由于以上特性,更新代碼***寫(xiě)在 viewWillAppear 方法里面,對(duì)于更新時(shí)效性特別強(qiáng)的,比如天氣類(lèi) app,這種***就是在該方法里面添加一個(gè) NSTimer 定時(shí)進(jìn)行刷新,在 viewWillDisAppear 方法中 進(jìn)行 取消NSTimer invalidate定時(shí)更新即可。

或者,你自己實(shí)現(xiàn)緩存,一樣可以?xún)?yōu)化。判斷如果請(qǐng)求來(lái)的數(shù)據(jù)和當(dāng)前數(shù)據(jù)內(nèi)容一致,那么就不進(jìn)行刷新列表操作。

知乎、得到 等等好多app的 Widget,只要走 viewDidLoad 方法就會(huì)閃一下,因?yàn)槊看蜽idget加載請(qǐng)求的數(shù)據(jù)后會(huì)進(jìn)行替換造成的。

至于為什么只要不再視線范圍再回來(lái)就刷新,我猜測(cè),是因?yàn)閮?nèi)存問(wèn)題。

widget對(duì)內(nèi)存的要求之高令人發(fā)指,你的widget中一旦有g(shù)if,基本上就完全沒(méi)有辦法顯示,過(guò)一會(huì)就會(huì)顯示無(wú)法載入。不僅如此,反復(fù)來(lái)回滾動(dòng)widget頁(yè)面,以不斷刷新也會(huì)導(dǎo)致占用內(nèi)存升高,不太清楚這個(gè)是不是蘋(píng)果的BUG,但是我自己的測(cè)試中,盡量都讓單個(gè)的widget內(nèi)存占用小于15M,這樣被殺掉內(nèi)存的機(jī)會(huì)很小。

所以,我在開(kāi)發(fā)的時(shí)候,gif圖都只取***幀。并且盡可能不主動(dòng)刷新UI,保持widget內(nèi)存處于一個(gè)較低的水平。

而且由于extension實(shí)際上不能直接使用主target中的那些框架,所以,我也寫(xiě)了一些最基本的功能組件。

首先當(dāng)然是緩存系統(tǒng),圖片緩存尤其關(guān)鍵,因?yàn)閣idget這種特性,會(huì)反復(fù)刷新,如果沒(méi)有緩存系統(tǒng),是非常大的浪費(fèi)。首先就是圖片緩存: 

  1. #import "QDTEImageCache.h" 
  2. #import <CommonCrypto/CommonDigest.h> 
  3.  
  4. @implementation QDTEImageCache 
  5.  
  6. + (instancetype)shareImageCache { 
  7.     static dispatch_once_t once; 
  8.     static id instance; 
  9.     dispatch_once(&once, ^{ 
  10.         instance = [self new]; 
  11.     }); 
  12.     return instance; 
  13.  
  14. - (BOOL)isExistCacheForKey:(NSString *)key { 
  15.     key = [self cachedFileNameForKey:key]; 
  16.     NSString *filePath = [[self getCachePath] stringByAppendingPathComponent:key]; 
  17.     return [[NSFileManager defaultManager] fileExistsAtPath:filePath]; 
  18.  
  19. - (NSData *)getImageDataForKey:(NSString *)key { 
  20.      
  21.     if ([self isExistCacheForKey:key]) { 
  22.         return [NSData dataWithContentsOfFile:[[self getCachePath] stringByAppendingPathComponent:[self cachedFileNameForKey:key]]]; 
  23.     } 
  24.     return nil; 
  25.  
  26. - (void)saveToCacheWithData:(NSData *)data forKey:(NSString *)key { 
  27.     key = [self cachedFileNameForKey:key]; 
  28.     NSString *filePath = [[self getCachePath] stringByAppendingPathComponent:key]; 
  29.      
  30.     dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
  31.         [data writeToFile:filePath atomically:YES]; 
  32.     }); 
  33.      
  34.  
  35. - (NSString *)getCachePath { 
  36.     NSFileManager *fileMgr = [NSFileManager defaultManager]; 
  37.     NSString *containerPath = [[fileMgr containerURLForSecurityApplicationGroupIdentifier:@"group.com.XXXXXXX"] path]; 
  38.      
  39.     NSString *path = [containerPath stringByAppendingString:@"/Caches/"]; 
  40.     if (![fileMgr fileExistsAtPath:path]) { 
  41.         BOOL res = [fileMgr createDirectoryAtPath:path 
  42.                       withIntermediateDirectories:YES 
  43.                                        attributes:nil 
  44.                                             error:nil]; 
  45.         if (!res) { 
  46.             return nil; 
  47.         } 
  48.     } 
  49.      
  50.     return path; 
  51.  
  52. - (NSString *)cachedFileNameForKey:(NSString *)key { 
  53.     const char *str = [key UTF8String]; 
  54.     if (str == NULL) { 
  55.         str = ""
  56.     } 
  57.     unsigned char r[CC_MD5_DIGEST_LENGTH]; 
  58.     CC_MD5(str, (CC_LONG)strlen(str), r); 
  59.     NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@"
  60.                           r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], 
  61.                           r[11], r[12], r[13], r[14], r[15], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]]; 
  62.      
  63.     return filename; 
  64. @end  

一個(gè)非常基礎(chǔ)的圖片緩存,同時(shí)配合文件管理類(lèi), 來(lái)管理接口返回的response:

控制器發(fā)出的請(qǐng)求,收到response的data時(shí)做一次緩存并比對(duì)

  1. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { 
  2.     [self.jsonData appendData:data]; 
  3.     NSDictionary *dic = [[NSJSONSerialization JSONObjectWithData:self.jsonData options:NSJSONReadingMutableContainers error:nil] copy]; 
  4.      
  5.     if (dic == nil) return
  6.      
  7.     self.jsonData = nil; 
  8.      
  9.     NSDictionary *metaDic = [dic valueForKey:@"meta"]; 
  10.      
  11.     if ([[metaDic valueForKey:@"status"] integerValue] == 200) { 
  12.          
  13.         NSArray *papers = [[dic valueForKey:@"response"] valueForKey:@"papers"]; 
  14.         NSDictionary *paperDic = [papers firstObject]; 
  15.          
  16.         [_fileMgr saveToCacheWithRawDic:paperDic]; 
  17.          
  18.         QDTELabModel *labModle = [self modelFromRawDic:paperDic]; 
  19.          
  20.         if (labModle.article_id.longValue == self.labModel.article_id.longValue) return
  21.          
  22.         self.labModel = labModle; 
  23.         dispatch_async(dispatch_get_main_queue(), ^{ 
  24.             for (UIView *subView in self.view.subviews) { 
  25.                 [subView removeFromSuperview]; 
  26.             } 
  27.             [self refreshContentView]; 
  28.         }); 
  29.     } 
  30.  

文件管理類(lèi)用來(lái)儲(chǔ)存 

  1. #import "QDTEFileManager.h" 
  2.  
  3. @implementation QDTEFileManager 
  4. + (instancetype)shareManager { 
  5.     static dispatch_once_t once; 
  6.     static id instance; 
  7.     dispatch_once(&once, ^{ 
  8.         instance = [self new]; 
  9.     }); 
  10.     return instance; 
  11.  
  12. - (NSDictionary *)getUserinfo { 
  13.     NSFileManager *fileMgr = [NSFileManager defaultManager]; 
  14.     NSString *containerPath = [[fileMgr containerURLForSecurityApplicationGroupIdentifier:@"group.com.XXXXXX"] path]; 
  15.      
  16.     NSString *filePath = [containerPath stringByAppendingPathComponent:@"QDUserinfo.json"]; 
  17.     if ([fileMgr fileExistsAtPath:filePath]) { 
  18.         NSError *error; 
  19.         return [[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath] options:NSJSONReadingMutableContainers error:&error] copy]; 
  20.     } 
  21.     return nil; 
  22.  
  23. - (NSDictionary *)getRawDicFromCache { 
  24.     NSFileManager *fileMgr = [NSFileManager defaultManager]; 
  25.     NSString *containerPath = [[fileMgr containerURLForSecurityApplicationGroupIdentifier:@"group.com.XXXXXX"] path]; 
  26.     NSString *path = [containerPath stringByAppendingString:@"/Caches/"]; 
  27.     NSString *filePath = [path stringByAppendingPathComponent:@"QDLabCache.json"]; 
  28.      
  29.     if ([fileMgr fileExistsAtPath:filePath]) { 
  30.         NSError *error; 
  31.         NSDictionary *rawDic = [[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath] options:NSJSONReadingMutableContainers error:&error] copy]; 
  32.         return rawDic; 
  33.     } 
  34.     return nil; 
  35.  
  36. - (void)saveToCacheWithRawDic:(NSDictionary *)rawDic { 
  37.     NSFileManager *fileMgr = [NSFileManager defaultManager]; 
  38.     NSString *containerPath = [[fileMgr containerURLForSecurityApplicationGroupIdentifier:@"group.com.XXXXXX"] path]; 
  39.      
  40.     NSString *path = [containerPath stringByAppendingString:@"/Caches/"]; 
  41.     BOOL res = [fileMgr createDirectoryAtPath:path 
  42.                   withIntermediateDirectories:YES 
  43.                                    attributes:nil 
  44.                                         error:nil]; 
  45.     if (!res) { 
  46.         return
  47.     } 
  48.     NSString *filePath = [path stringByAppendingPathComponent:@"QDLabCache.json"]; 
  49.      
  50.     if ([NSJSONSerialization isValidJSONObject:rawDic]) 
  51.     { 
  52.         NSError *error; 
  53.         NSData *jsonData = [NSJSONSerialization dataWithJSONObject:rawDic 
  54.                                                            options:NSJSONWritingPrettyPrinted 
  55.                                                              error:&error]; 
  56.          
  57.         dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
  58.             [jsonData writeToFile:filePath atomically:YES]; 
  59.         }); 
  60.     } 
  61.  
  62. - (NSString *)getServerIP 
  63.     if ([self getDEBUG]) { 
  64.         NSFileManager *fileMgr = [NSFileManager defaultManager]; 
  65.         NSString *containerPath = [[fileMgr containerURLForSecurityApplicationGroupIdentifier:@"group.com.XXXXXX"] path]; 
  66.          
  67.         NSString *filePath = [containerPath stringByAppendingPathComponent:@"QDServerIP.json"]; 
  68.          
  69.         if ([fileMgr fileExistsAtPath:filePath]) { 
  70.             NSError *error; 
  71.             NSArray *serverIPArr = [[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:filePath] options:NSJSONReadingMutableContainers error:&error] copy]; 
  72.             return serverIPArr.firstObject; 
  73.         } 
  74.     } 
  75.     return @"http://app3.qdaily.com"
  76.  
  77. - (BOOL)getDEBUG { 
  78. #ifdef DEBUG 
  79.     return YES; 
  80. #elif BETA 
  81.     return YES; 
  82. #else 
  83.     return NO
  84. #endif 
  85. @end  

***呢,這個(gè)是我其中一個(gè)widget的文件結(jié)構(gòu)。 

 

 

 

widget雖小,但是我當(dāng)時(shí)在開(kāi)發(fā)的時(shí)候還是盡量想怎么復(fù)雜怎么做,畢竟這種東西,開(kāi)發(fā)一次,幾乎以后再也不會(huì)去動(dòng)了。畢竟……針尖上還要帶著腳鐐跳舞實(shí)在太累了😂。

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2010-06-09 17:09:40

三網(wǎng)融合

2011-09-08 09:24:34

Mac Widget監(jiān)測(cè)系統(tǒng)iStatpro

2009-11-23 09:20:42

Intel CTO數(shù)據(jù)中心

2014-01-09 13:56:51

2013-08-02 10:32:56

DevOps

2013-09-12 13:27:07

DevOps

2010-12-01 11:36:02

跳槽

2010-03-31 13:37:38

Ubuntu 10.0

2011-09-07 14:20:42

Android Wid組件

2013-01-06 14:28:45

Ubuntu手機(jī)Ubuntu手機(jī)系統(tǒng)

2011-09-07 16:24:10

Qt Widget

2009-06-15 11:33:44

無(wú)線路由器WGR612NETGEAR

2012-11-27 11:10:11

云計(jì)算經(jīng)濟(jì)

2011-09-08 11:13:29

Widget

2019-12-03 10:58:58

HTTPS證書(shū)網(wǎng)站

2011-05-03 15:13:23

BlackBerryWidget

2011-09-08 15:07:10

Android Wid搭建

2011-09-08 15:51:33

Android Wid組件

2011-09-09 17:59:26

QT Widget

2010-07-13 09:02:19

Widget開(kāi)發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 成人激情视频在线 | 精品在线一区 | 国产亚洲一区二区三区在线 | 亚洲成人一区二区 | 欧美成人激情 | a在线观看免费 | 亚洲成av人片在线观看 | www.99热.com | 久久中文字幕一区 | a级免费黄色片 | av一级 | 国产一区二区三区亚洲 | 不卡一区| 欧美精品在欧美一区二区 | 国产在线精品一区二区 | 亚洲在线一区 | 国产亚洲一区二区三区在线观看 | 影音先锋中文字幕在线观看 | 欧美日韩国产一区二区 | 国产伦精品一区二区三区精品视频 | 精品99爱视频在线观看 | 91精品成人久久 | 国产免费观看视频 | 亚洲三级av | 在线观看亚洲 | 日本成人福利视频 | 亚洲天堂中文字幕 | 真人女人一级毛片免费播放 | 国产精品亚洲视频 | 成年人免费看的视频 | 欧美午夜精品理论片a级按摩 | 精品一二三区 | 人人澡人人射 | 国产一区二区久久 | 香蕉大人久久国产成人av | 国产美女一区二区 | 欧美天堂 | 国产亚洲一区二区三区在线 | 久久三区 | 亚洲国产区 | 一区二区三区免费在线观看 |