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

62

我在UITableViewController的viewDidLoad方法中添加子视图(UIView)时遇到了困难。

以下代码可以正常工作:

[self.view addSubview:self.progView];

但是你可以看到表格单元格的线条渗透到了UIView progView中。

我尝试过以下方法:

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

尝试将progView(一个UIView)添加到当前视图的上层superview中。但是当我尝试这样做时,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出现了,但似乎在表格的后面。


2
你尝试过使用aboveSubview:self.view吗?我不是说它会解决你的问题,但听起来它可以消除你正在遇到的错误信息。 - Leo Cassarani
是的,我尝试过了。确实失去了警告,但仍然无法呈现视图。不过我会编辑我的帖子。 - user558096
一个简单的方法是设置progView的框架,我确定它可以工作。任何约束都不起作用,包括VFL。 - BollMose
21个回答

94

我尝试了上述方法,但无法使其工作。我还发现它需要太多的配置和代码,因为它需要从头设置表视图(这是在stroyboard中轻松完成的事情)。

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

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

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


11
你的解决方案存在问题,因为当你这样做时,视图还会添加在导航栏上方。 - Mittchel
37
你可以使用以下代码实现:[self.navigationController.view insertSubview:holderView belowSubview:self.navigationController.navigationBar]; 这行代码的作用是将 holderView 视图插入到导航栏下方。 - Renfei Song
5
另一个问题是,添加的视图会停留在UINavigationController的顶部,即使推入了一个新的UIViewController。 - Raphael
5
随着时间的推移,除非视图必须是列表并且您想强制执行该列表,否则我已经开始避免使用表视图控制器。相反,如果您将表视图添加到常规视图控制器中,并自己设置数据源和代理,那么您将获得一个更加灵活的设置,其中表视图只是主视图中的另一个视图,而不是主视图本身。这将使您摆脱表视图控制器的许多内置限制。 - Daniel Saidi
@Raphael 我使用viewDidAppear解决了这个问题,让视图出现,然后在viewWillDisappear方法中,你可以将其从父视图中移除。 - Edu

42

距离我原来的回答已经过去了七年,我又碰巧遇到了这个问题。让我们彻底解决它:

  1. viewDidLoad中,将你的子视图添加到(表)视图。
  2. 将约束固定相对于安全区域布局指南。这样可以防止它随着表内容滚动(正如cornr的答案所指出的那样)。
  3. viewDidLayoutSubviews中,将子视图置于最前面。这确保了它不会被表分隔符遮挡。

Swift:

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)
}

Objective-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开发者。


@ThomasVerbeek 在你的Storyboard或XIB文件中:将UITableViewController中的tableView连接到tableViewReference变量。这会改变一切,因为我没有使用IB。我撤销了我的踩票,没问题。 - Tancrede Chazallet
这在iOS 7和8中非常有效,但我有机会在iOS 6中尝试它。在我的情况下,viewDidLoad从未被调用,我认为是因为“view”从未真正被引用。我将 -(UIView *)view {return viewReference;} 更改为如果viewReference为nil,则返回[super view],并将 -(UITableView *)tableView {return tableViewReference;} 更改为如果tableViewReference为nil,则返回[super tableView]。这导致viewDidLoad被调用,但视图本身的内容存在“偏移”问题。有什么想法吗? - ScottyB
对于遇到上述评论中描述的问题的其他人,我的解决方案是在viewReference代码之后动态地在运行时添加新的(UIView *)view方法。这里的示例起了很大的作用:http://nshipster.com/method-swizzling/ - ScottyB
1
在setter方法中设置super.view = newValuesuper.tableView = newValue,它就可以工作了。 - farzadshbfn
2
干得好。下次见到你,Thomas,提醒我请你喝啤酒。 - Jed Soane
显示剩余6条评论

12

通过使用UIViewController容器,我已经能够在UITableViewController上方添加子视图。

当涉及到静态单元格时,UITableViewController实际上非常方便,这可能是唯一一个普遍回答“只需使用uitableview”可能无法实现的情况。

这就是我的做法:

  1. 给你的UITableViewController起一个Storyboard标识符,例如MyStaticTableView
  2. 创建一个全新的UIViewController子类并将其命名为UITableViewControllerContainer
  3. 在故事板中用这个控制器替代你的UITableViewController
  4. 添加一个子视图到新的控制器,并将其与名为"view_container"的outlet连接起来
  5. 在你的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];
}

你可能会遇到的问题:

  1. 如果在你的UITableViewController上方有额外的空白空间,则确保向其中添加标志“wants fullscreen”
  2. 如果它在你的UITableViewControllerContainer上不能正确地调整大小

添加代码:

- (void)viewWillAppear:(BOOL)animated
{
    [[self.view_container.subviews lastObject] setFrame:self.view.frame];
}

从您的UITableViewController开始,您可以直接访问容器视图:

self.view.superview.superview

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


1
从iOS 6.0及以上版本开始,UIContainerView使得解决这个问题变得更加简单。 - eladleb

12

Swift 2018

以下是我在 storyboard 中的方法:

1)在 storyboard 中添加一个视图。
enter image description here

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) Voilà!
enter image description here

The view will not move along with the table view. It can be easily designed in the storyboard.

NOTE: This solution adds a subview to the navigation controller. If you navigate further down the navigation stack, the subview will continue to exist. To remove it, use copyrightView.removeFromSuperView in viewDidDisappear when performing a segue.


对我来说非常好用。干得好。 - D. Rothschild
干得好!它帮了我很多。如果你偶然发现这个问题,这个解决方案会向导航控制器添加子视图,如果你从这里进入到导航栏下面的另一个屏幕,你会发现这个子视图仍然存在,所以在执行segue时,在viewDidDisappear中使用copyrightView.removeFromSuperView将其删除。 - Hammad Tariq

11

你可以增加视图的图层的zPosition。

这将使其显示在其他视图的顶部(默认情况下具有zPosition等于0的其他视图)。

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

这对我有用。我只是使用了一个普通的UIViewController,其中包含一个UITableView,我想放置一个位于表视图顶部的页脚视图。由于某种原因,如果我通过bringSubviewToFront调用或通过Storyboard将页脚视图带到前面,UITableView将被推下。这在顶部留下了一个很大的间隙。我不确定为什么UITableView不能在视图层次结构中有任何东西覆盖它。更改页脚视图层的zPosition属性成功地将其置于顶部,而不改变UITableView的定位。 - Sid
这个可以工作,但是图层不处理用户交互(这就是为什么我们有UIView)。只是指出一下。 - Chris
我的自定义UIActivityIndicatorView可以很好地工作!由于它是一个指示器,不需要用户交互,因此非常适合我的需求。我终于可以展示海报因为正在加载而被卡住了。 :) - Michele Dall'Agata

8
作为UIViewController的子类,UITableViewController需要将您想要的视图添加到其父视图中。
Swift:
self.view.superview?.addSubview(viewTobeAdded)

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

1
在我的UITableViewController(在viewDidLoad中),self.view.superview为nil。 - Jovan
3
@Jovan:没错,但我仍然认为Saikiran的简洁回答很棒。在viewDidAppear或viewDidLayoutSubviews中self.view.superview不为nil。有趣的是,当你在控制台中打印self.view.superview时,它会打印出:<UIViewControllerWrapperView: 0x157b03d70; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x2834445c0>>。这个UIViewControllerWrapperView的名字非常合理。 - Joss

6
问题在于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并没有增加太多功能,它实现的少量功能很容易被复制。有一些文章,如重新创建UITableViewController以增加代码重用性,可以很容易地解释如何做到这一点。


感谢您的帮助。不幸的是,我真的很难让您的第一个建议起作用。也许我正在尝试做的事情根本不可能。此时,我正在尝试将UIView作为UITableViewController子类的self.view。然后将UITableView作为UIView的子视图。似乎不可能做到这一点,我真的需要按照您的第二个建议将我的UITableViewController转换为UIViewController。有什么想法或指向教程或示例代码的指针可能会有所帮助。提前致谢。 - user558096
1
我不会在loadView中引用self.view来读取,否则你会陷入无限循环。只分配给它。 - Diziet

6

我曾经遇到过类似的问题,通过以下代码解决了:

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

这使用栈中存在的控制器将视图插入正确的位置。

5

苹果公司的示例 "iPhoneCoreDataRecipes" 使用 NIB 将标题加载到 UITableView 上。详情请参见 此处


我同意这个回答! - Jagie

4

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接