如何在当前 UITableViewController 上方添加一个 UIView

Posted

技术标签:

【中文标题】如何在当前 UITableViewController 上方添加一个 UIView【英文标题】:How to add a UIView above the current UITableViewController 【发布时间】:2011-01-09 21:18:39 【问题描述】:

我很难从 UITableViewController 的 viewDidLoad 方法中添加子视图 (UIView)

这行得通:

[self.view addSubview:self.progView];

但是你可以看到表格单元格线通过 UIView progView 流血。

我试过这种方法:

[self.view.superview insertSubview:self.progView aboveSubview:self.view];

这是尝试将 progView、UIView 添加到当前视图上方的超级视图。当我尝试这个时,UIView 永远不会出现。

-- 更新--

以下是最近的尝试:

UIView *myProgView = (UIView *)self.progView; //progView is a method that returns a UIView

[self.tableView insertSubview:myProgView aboveSubview:self.tableView]; 
[self.tableView bringSubviewToFront:myProgView];

结果与 [self.view addSubview:self.progView] 相同; UIView 出现但似乎在表格后面。

【问题讨论】:

您是否尝试过使用aboveSubview:self.view?我并不是说它会解决您的问题,但听起来它会消除您收到的错误消息。 是的,我确实尝试过。确实我失去了警告,但它仍然没有呈现视图。不过我会编辑我的帖子。 一个简单的方法是设置progVIew的框架,我确定哪个可以工作。任何约束都不起作用,包括 VFL 【参考方案1】:

我尝试了上述方法,但没有成功。我还发现它需要太多的配置和代码,因为它需要从头开始设置表格视图(这在故事板中很容易完成)。

相反,我将想要添加到 UITableView 上方的视图添加到 UITableViewController 的 UINavigationController 的视图中,如下所示:

[self.navigationController.view addSubview:<view to add above the table view>];

这种方法需要你在 UINavigationController 中嵌入 UITableViewController,但即使你不需要导航控制器,你仍然可以使用这种方法,只是隐藏导航栏。

【讨论】:

你的解决方案的问题是,当你这样做的时候,视图也被添加到了导航栏上 你可以这样做:[self.navigationController.view insertSubview:holderView belowSubview:self.navigationController.navigationBar]; 另一个问题是添加的视图仍然在 UINavigationController 的顶部,即使在推送新的 UIViewController 之后也是如此。 随着时间的推移,我已经开始不再使用表格视图控制器,除非视图应该完全是一个列表并且您想要强制执行它。如果您改为将表视图添加到常规视图控制器并设置数据源并委托自己,您将获得更灵活的设置,其中表视图只是主视图中的另一个视图,而不是主视图本身。这将使您摆脱表格视图控制器的许多内置限制。 @Raphael 我用 viewDidAppear 弄明白了,让视图出现,然后在 viewWillDisappear 方法上,你可以 removeFromSuperView【参考方案2】:

距离我最初的答案已经过去了 7 年,我碰巧再次偶然发现了这个问题。让我们一劳永逸地解决这个问题:

    viewDidLoad 中,将您的子视图添加到(表格)视图中。 相对于安全区域布局指南固定约束。这会阻止它随着表格内容滚动(如 cornr's answer 中所指出的那样)。 在viewDidLayoutSubviews 中,将子视图置于最前面。这样可以确保它不会在表格分隔符后面丢失。

斯威夫特:

override func viewDidLoad() 
    super.viewDidLoad()

    // 1.
    view.addSubview(mySubview)

    // 2. For example:
    mySubview.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        mySubview.widthAnchor.constraint(equalToConstant: 100),
        mySubview.heightAnchor.constraint(equalToConstant: 100),
        mySubview.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
        mySubview.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor)
    ])


override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()

    // 3.
    view.bringSubviewToFront(mySubview)

目标-C:

- (void)viewDidLoad

    [super viewDidLoad];

    // 1.
    [self.view addSubview:self.mySubview];

    // 2.
    self.mySubview.translatesAutoresizingMaskIntoConstraints = false;
    [NSLayoutConstraint activateConstraints:@[
        [self.mySubview.widthAnchor constraintEqualToConstant:100],
        [self.mySubview.heightAnchor constraintEqualToConstant:100],
        [self.mySubview.centerXAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerXAnchor],
        [self.mySubview.centerYAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerYAnchor]
    ]];


- (void)viewDidLayoutSubviews

    [super viewDidLayoutSubviews];

    // 3.
    [self.view bringSubviewToFront:self.mySubview];

唷,很高兴已经完成了!看到这个答案有多理智,我会省略我原来的答案。

有趣的事实:7 年过去了,我仍然是一名 ios 开发人员。

【讨论】:

我不明白它是如何工作的,当你执行- (UITableView*)tableView return mainTableView; 并在mainTableView = self.tableView; 之后调用它就像在执行mainTableView = mainTableView... @AncAinu 如果您阅读我在代码下方的注释,您会发现需要停止丢失对它的引用。不明白为什么这值得一票否决。 @ThomasVerbeek 因为您所做的完全无法使用。 - (UITableView*)tableView 这替换了原来的self.tableView,所以当你调用mainTableView = self.tableView; 就像调用mainTableView = mainTableView 并且因为你之前从未分配过它,这就像在调用nil = nil。您的 mainTableView 永远不会在此代码中填充任何内容。 @goggelj 在 setter 方法中设置 super.view = newValuesuper.tableView = newValue 并且它会工作。 出色的工作。提醒我下次见到你时给你买杯啤酒,Thomas【参考方案3】:

我已经能够通过使用 uiviewcontroller 容器在 uitableviewcontroller 之上添加子视图。

UITableViewController 在静态单元格方面实际上非常方便,这可能是唯一一次常见的答案“只使用 uitableview”实际上可能不可行。

所以我就是这样做的。

    给您的 UITableViewController 一个 StoryBoard 标识符,即 MyStaticTableView 创建一个全新的 UIViewController 子类并将其命名为 UITableViewControllerContainer 将此控制器放置在情节提要中的 UITableViewController 的位置 向新控制器添加子视图并将其链接到名为“view_container”的出口 在你身上UITableViewControllerContainer viewDidLoad 方法

添加如下代码:

- (void)viewDidLoad

    [super viewDidLoad];
    // Do any additional setup after loading the view.
    UITableViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"MyStaticTableView"];
    [self addChildViewController:vc];
    [self.view_container addSubview:vc.view];

您可能遇到的问题:

    如果您有额外的顶部空间,请确保将标志“想要全屏”添加到您的 UITableViewController 如果它没有在你的 UITableViewControllerContainer 上正确调整大小

添加代码:

- (void)viewWillAppear:(BOOL)animated

    [[self.view_container.subviews lastObject] setFrame:self.view.frame];

此时,您可以通过 UITableViewController 直接访问容器视图

self.view.superview.superview

您添加的任何内容都将显示在您的表格视图控制器顶部

【讨论】:

从 iOS 6.0 及更高版本开始,UIContainerView 使解决方案变得更加容易。【参考方案4】:

斯威夫特 2018

这是我的故事板方式:

1) 在情节提要中添加视图。

2) 将其与 UITableViewController 类链接:

@IBOutlet weak var copyrightLabel: UILabel!

3) 在代码中添加

self.navigationController?.view.addSubview(copyrightView)
copyrightView.frame = CGRect(x: 0,
                            y: self.view.bounds.size.height - copyrightView.bounds.size.height,
                            width: self.view.bounds.size.width,
                            height: copyrightView.bounds.size.height)

4) 瞧!

视图不会随着表格视图滚动。它可以从情节提要中轻松设计。

注意: 此解决方案将子视图添加到导航控制器,如果您要从此处进一步向下导航到另一个屏幕,您会发现此子视图会持续存在,在执行 segue 时使用 viewDidDisappear 上的 copyrightView.removeFromSuperView 将其删除。

【讨论】:

非常适合我。干得好。 干得好!它帮助了我。如果您偶然发现此问题,仅适用于其他人:此解决方案将子视图添加到导航控制器,如果您要从此处进一步向下导航到另一个屏幕,您会发现此子视图持续存在,在执行时使用 viewDidDisappear 上的 copyrightView.removeFromSuperView 将其删除继续。【参考方案5】:

您可以增加视图层的 zPosition。

这将使它显示在其他视图上方(默认情况下,zPosition 等于 0)

self.progView.layer.zPosition++;
[self.view addSubview:self.progView];

【讨论】:

这对我有用。我只是使用具有 UITableView 的普通 UIViewController 并且我想放置一个位于表格视图顶部的页脚视图。出于某种原因,如果我通过bringSubviewToFront 调用或通过故事板将足部查看器带到前面,UITableView 将被推下。这在最顶端留下了很大的差距。我不确定为什么 UITableView 在视图层次结构中不能有任何东西。更改页脚视图层的zPosition 属性成功地将其置于顶部,而不会更改 UITableView 的位置。 这可行,但层不处理用户交互(这就是我们有 UIView 的原因)。只是指出这一点。 它与我的自定义 UIActivityIndi​​catorView 配合得很好!作为一个指标,不需要用户交互,所以它非常适合我的需求。我终于可以证明海报被卡住了,因为它们正在加载。 :)【参考方案6】:

由于 UITableViewController 是 UIViewController 的子类,因此您需要将所需的视图添加到其父视图中。

Swift:
self.view.superview?.addSubview(viewTobeAdded)

Objective C:
[self.view.superview addSubview: viewTobeAdded];

【讨论】:

self.view.superview 对于我的 UITableViewController 是 nil(在 viewDidLoad 中) @Jovan:是的,但我仍然认为 Saikiran 的回答极简主义的回答很棒。在 viewDidAppear 或 viewDidLayoutSubviews 中 self.view.superview 不为零。有趣的是,当您在控制台中打印出 self.view.superview 时,它会打印: >。这个 UIViewControllerWrapperView 的名字很有意义。【参考方案7】:

问题在于UITableViewControllerview 属性与tableView 属性相同。这意味着根视图始终是一个表视图控制器,并且作为子视图添加的任何内容都将受制于表视图功能。这还有其他不良副作用,例如您的子视图在您可能不希望它们滚动时滚动。

这里有几个选项。您可以覆盖 loadView 并安装您自己的视图和表格视图:

// note: untested
- (void)loadView 
    self.view = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
    self.view.backgroundColor = [UIColor whiteColor];

    UITableView *tblView = [[UITableView alloc]
        initWithFrame:CGRectZero
        style:UITableViewStylePlain
    ];

    tblView.autoresizingMask =
        UIViewAutoresizingFlexibleWidth |
        UIViewAutoresizingFlexibleHeight
    ;

    self.tableView = tblView;
    [self.view addSubview:tblView];
    [tblView release];

然后当你需要添加子视图时,在self.tableView下方或上方酌情添加。

另一种选择是创建一个 UIViewController 子类来满足您的需求。 UITableViewController 老实说并没有添加那么多,而且它实现的小功能很容易复制。像Recreating UITableViewController to increase code reuse 这样的文章解释了如何很容易地做到这一点。

【讨论】:

感谢您的帮助。不幸的是,我真的很难让你的第一个建议奏效。也许我正在尝试做的事情是不可能的。在这一点上,我试图使 UIView 成为 UITableViewController 子类的 self.view 。然后让 UITableView 成为 UIView 的子视图。似乎不可能做到这一点,我真的需要根据你的第二个建议将我的 UITableViewController 转换为 UIViewController 。任何可能有用的教程或示例代码的想法或指针。提前致谢。 我不会在 loadView 中引用 self.view 来读取,你最终会陷入无限循环。只分配给它。【参考方案8】:

我遇到了类似的问题,用下面的代码解决了:

     [self.navigationController.view insertSubview:<subview> 
       belowSubview:self.navigationController.navigationBar];

这会使用 Stack 中的控制器将视图插入正确的位置。

【讨论】:

【参考方案9】:

Apple 示例“iPhoneCoreDataRecipes”使用 NIB 将标头加载到 UITableView。 见here:

【讨论】:

我同意这个答案!【参考方案10】:

我在桌子上方添加了一张图片。我使用了这段代码:

self.tableView.tableHeaderView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"header.png"]];

【讨论】:

【参考方案11】:

swift的解决方案(相当于Daniel Saidi):

如果您的控制器是 Storyboard 或 XIB 中的 UITableViewController,并且您希望在保留现有表格视图的同时将 self.view 重新分配给标准 UIView:

@IBOutlet var tableViewReference: UITableView!
var viewReference: UIView!

然后在你的实现文件中:

将这些实例变量添加到您的表视图控制器文件中:

override var tableView: UITableView! 
    get  return tableViewReference 
    set  super.tableView = newValue 


override var view: UIView! 
    get  return viewReference 
    set  super.view = newValue 


override func viewDidLoad() 
    super.viewDidLoad()
    self.edgesForExtendedLayout = UIRectEdge.None
    self.extendedLayoutIncludesOpaqueBars = false
    self.automaticallyAdjustsScrollViewInsets = false

    //rewiring views due to add tableView as subview to superview
    viewReference = UIView.init(frame: tableViewReference.frame)
    viewReference.backgroundColor = tableViewReference.backgroundColor
    viewReference.autoresizingMask = tableViewReference.autoresizingMask
    viewReference.addSubview(tableViewReference)

在您的 Storyboard 或 XIB 文件中:将 UITableViewController 中的 tableView 连接到 tableViewReference 变量。

然后您就可以按如下方式添加子视图:

self.view.addSubView(someView)

【讨论】:

【参考方案12】:

iOS 11 更新:

现在可以在对安全区域使用 AutoLayout 约束时将子视图添加到 UITableView。这些视图不会沿着 TableView 滚动。

此示例将一个视图放置在 NavigationBar 下方的 UITableView 顶部 UITableViewController

[self.tableView addSubview:self.topBarView];
[NSLayoutConstraint activateConstraints:@[
    [self.topBarView.topAnchor constraintEqualToAnchor:self.tableView.safeAreaLayoutGuide.topAnchor],
    [self.topBarView.leadingAnchor constraintEqualToAnchor:self.tableView.safeAreaLayoutGuide.leadingAnchor],
    [self.topBarView.trailingAnchor constraintEqualToAnchor:self.tableView.safeAreaLayoutGuide.trailingAnchor],
    [self.topBarView.heightAnchor constraintEqualToConstant:40.0]
]];

【讨论】:

你能解释一下viewController是什么以及它和UITableViewController之间的关系吗? 它应该是UITableViewController 或者更确切地说是它的tableView。我从我的 UINavigationController 子类中复制了代码,该子类引用了显示的 ViewController(在我的例子中是 UITableViewController)。我修复了这个例子。 这适用于定位子视图。但是,我发现 tableView 部分标题呈现在我的视图之上。如果我找到解决方案,我会更新。【参考方案13】:

试试这样的:

[self.tableView addSubview:overlayView];
overlayView.layer.zPosition = self.tableView.backgroundView.layer.zPosition + 1;

【讨论】:

【参考方案14】:

您可以简单地将以下代码放在 viewDidAppear 中:

[self.tableView.superview addSubview:<your header view>];

【讨论】:

【参考方案15】:

试试:[self.view bringSubviewToFront:self.progView];

或者您可以尝试将self.progView 添加到您的表格视图中。

【讨论】:

[self.view bringSubViewToFront:self.progView];给我警告:'UIView' 可能无法响应'-bringSubViewToFront:' 并导致应用程序因“无法识别的选择器发送到实例”异常而崩溃。 那么你的 Controller 是 UITableViewController 吗? 是的,我的控制器是 UITableViewController。 然后将 self.view 替换为 yourTable 这似乎与 [self.view addSubview:self.progView] 的结果相同; UIView 出现,但在桌子后面。将通过帖子更新此附加信息和我正在根据您的建议尝试的当前代码。【参考方案16】:

为了在 UITableViewController 中保持 UIView 高于表视图,我使用了一个(或多个)委托方法 (UITableViewDelegate)。

override func viewDidLoad() 
    super.viewDidLoad()
    self.tableView.addSubview(headerView)


func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) 
    tableView.addSubview(overlayView) // adds view always on top

// if using footers in table view
tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int)  ... 

由于视图只能有一个超级视图,接缝也是很好的解决方案,如果我错了,请纠正我。仍然可以达到 60fps,所以对我来说没问题。

【讨论】:

你是如何布置overlayViewheaderVew的?【参考方案17】:

斯威夫特 4

这是我们重新组合视图层次结构的许多答案的最简化版本。这种方法不需要额外的故事板/笔尖出口,也适用于以编程方式构建的实例。

class MyTableViewController: UITableViewController 

    var strongTableView: UITableView?

    override var tableView: UITableView! 
        get 
            return strongTableView ?? super.tableView
        
        set 
            strongTableView = newValue
        
    

    override func viewDidLoad() 
        super.viewDidLoad()

        // theoretically we could use self.tableView = self.tableView but compiler will not let us assign a property to itself
        self.tableView = self.view as? UITableView
        self.view = UIView(frame: self.tableView!.frame)
        self.view.addSubview(tableView)

    


【讨论】:

【参考方案18】:

要在当前 UITableViewController 之上添加一个 customView,它必须是使用 'self.navigationController.view addSubview:customView' 的好方法,就像 Daniel 评论的那样。

但是,在实现用作导航栏的customView的情况下,Daniel的方式可能会导致在UITableViewController前后的其他navigationViewControllers上的默认或自定义navigationBar出现意外结果。

最好的简单方法是将 UITableViewController 转换为 UIViewController ,它对子视图的布局没有限制。但是,如果您正在为大量、长期遗留的 UITableViewController 代码而苦苦挣扎,那么情况就完全不同了。我们没有任何转换时间。

在这种情况下,您可以简单地劫持 UITableViewController 的 tableView 并解决整个问题。

我们应该知道的最重要的事情是UITableViewController的'self.view.superview'是nil,'self.view'是UITableView本身。

首先,劫持 UITableVIew。

UITableView *tableView = self.tableView;

然后,用新的 UIView 替换 'self.view'(现在是 UITableView),这样我们就可以无限制地布局 customView。

UIView *newView = UIView.new;
newView.frame = tableView.frame;
self.view = newView;

然后,将我们之前劫持的 UITableView 放到新的 self.view 上。

[newView addSubview:tableView];
tableView.translatesAutoresizingMaskIntoConstraints = NO;
[tableView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
[tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
[tableView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;

现在,我们可以在 UITableViewController 上的这个全新的花哨的“self.view”上做任何我们想做的事情。

带一个自定义视图,然后添加为子视图。

UIView *myNaviBar = UIView.new;
[myNaviBar setBackgroundColor:UIColor.cyanColor];
[self.view addSubview:myNaviBar];

myNaviBar.translatesAutoresizingMaskIntoConstraints = NO;
[myNaviBar.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[myNaviBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
[myNaviBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
[myNaviBar.heightAnchor constraintEqualToConstant:90].active = YES;

gif

【讨论】:

【参考方案19】:

可能有理由不这样做,但到目前为止这对我有用。如果你使用一个应用程序

viewDidLayoutSubviews 中你可以运行它,但要确保只运行一次

self.searchTableView = [[UITableView alloc] initWithFrame:self.tableView.frame style:UITableViewStylePlain];
self.searchTableView.backgroundColor = [UIColor purpleColor];
[self.view.superview addSubview:self.searchTableView];

【讨论】:

【参考方案20】:

我有一个类似的问题,我想在我的 UITableViewController 上添加一个加载指示器。为了解决这个问题,我将 UIView 添加为窗口的子视图。这解决了问题。我就是这样做的。

-(void)viewDidLoad
    [super viewDidLoad];
    //get the app delegate
    XYAppDelegate *delegate = [[UIApplication sharedApplication] delegate];

    //define the position of the rect based on the screen bounds
    CGRect loadingViewRect = CGRectMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2, 50, 50);
    //create the custom view. The custom view is a property of the VIewController
    self.loadingView = [[XYLoadingView alloc] initWithFrame:loadingViewRect];
    //use the delegate's window object to add the custom view on top of the view controller
    [delegate.window addSubview: loadingView]; 

【讨论】:

【参考方案21】:

这对我有用:

self.view.superview?.addSubview(yourCustomView)
self.view.bringSubviewToFront(yourCustomView)

【讨论】:

以上是关于如何在当前 UITableViewController 上方添加一个 UIView的主要内容,如果未能解决你的问题,请参考以下文章

tableHeaderView中的UITextView找不到委托

viewController的生命周期

将 UITableView 实例化为弹出框时,UITableViewCell 的属性为零

如何在 Windows Phone 8.1 中获取当前位置的当前货币代码

在android中如何获取当前日期?

如何在当前用户下启动进程?