iOS之動態修改app 圖標(icon)


一,應用需求  

  動態修改app的圖標,就是在不重新安裝app的情況下,可以修改當前的icon圖標;在某些情況下,是有這個需求的;例如,可以更換主題的app中,一般都會有一套完整的主題包含相應的icon;還有就是一些節日主題的icon或者促銷的icon,例如淘寶、京東等的節日icon。

二,相關API  

  在iOS 10.3之后,蘋果官方提供了相關的API來實現這個功能,主要是下面這幾個方法:

interface UIApplication (UIAlternateApplicationIcons) 

// 如果為NO,表示當前進程不支持替換圖標
@property (readonly, nonatomic) BOOL supportsAlternateIcons NS_EXTENSION_UNAVAILABLE("Extensions may not have alternate icons") API_AVAILABLE(ios(10.3), tvos(10.2));

// 傳入nil代表使用主圖標. 完成后的操作將會在任意的后台隊列中異步執行; 如果需要更改UI,請確保在主隊列中執行.
- (void)setAlternateIconName:(nullable NSString *)alternateIconName completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler NS_EXTENSION_UNAVAILABLE("Extensions may not have alternate icons") API_AVAILABLE(ios(10.3), tvos(10.2));

// 如果alternateIconName為nil,則代表當前使用的是主圖標.
@property (nullable, readonly, nonatomic) NSString *alternateIconName NS_EXTENSION_UNAVAILABLE("Extensions may not have alternate icons") API_AVAILABLE(ios(10.3), tvos(10.2));

@end

三,工程內部相關配置

 

  1. 配置icon  
      動態修改的icon不能放在 Assets.xcassets 里,但是正常的主icon還是可以在這里設置的,也可以按下面的方法來設置;
    首先,把需要修改的icon放在一個文件夾內:


    其文件夾內是這樣的


    這里每種icon我只放了一個,如果有多個尺寸的icon,也可以直接全放進去:


    然后,文件夾會變成這樣:


    這里的icon名稱只需要和下面配置一致即可

  2. 配置info.plist

    • 在info.plist中右鍵 -> Add Row :輸入Icon... 會有提示,選擇Icon files(iOS 5)

      這時候,內容是這樣的:
      這里的Icon files(iOS 5)是個字典,其中可包含的Key值有
      CFBundlePrimaryIcon -> Primary Icon
      CFBundleAlternateIcons
      UINewsstandIcon -> Newsstand Icon

      這里的Primary Icon是設置app的主icon,可以在這里的Icon files數組內添加,有多個的話,依次添加,也可以這里不用填寫,直接在Assets.xcassets 里配置;

      下面的Newsstand Icon,暫時用不到,不用管,也可以刪除。
      在 Icon files(iOS 5)內添加一個Key: CFBundleAlternateIcons ,類型為字典,在這個字典里配置我們所有需要動態修改的icon:鍵為icon的名稱,值為一個字典(這個字典里包含兩個鍵:CFBundleIconFiles,其值類型為Array,內容為icon的名稱;UIPrerenderedIcon,其值類型為bool,內容為NO,也可以不加此key),例如:

      把第一步中添加的圖片全部添加進來就是這樣的:
      到此,info.plist的配置即完成了;
      或者將info.plist文件以 Source code 方式打開,添加以下代碼:
      <key>CFBundleIcons</key>
          <dict>
              <key>CFBundleAlternateIcons</key>
              <dict>
                  <key>rain</key>
                  <dict>
                      <key>CFBundleIconFiles</key>
                      <array>
                          <string>rain</string>
                      </array>
                      <key>UIPrerenderedIcon</key>
                      <false/>
                  </dict>
                  <key>snow</key>
                  <dict>
                      <key>CFBundleIconFiles</key>
                      <array>
                          <string>snow</string>
                      </array>
                      <key>UIPrerenderedIcon</key>
                      <false/>
                  </dict>
                  <key>sunshine</key>
                  <dict>
                      <key>CFBundleIconFiles</key>
                      <array>
                          <string>sunshine</string>
                      </array>
                      <key>UIPrerenderedIcon</key>
                      <false/>
                  </dict>
                  <key>cloudy</key>
                  <dict>
                      <key>CFBundleIconFiles</key>
                      <array>
                          <string>cloudy</string>
                      </array>
                      <key>UIPrerenderedIcon</key>
                      <false/>
                  </dict>
              </dict>
              <key>CFBundlePrimaryIcon</key>
              <dict>
                  <key>CFBundleIconFiles</key>
                  <array>
                      <string></string>
                  </array>
                  <key>UIPrerenderedIcon</key>
                  <false/>
              </dict>
              <key>UINewsstandIcon</key>
              <dict>
                  <key>CFBundleIconFiles</key>
                  <array>
                      <string></string>
                  </array>
                  <key>UINewsstandBindingType</key>
                  <string>UINewsstandBindingTypeMagazine</string>
                  <key>UINewsstandBindingEdge</key>
                  <string>UINewsstandBindingEdgeLeft</string>
              </dict>
          </dict>

      如果是添加了多個尺寸icon,也要在這里分別配置,以上面添加的sunshine圖標為例:


      使用的時候還是使用sunshine進行賦值即可!

  3. 代碼配置完成后,代碼部分就比較簡單了:

    - (void)changeAppIconWithName:(NSString *)iconName {
        if (![[UIApplication sharedApplication] supportsAlternateIcons]) {
            return;
        }
        if ([iconName isEqualToString:@""]) {
            iconName = nil;
        }
        [[UIApplication sharedApplication] setAlternateIconName:iconName completionHandler:^(NSError * _Nullable error) {
            if (error) {
                NSLog(@"更換app圖標發生錯誤了 : %@",error);
            }
        }];
    }

    在需要修改icon的地方調用這個方法,並把相應的icon名稱傳進去即可:

    - (IBAction)snow:(id)sender {
        [self changeAppIconWithName:@"snow"];
    }
    - (IBAction)rain:(id)sender {
        [self changeAppIconWithName:@"rain"];
    }
    - (IBAction)cloudy:(id)sender {
        [self changeAppIconWithName:@"rain"];
    }
    - (IBAction)sunshine:(id)sender {
        [self changeAppIconWithName:@"sunshine"];
    }

    示意圖:

  4. 設置iPad動態圖標

    iPad的動態圖標設置和上面步驟基本一樣,有的文章說是將 CFBundleIcons 改為 CFBundleIcons~ipad,即:
    但是,在測試中發現,使用上面的key值也是可以實現動態改變的,即不做任何修改,iPhone和iPad使用相同的配置,即:CFBundleIcons。

  5. 去掉更換icon時的彈框

    從上面的示意圖可以發現,在設置icon的時候,會有個系統彈框,這樣有時候會不太友好,我們可以使用Runtime,對UIViewController進行擴展來隱藏這個彈框:
    // UIViewController+LQNoPresent.h
    #import <UIKit/UIKit.h>
    @interface UIViewController (LQNoPresent)
    @end
    
    // UIViewController+LQNoPresent.m
    #import "UIViewController+LQNoPresent.h"
    #import <objc/runtime.h>
    
    @implementation UIViewController (LQNoPresent)
    
    + (void)load {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Method presentM = class_getInstanceMethod(self.class, @selector(presentViewController:animated:completion:));
            Method presentSwizzlingM = class_getInstanceMethod(self.class, @selector(lq_presentViewController:animated:completion:));
            
            method_exchangeImplementations(presentM, presentSwizzlingM);
        });
    }
    
    - (void)lq_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
        if ([viewControllerToPresent isKindOfClass:[UIAlertController class]]) {
    //        NSLog(@"title : %@",((UIAlertController *)viewControllerToPresent).title);
    //        NSLog(@"message : %@",((UIAlertController *)viewControllerToPresent).message);
            UIAlertController *alertController = (UIAlertController *)viewControllerToPresent;
            if (alertController.title == nil && alertController.message == nil) {
                return;
            }
        }
        [self lq_presentViewController:viewControllerToPresent animated:flag completion:completion];
    }
    @end

    這樣在切換圖標的時候就沒有系統的彈框了:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM