自 IOS 13 以来,UISearchController 在状态栏中意外绘制了 UITableView
UISearchController unexpected paint of UITableView in status bar since IOS 13
我在非常基本的 UINavigationBar 中有一个非常基本的 UITableController,它使用非常基本的 UISearchController(但是 hidesNavigationBarDuringPresentation = YES
)。
我遇到的问题是,如果搜索处于活动状态,则 UITableView 滚动条在状态栏中可见。请注意,此行为是自 IOS13 以来的新行为,之前由于不透明的搜索栏而没有发生。对我来说,它看起来更像是一个 IOS 错误,而不是预期的行为。
任何建议如何解决这个问题?理想情况下,我希望拥有与 IOS12 和之前相同的外观。
IOS 13,状态栏滚动可见
IOS12、搜索栏中途不透明:没问题
(来源:generomobile.de)
#import <UIKit/UIKit.h>
@interface MyAppDelegate : UIResponder <UIApplicationDelegate,UITableViewDelegate,UITableViewDataSource,UISearchResultsUpdating>
{
UIWindow* _win;
UINavigationController* _nav;
UITableViewController* _tc;
NSMutableArray* _model;
NSArray* _filteredModel;
UISearchController* _search;
UILabel* _label;
}
@end
@implementation MyAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_win=[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIViewController* c=[[UIViewController alloc] init];
UIView* v=c.view;
v.backgroundColor=UIColor.whiteColor;
_label=[[UILabel alloc] initWithFrame:CGRectMake(10, 100, 200, 20)];
_model=[NSMutableArray array];
_label.text=@"Tap on 'Show Table'";
[v addSubview:_label];
for (int i=0;i<200;i++) {
_model[i]=[NSString stringWithFormat:@"Item %d",i];
}
_nav=[[UINavigationController alloc] initWithRootViewController:c];
c.navigationItem.title=@"A title";
UIBarButtonItem* b=[[UIBarButtonItem alloc] initWithTitle:@"Show Table" style:UIBarButtonItemStylePlain target:self action:@selector(showTable:)];
c.navigationItem.rightBarButtonItem=b;
_tc=[[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
_tc.definesPresentationContext=YES; //have to do it for other reasons
_search=[[UISearchController alloc] initWithSearchResultsController:nil];
_search.obscuresBackgroundDuringPresentation=NO; //needed to be able to tap in results
_search.hidesNavigationBarDuringPresentation=YES; //have to do it for other reasons
_search.searchResultsUpdater=self;
UITableView* tv=_tc.tableView;
tv.delegate=self;
tv.dataSource=self;
tv.tableHeaderView=_search.searchBar;
_win.rootViewController=_nav;
[_win makeKeyAndVisible];
return YES;
}
-(void)showTable:(UIBarButtonItem*)b {
[_nav pushViewController:_tc animated:YES];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
_label.text=[NSString stringWithFormat:@"You selected row:%ld",(long)indexPath.row];
[_nav popViewControllerAnimated:TRUE];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _search.active?_filteredModel.count:_model.count;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString* reuse=@"whatever";
UITableViewCell* cell=[tableView dequeueReusableCellWithIdentifier:reuse];
if (cell==nil) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuse];
}
NSInteger row=indexPath.row;
cell.textLabel.text=_search.active?_filteredModel[row]:_model[row];
return cell;
}
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
NSString* searchText=searchController.searchBar.text;
if (searchText.length==0) {
_filteredModel=_model;
} else {
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"SELF contains[c] %@",searchText];
_filteredModel=[_model filteredArrayUsingPredicate:predicate];
}
[_tc.tableView reloadData];
}
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));
}
}
此(错误)行为的原因是 IOS13 上的 UISearchBarBackground
视图不再覆盖状态栏。它确实在以前的 IOS 版本中涵盖了它。
IOS12UISearchBarBackground
覆盖整个上层区域
IOS13 UISearchBarBackground
不覆盖状态栏
(来源:generomobile.de)
我在没有触及代码中任何其他 ViewController 层次结构的情况下提出的解决方案是一个非常讨厌的 hack,扩展 UISearchBarBackground
的框架以匹配 [=31] 之前的状态=] 版本。
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
NSString* searchText=searchController.searchBar.text;
if (@available(iOS 13.0, *)) {
if (searchController.active) {
//The intrinsic UISearchBar code seems to adjust the
//background view of UISearchBar later on thats why we
//need to delay
[self performSelector:@selector(adjustBg) withObject:nil afterDelay:0.1];
}
}
if (searchText.length==0) {
_filteredModel=_model;
} else {
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"SELF contains[c] %@",searchText];
_filteredModel=[_model filteredArrayUsingPredicate:predicate];
}
[_tc.tableView reloadData];
}
CGPoint pointInScreen(UIView* v,CGPoint pt)
{
CGPoint ptWin=[v.superview convertPoint:pt toView:nil];
CGPoint ptScreen=[v.window convertPoint:ptWin toWindow:nil];
return ptScreen;
}
-(void)adjustBg
{
//ugly: we try to adjust the background view of the UISearchBar
//to the top of the screen (like it was in pre IOS13 times)
if (!_search.active) {
return;
}
UISearchBar* bar=_search.searchBar;
if (bar.subviews.count==0) { return; }
UIView* first=bar.subviews[0];
if (first.class!=UIView.class || first.subviews.count==0) { return;}
UIView* bg=first.subviews[0];
if ([bg isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
CGSize size=bg.frame.size;
CGPoint orig=bg.frame.origin;
CGPoint p=pointInScreen(bg,orig);
CGPoint p2=pointInScreen(bg, CGPointMake(0, orig.y+size.height));
if (p.y>0) { //not top of screen
bg.frame=CGRectMake(orig.x,-p.y,size.width,p2.y);
}
}
}
这确实不是我一直在寻找的解决方案,但只要没有更好的解决方案,我就需要维护它(并且 post 它在这里以防其他人偶然发现)
我在非常基本的 UINavigationBar 中有一个非常基本的 UITableController,它使用非常基本的 UISearchController(但是 hidesNavigationBarDuringPresentation = YES
)。
我遇到的问题是,如果搜索处于活动状态,则 UITableView 滚动条在状态栏中可见。请注意,此行为是自 IOS13 以来的新行为,之前由于不透明的搜索栏而没有发生。对我来说,它看起来更像是一个 IOS 错误,而不是预期的行为。
任何建议如何解决这个问题?理想情况下,我希望拥有与 IOS12 和之前相同的外观。
IOS 13,状态栏滚动可见
IOS12、搜索栏中途不透明:没问题
(来源:generomobile.de)
#import <UIKit/UIKit.h>
@interface MyAppDelegate : UIResponder <UIApplicationDelegate,UITableViewDelegate,UITableViewDataSource,UISearchResultsUpdating>
{
UIWindow* _win;
UINavigationController* _nav;
UITableViewController* _tc;
NSMutableArray* _model;
NSArray* _filteredModel;
UISearchController* _search;
UILabel* _label;
}
@end
@implementation MyAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
_win=[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIViewController* c=[[UIViewController alloc] init];
UIView* v=c.view;
v.backgroundColor=UIColor.whiteColor;
_label=[[UILabel alloc] initWithFrame:CGRectMake(10, 100, 200, 20)];
_model=[NSMutableArray array];
_label.text=@"Tap on 'Show Table'";
[v addSubview:_label];
for (int i=0;i<200;i++) {
_model[i]=[NSString stringWithFormat:@"Item %d",i];
}
_nav=[[UINavigationController alloc] initWithRootViewController:c];
c.navigationItem.title=@"A title";
UIBarButtonItem* b=[[UIBarButtonItem alloc] initWithTitle:@"Show Table" style:UIBarButtonItemStylePlain target:self action:@selector(showTable:)];
c.navigationItem.rightBarButtonItem=b;
_tc=[[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
_tc.definesPresentationContext=YES; //have to do it for other reasons
_search=[[UISearchController alloc] initWithSearchResultsController:nil];
_search.obscuresBackgroundDuringPresentation=NO; //needed to be able to tap in results
_search.hidesNavigationBarDuringPresentation=YES; //have to do it for other reasons
_search.searchResultsUpdater=self;
UITableView* tv=_tc.tableView;
tv.delegate=self;
tv.dataSource=self;
tv.tableHeaderView=_search.searchBar;
_win.rootViewController=_nav;
[_win makeKeyAndVisible];
return YES;
}
-(void)showTable:(UIBarButtonItem*)b {
[_nav pushViewController:_tc animated:YES];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
_label.text=[NSString stringWithFormat:@"You selected row:%ld",(long)indexPath.row];
[_nav popViewControllerAnimated:TRUE];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _search.active?_filteredModel.count:_model.count;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString* reuse=@"whatever";
UITableViewCell* cell=[tableView dequeueReusableCellWithIdentifier:reuse];
if (cell==nil) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuse];
}
NSInteger row=indexPath.row;
cell.textLabel.text=_search.active?_filteredModel[row]:_model[row];
return cell;
}
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
NSString* searchText=searchController.searchBar.text;
if (searchText.length==0) {
_filteredModel=_model;
} else {
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"SELF contains[c] %@",searchText];
_filteredModel=[_model filteredArrayUsingPredicate:predicate];
}
[_tc.tableView reloadData];
}
@end
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));
}
}
此(错误)行为的原因是 IOS13 上的 UISearchBarBackground
视图不再覆盖状态栏。它确实在以前的 IOS 版本中涵盖了它。
IOS12UISearchBarBackground
覆盖整个上层区域
IOS13 UISearchBarBackground
不覆盖状态栏
(来源:generomobile.de)
我在没有触及代码中任何其他 ViewController 层次结构的情况下提出的解决方案是一个非常讨厌的 hack,扩展 UISearchBarBackground
的框架以匹配 [=31] 之前的状态=] 版本。
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
NSString* searchText=searchController.searchBar.text;
if (@available(iOS 13.0, *)) {
if (searchController.active) {
//The intrinsic UISearchBar code seems to adjust the
//background view of UISearchBar later on thats why we
//need to delay
[self performSelector:@selector(adjustBg) withObject:nil afterDelay:0.1];
}
}
if (searchText.length==0) {
_filteredModel=_model;
} else {
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"SELF contains[c] %@",searchText];
_filteredModel=[_model filteredArrayUsingPredicate:predicate];
}
[_tc.tableView reloadData];
}
CGPoint pointInScreen(UIView* v,CGPoint pt)
{
CGPoint ptWin=[v.superview convertPoint:pt toView:nil];
CGPoint ptScreen=[v.window convertPoint:ptWin toWindow:nil];
return ptScreen;
}
-(void)adjustBg
{
//ugly: we try to adjust the background view of the UISearchBar
//to the top of the screen (like it was in pre IOS13 times)
if (!_search.active) {
return;
}
UISearchBar* bar=_search.searchBar;
if (bar.subviews.count==0) { return; }
UIView* first=bar.subviews[0];
if (first.class!=UIView.class || first.subviews.count==0) { return;}
UIView* bg=first.subviews[0];
if ([bg isKindOfClass:NSClassFromString(@"UISearchBarBackground")]) {
CGSize size=bg.frame.size;
CGPoint orig=bg.frame.origin;
CGPoint p=pointInScreen(bg,orig);
CGPoint p2=pointInScreen(bg, CGPointMake(0, orig.y+size.height));
if (p.y>0) { //not top of screen
bg.frame=CGRectMake(orig.x,-p.y,size.width,p2.y);
}
}
}
这确实不是我一直在寻找的解决方案,但只要没有更好的解决方案,我就需要维护它(并且 post 它在这里以防其他人偶然发现)