Open ChenYilong opened 5 years ago
发现MFMessageComposeViewController
无法通过 vc.modalPresentationStyle = .fullScreen
来设置全屏的模态,只能重写其-modalPresentationStyle
方法。不知道是否还有其他系统控制器存在类似情况
由于项目里面present的viewController比较多,逐一去修改比较麻烦,目前这种方式解决比较优雅,如果其他童鞋有比较优雅的解决方法劳烦告知一下: 1.添加ViewController分类 2.runtime交换ViewController setModalPresentationStyle 以及 presentViewController方法 3.ViewController分类中动态添加has_presentationStyle标志位 4.在交换setModalPresentationStyle 的方法中将标志位至为 true 5.在交换presentViewController的方法中判断ViewController对应的标志位是否为true,true代表有童鞋手动设置过presentationStyle的方式,不作修改,false代表未曾设置过presentationStyle 默认为 UIModalPresentationFullScreen
code:
static const char *key = "ab_hasSetPresentStyle"; @interface UIViewController() @property (nonatomic, assign) BOOL ab_hasSetPresentStyle; @end
@implementation UIViewController (PresentationStyle)
(BOOL)ab_hasSetPresentStyle { return objc_getAssociatedObject(self, key); }
(void)setAb_hasSetPresentStyle:(BOOL)ab_hasSetPresentStyle { objc_setAssociatedObject(self, key, @(ab_hasSetPresentStyle), OBJC_ASSOCIATION_ASSIGN); }
(void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self swizzleInstanceMethodWithClass:[self class] originalSEL:@selector(setModalPresentationStyle:) swizzledSEL:@selector(ab_setModalPresentationStyle:)];
[self swizzleInstanceMethodWithClass:[self class]
originalSEL:@selector(presentViewController:animated:completion:)
swizzledSEL:@selector(ab_presentViewController:animated:completion:)];
}); }
(void)ab_setModalPresentationStyle:(UIModalPresentationStyle)modalPresentationStyle { self.ab_hasSetPresentStyle = true; [self ab_setModalPresentationStyle:modalPresentationStyle]; }
(void)ab_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { if (!viewControllerToPresent.ab_hasSetPresentStyle) { viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen; } [self ab_presentViewController:viewControllerToPresent animated:flag completion:completion]; }
(BOOL)swizzleInstanceMethodWithClass:(Class)classObject originalSEL:(SEL)originalSEL swizzledSEL:(SEL)swizzledSEL { Method origMethod = class_getInstanceMethod(classObject, originalSEL); Method altMethod = class_getInstanceMethod(classObject, swizzledSEL); if (!origMethod || !altMethod) { return NO; }
BOOL didAddMethod = class_addMethod(classObject,swizzledSEL, method_getImplementation(altMethod), method_getTypeEncoding(altMethod));
if (didAddMethod) { class_replaceMethod(classObject,swizzledSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); } else { method_exchangeImplementations(origMethod, altMethod); }
return YES; } @end
如果当前控制器被 NavigationController 包裹,则需要修改 NavigationController 的 modalPresentationStyle 属性。只修改 当前控制器 的 modalPresentationStyle 属性是不够的
iOS13 present 返回之后会出现白屏?有遇到的吗
iOS13 present 返回之后会出现白屏?有遇到的吗 我也遇到了 请问你有解决吗?
由于项目里面present的viewController比较多,逐一去修改比较麻烦,目前这种方式解决比较优雅,如果其他童鞋有比较优雅的解决方法劳烦告知一下: 1.添加ViewController分类 2.runtime交换ViewController setModalPresentationStyle 以及 presentViewController方法 3.ViewController分类中动态添加has_presentationStyle标志位 4.在交换setModalPresentationStyle 的方法中将标志位至为 true 5.在交换presentViewController的方法中判断ViewController对应的标志位是否为true,true代表有童鞋手动设置过presentationStyle的方式,不作修改,false代表未曾设置过presentationStyle 默认为 UIModalPresentationFullScreen
code:
import "UIViewController+PresentationStyle.h"
import <objc/runtime.h>
static const char *key = "ab_hasSetPresentStyle"; @interface UIViewController() @Property (nonatomic, assign) BOOL ab_hasSetPresentStyle; @EnD
@implementation UIViewController (PresentationStyle)
(BOOL)ab_hasSetPresentStyle { return objc_getAssociatedObject(self, key); }
(void)setAb_hasSetPresentStyle:(BOOL)ab_hasSetPresentStyle { objc_setAssociatedObject(self, key, @(ab_hasSetPresentStyle), OBJC_ASSOCIATION_ASSIGN); }
(void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self swizzleInstanceMethodWithClass:[self class] originalSEL:@selector(setModalPresentationStyle:) swizzledSEL:@selector(ab_setModalPresentationStyle:)];
[self swizzleInstanceMethodWithClass:[self class] originalSEL:@selector(presentViewController:animated:completion:) swizzledSEL:@selector(ab_presentViewController:animated:completion:)];
}); }
(void)ab_setModalPresentationStyle:(UIModalPresentationStyle)modalPresentationStyle { self.ab_hasSetPresentStyle = true; [self ab_setModalPresentationStyle:modalPresentationStyle]; }
(void)ab_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { if (!viewControllerToPresent.ab_hasSetPresentStyle) { viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen; } [self ab_presentViewController:viewControllerToPresent animated:flag completion:completion]; }
(BOOL)swizzleInstanceMethodWithClass:(Class)classObject originalSEL:(SEL)originalSEL swizzledSEL:(SEL)swizzledSEL { Method origMethod = class_getInstanceMethod(classObject, originalSEL); Method altMethod = class_getInstanceMethod(classObject, swizzledSEL); if (!origMethod || !altMethod) { return NO; } BOOL didAddMethod = class_addMethod(classObject,swizzledSEL, method_getImplementation(altMethod), method_getTypeEncoding(altMethod)); if (didAddMethod) { class_replaceMethod(classObject,swizzledSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); } else { method_exchangeImplementations(origMethod, altMethod); } return YES; } @EnD
真心不建议用Runtime形式修改,影响大、后续维护成本高。
@zhangjirui 小项目挨个修改还行,大项目挨个修改真心不现实,这位同学有没有更好的实现思路?
@AsHighAsHonor 设置下modalPresentationStyle属性就可以了
由于项目里面present的viewController比较多,逐一去修改比较麻烦,目前这种方式解决比较优雅,如果其他童鞋有比较优雅的解决方法劳烦告知一下: 1.添加ViewController分类 2.runtime交换ViewController setModalPresentationStyle 以及 presentViewController方法 3.ViewController分类中动态添加has_presentationStyle标志位 4.在交换setModalPresentationStyle 的方法中将标志位至为 true 5.在交换presentViewController的方法中判断ViewController对应的标志位是否为true,true代表有童鞋手动设置过presentationStyle的方式,不作修改,false代表未曾设置过presentationStyle 默认为 UIModalPresentationFullScreen
code:
import "UIViewController+PresentationStyle.h"
import <objc/runtime.h>
static const char *key = "ab_hasSetPresentStyle"; @interface UIViewController() @Property (nonatomic, assign) BOOL ab_hasSetPresentStyle; @EnD
@implementation UIViewController (PresentationStyle)
(BOOL)ab_hasSetPresentStyle { return objc_getAssociatedObject(self, key); }
(void)setAb_hasSetPresentStyle:(BOOL)ab_hasSetPresentStyle { objc_setAssociatedObject(self, key, @(ab_hasSetPresentStyle), OBJC_ASSOCIATION_ASSIGN); }
(void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self swizzleInstanceMethodWithClass:[self class] originalSEL:@selector(setModalPresentationStyle:) swizzledSEL:@selector(ab_setModalPresentationStyle:)];
[self swizzleInstanceMethodWithClass:[self class] originalSEL:@selector(presentViewController:animated:completion:) swizzledSEL:@selector(ab_presentViewController:animated:completion:)];
}); }
(void)ab_setModalPresentationStyle:(UIModalPresentationStyle)modalPresentationStyle { self.ab_hasSetPresentStyle = true; [self ab_setModalPresentationStyle:modalPresentationStyle]; }
(void)ab_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { if (!viewControllerToPresent.ab_hasSetPresentStyle) { viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen; } [self ab_presentViewController:viewControllerToPresent animated:flag completion:completion]; }
(BOOL)swizzleInstanceMethodWithClass:(Class)classObject originalSEL:(SEL)originalSEL swizzledSEL:(SEL)swizzledSEL { Method origMethod = class_getInstanceMethod(classObject, originalSEL); Method altMethod = class_getInstanceMethod(classObject, swizzledSEL); if (!origMethod || !altMethod) { return NO; } BOOL didAddMethod = class_addMethod(classObject,swizzledSEL, method_getImplementation(altMethod), method_getTypeEncoding(altMethod)); if (didAddMethod) { class_replaceMethod(classObject,swizzledSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); } else { method_exchangeImplementations(origMethod, altMethod); } return YES; } @EnD
这个解决防范存在几个问题: 1、NavigationController 包裹后,这么设置是无效的; 2、如果某个地方有特定样式的,这里会全部给替换掉,造成异常; 3、还有一个坑,系统里默认值可能会不起作用。
/*
Defines the presentation style that will be used for this view controller when it is presented modally. Set this property on the view controller to be presented, not the presenter.
If this property has been set to UIModalPresentationAutomatic, reading it will always return a concrete presentation style. By default UIViewController resolves UIModalPresentationAutomatic to UIModalPresentationPageSheet, but system-provided subclasses may resolve UIModalPresentationAutomatic to other concrete presentation styles. Participation in the resolution of UIModalPresentationAutomatic is reserved for system-provided view controllers.
Defaults to UIModalPresentationAutomatic on iOS starting in iOS 13.0, and UIModalPresentationFullScreen on previous versions. Defaults to UIModalPresentationFullScreen on all other platforms.
*/
@property(nonatomic,assign) UIModalPresentationStyle modalPresentationStyle API_AVAILABLE(ios(3.2));
谷歌翻译如下:
定义以模态显示时将用于此视图控制器的显示样式。 在要显示的视图控制器(而不是演示者)上设置此属性。 如果将此属性设置为UIModalPresentationAutomatic,则读取该属性将始终返回具体的呈现样式。 默认情况下,UIViewController将UIModalPresentationAutomatic解析为UIModalPresentationPageSheet,但是系统提供的子类可以将UIModalPresentationAutomatic解析为其他具体的呈现样式。 保留UIModalPresentationAutomatic的分辨率供系统提供的视图控制器使用。 从iOS 13.0开始,在iOS上默认为UIModalPresentationAutomatic,在以前的版本上默认为UIModalPresentationFullScreen。 在所有其他平台上,默认为UIModalPresentationFullScreen。
要注意了iOS13之后UIModalPresentationAutomatic只是一个虚拟占位的值,并非一个确定的样式,系统默认是UIModalPresentationAutomatic之后,他会根据某种条件去给modalPresentationStyle赋某个样式值。例如API注释中提到的,UIViewController将UIModalPresentationAutomatic解析为UIModalPresentationPageSheet。 如果所有的VC都是继承自定义父类并且跳转都是父类完成的,那么只需要在父类地方处理添加
modalPresentationStyle = UIModalPresentationFullScreen
如果很多自己写的VC,或者集成系统的VC,那么就要注意了。 目前觉得全局搜索手动去改比较有效(虽然很傻的解决方案)。 期望更好的方案。
@EchoZuo 如果集成的三方,或者framework 没有办法改,可以看看我的方法
Action
正文
13的present 默认有视差效果,模态出来的界面现在默认都下滑返回。
一些页面必须要点确认才能消失的,需要适配。
如果项目中页面高度全部是屏幕尺寸,那么多出来的导航高度会出现问题。
可以 手动写
//TODO:
有很多自定义的弹框,使用的是 .custom ,sheet alert bubble 之类。