Tencent / QMUI_iOS

QMUI iOS——致力于提高项目 UI 开发效率的解决方案
http://qmuiteam.com/ios
Other
7.08k stars 1.38k forks source link

UITabBarItem.selectedImage 无法二次更新 #1218

Closed MoLice closed 3 years ago

MoLice commented 3 years ago

Bug 表现 UITabBarItem 只有第一次 setSelectedImage: 有效。

如何重现

如以 QMUI Demo 为例,在设置完 selectedImage 后再重新 setSelectedImage:,选中的图片依然保持第一次设置的值。

其他信息

MoLice commented 3 years ago

在 4.2.2 版本里,为了解决 #1122 ,在 UITabBarItem (QMUIThemeCompatibility) 里对 selectedImage 做了 QMUI 自己的备份。这里预期的执行顺序是 ①→③→②,也即在 setSelectedImage: 后,对最新的 selectedImage 备份了一份,等到访问 getter selectedImage 时再把之前备份的那个对象返回回去。

但实际的执行顺序却是 ①→②→③,也即在 [super setSelectedImage:] 里会先触发一次 [super selectedImage],在这里就获取绑定的 selectedImage,而此时③尚未执行,新的 selectedImage 其实还没绑定到 item 里,所以②拿到的是旧的图片,表现出来就是第二次设置的 selectedImage 无法生效。

4.2.3 里将会修复该问题,在此之前你可以将以下代码替换到本地同名文件内:

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // UITabBarItem.image 会一直保存原始的 image(例如 QMUIThemeImage),但 selectedImage 只会返回 rawImage,这导致了将一个 QMUIThemeImage 设置给 selectedImage 后,主题切换后 selectedImage 无法刷新(因为 UITabBarItem 并没有保存它,保存的是它的 rawImage),所以这里自己保存 image 的引用。
        // https://github.com/Tencent/QMUI_iOS/issues/1122
        OverrideImplementation([UITabBarItem class], @selector(setSelectedImage:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
            return ^(UITabBarItem *selfObject, UIImage *selectedImage) {

                // 必须先保存起来再执行 super,因为 setter 的 super 里会触发 getter,如果不先保存,就会导致走到 getter 时拿到的 boundObject 还是旧值
                // https://github.com/Tencent/QMUI_iOS/issues/1218
                [selfObject qmui_bindObject:selectedImage.qmui_isDynamicImage ? selectedImage : nil forKey:@"UITabBarItem(QMUIThemeCompatibility).selectedImage"];

                // call super
                void (*originSelectorIMP)(id, SEL, UIImage *);
                originSelectorIMP = (void (*)(id, SEL, UIImage *))originalIMPProvider();
                originSelectorIMP(selfObject, originCMD, selectedImage);
            };
        });

        OverrideImplementation([UITabBarItem class], @selector(selectedImage), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
            return ^UIImage *(UITabBarItem *selfObject) {

                // call super
                UIImage * (*originSelectorIMP)(id, SEL);
                originSelectorIMP = (UIImage * (*)(id, SEL))originalIMPProvider();
                UIImage *result = originSelectorIMP(selfObject, originCMD);

                UIImage *selectedImage = [selfObject qmui_getBoundObjectForKey:@"UITabBarItem(QMUIThemeCompatibility).selectedImage"];
                if (selectedImage) {
                    return selectedImage;
                }

                return result;
            };
        });
    });
}

@end
MoLice commented 3 years ago

已发布 4.2.3 修复该问题。