视图已添加到 UIViewControllerWrapperView,但显示不正确

View is added to UIViewControllerWrapperView, but it doesn't appear correctly

我正在尝试在 UITableViewController 上显示加载指示器。因此,我将我的子视图添加到 myTableViewontroller.View.Superview,即 UIViewControllerWrapperView。到目前为止,这是有效的,但并非在所有情况下都有效。如果 UITableViewController 是第一次添加到导航堆栈中,我可以根据需要随时显示加载指示器。如果我弹出 UITableViewController 并再次将它的新实例推送到导航堆栈,则加载指示器不会出现。连续调用使加载指示器再次出现!

我尝试在 UpdateConstraintsLayoutSubviews 中设置我的约束,但没有帮助。不知何故,框架的高度似乎为零。检查帧显示如下:

正常情况:

parent:{X=0,Y=0,Width=320,Height=480}
hud:{X=0,Y=0,Width=0,Height=0}
parent - before setting up constraints{X=0,Y=0,Width=320,Height=480}
hud - after hud showing: {X=0,Y=0,Width=279,5,Height=0}
parent:{X=0,Y=0,Width=320,Height=480}
hud:{X=0,Y=0,Width=320,Height=480}

失败案例

parent:{X=0,Y=0,Width=320,Height=480}
hud:{X=0,Y=0,Width=0,Height=0}
parent - before setting up constraints{X=0,Y=0,Width=320,Height=480}
hud - after hud showing: {X=-20,Y=59,Width=279,5,Height=0}

所以 LayoutSubviews 似乎在正常情况下调用了两次,因为 parent:hud: 行在那里输出。 -20 来自我使用的一个约束,我在接下来的步骤之一中通过使用不同的约束(限制 widht/height)摆脱了它。

如果我添加最小高度,我可以在 table 视图左上角的导航栏下方看到 HUD,但它被导航栏截断并且未居中。如果我尝试将 width/height 限制在零和我定义的最大大小之间,则会发生相同的行为:

NSLayoutConstraint widthConstraint1 = NSLayoutConstraint.Create(this.hudContainer, NSLayoutAttribute.Width, NSLayoutRelation.LessThanOrEqual, this, NSLayoutAttribute.Width, 1, -40);
widthConstraint1.Priority = 750;
NSLayoutConstraint widthConstraint2 = NSLayoutConstraint.Create(this.hudContainer, NSLayoutAttribute.Width, NSLayoutRelation.GreaterThanOrEqual, null, NSLayoutAttribute.NoAttribute, 1, 0);
widthConstraint2.Priority = 1000;
NSLayoutConstraint heightConstraint1 = NSLayoutConstraint.Create(this.hudContainer, NSLayoutAttribute.Height, NSLayoutRelation.LessThanOrEqual, this, NSLayoutAttribute.Height, 1, -100);
heightConstraint1.Priority = 750;
NSLayoutConstraint heightConstraint2 = NSLayoutConstraint.Create(this.hudContainer, NSLayoutAttribute.Height, NSLayoutRelation.GreaterThanOrEqual, null, NSLayoutAttribute.NoAttribute, 1, 0);
heightConstraint2.Priority = 1000;
AddConstraints(new NSLayoutConstraint[] { widthConstraint1, widthConstraint2, heightConstraint1, heightConstraint2});

这里不考虑最大宽度。

我使用此代码将 hud 添加为子视图并设置主要约束:

this.TranslatesAutoresizingMaskIntoConstraints = false;
this.parentView.AddSubview(this);

NSMutableDictionary viewsDictionary = new NSMutableDictionary();
viewsDictionary["hud"] = this;

this.parentView.LayoutIfNeeded();
Console.WriteLine("parent - before setting up constraints" + this.parentView.Frame);

this.parentView.AddConstraint(NSLayoutConstraint.Create(this, NSLayoutAttribute.Width, NSLayoutRelation.Equal, this.parentView, NSLayoutAttribute.Width, 1, 0));
this.parentView.AddConstraint(NSLayoutConstraint.Create(this, NSLayoutAttribute.Height, NSLayoutRelation.Equal, this.parentView, NSLayoutAttribute.Height, 1, 0));
this.parentView.AddConstraint(NSLayoutConstraint.Create(this, NSLayoutAttribute.Top, NSLayoutRelation.Equal, this.parentView, NSLayoutAttribute.Top, 1, 0));
this.parentView.AddConstraint(NSLayoutConstraint.Create(this, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this.parentView, NSLayoutAttribute.Right, 1, 0));
this.parentView.AddConstraint(NSLayoutConstraint.Create(this, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, this.parentView, NSLayoutAttribute.Bottom, 1, 0));
this.parentView.AddConstraint(NSLayoutConstraint.Create(this, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this.parentView, NSLayoutAttribute.Left, 1, 0));
this.parentView.AddConstraint(NSLayoutConstraint.Create(this, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, this.parentView, NSLayoutAttribute.CenterX, 1, 0));
this.parentView.AddConstraint(NSLayoutConstraint.Create(this, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, this.parentView, NSLayoutAttribute.CenterY, 1, 0));

这是删除和隐藏 hud 的代码:

this.Alpha = 0.0f;
RemoveFromSuperview();

我 运行 没主意了。我怎么解决这个问题?也许有人可以给我一个提示,我怎样才能更好地调试它。此处提供的代码使用 C#,但您可以在 Objective-C/Swift!

中提供代码示例

您不应手动向 UIViewControllerWrapperView 添加视图,因为它是内部 API。这样做会导致不可预测的table结果。

相反,将您的加载指示器作为子视图添加到 UITableViewControllerview 属性。也许这已经解决了您的问题。

还有一件事:如果您的 HUD 是 UIActivityIndicatorView,则无需移除它即可隐藏它。只需将 hidesWhenStopped 属性 设置为 true;然后指标会在调用 stopAnimating 时自动隐藏,并在调用 startAnimating 时重新出现。

编辑:

正如您所指出的,将 HUD 直接添加到视图中会导致定位问题。解决这个问题的一种方法是手动设置一个容器视图(一个普通的 UIView)来承载 table 视图和 HUD:

override func viewDidLoad() {
  super.viewDidLoad()

  let tableView = self.tableView
  let containerView = UIView(frame: self.view.bounds)
  containerView.addSubview(tableView)
  containerView.addSubview(spinner)      
  view = containerView

  spinner.translatesAutoresizingMaskIntoConstraints = false
  view.addConstraint(NSLayoutConstraint(item: spinner, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: 0))
  view.addConstraint(NSLayoutConstraint(item: spinner, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: 0))

  spinner.startAnimating()
}

请注意,这个例子使用了一个简单的 activity 指标,它有一个固有的大小,所以要让它居中只需要两个约束;对于您的 HUD,您可能需要添加更复杂的约束。

我采用了 dr_barto 建议的方法。我定义了我的新基地classHUDTableViewController,也就是我的新基地UITableViewController。所以我从 HUDTableViewController 而不是 UITableViewController.

子 classing

我的 HUD 获得以下视图:

TableView.Superview(在我的子classed HUDTableViewController里面)

必须添加额外的方法并为 UICollectionViewController 实现这样的基础 class。在这里,我举了一个 UITableViewController:

的例子
public class HUDTableViewController : UIViewController, IUITableViewDataSource, IUITableViewDelegate, IDisposable
{
    private UITableView tableView;

    public UITableView TableView
    {
        get
        {
            return this.tableView;
        }
        set
        {
            this.tableView = value;
        }
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        this.tableView = new UITableView();
        this.tableView.TranslatesAutoresizingMaskIntoConstraints = false;

        this.tableView.WeakDelegate = this;
        this.tableView.WeakDataSource = this;

        UIView parentView = new UIView();
        parentView.AddSubview(this.tableView);
        View = parentView;

        NSMutableDictionary viewsDictionary = new NSMutableDictionary();
        viewsDictionary["parent"] = parentView;
        viewsDictionary["tableView"] = this.tableView;

        parentView.AddConstraints(NSLayoutConstraint.FromVisualFormat("H:|[tableView]|", (NSLayoutFormatOptions)0, null, viewsDictionary));
        parentView.AddConstraints(NSLayoutConstraint.FromVisualFormat("V:|[tableView]|", (NSLayoutFormatOptions)0, null, viewsDictionary));
    }

    [Foundation.Export("numberOfSectionsInTableView:")]
    public virtual System.nint NumberOfSections(UIKit.UITableView tableView)
    {
        throw new NotImplementedException();
    }

    public virtual System.nint RowsInSection(UIKit.UITableView tableview, System.nint section)
    {
        throw new NotImplementedException();
    }

    public virtual UIKit.UITableViewCell GetCell(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
    {
        throw new NotImplementedException();
    }

    [Foundation.Export("tableView:didSelectRowAtIndexPath:")]
    public virtual void RowSelected(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
    {
        throw new NotImplementedException();
    }

    [Foundation.Export("tableView:heightForHeaderInSection:")]
    public virtual System.nfloat GetHeightForHeader(UIKit.UITableView tableView, System.nint section)
    {
        throw new NotImplementedException();
    }

    [Foundation.Export("tableView:viewForHeaderInSection:")]
    public virtual UIKit.UIView GetViewForHeader(UIKit.UITableView tableView, System.nint section)
    {
        throw new NotImplementedException();
    }

    [Foundation.Export("tableView:willDisplayCell:forRowAtIndexPath:")]
    public virtual void WillDisplay(UIKit.UITableView tableView, UIKit.UITableViewCell cell, Foundation.NSIndexPath indexPath)
    {
        throw new NotImplementedException();
    }
}

正如大家所见,我在 UITableView 后面添加了一个额外的视图。现在我可以为我的 HUD 使用 table 视图的超级视图。到目前为止似乎有效。

懒惰的替代品(具有不同的行为):

  • 使用Window 属性:

    UIApplication.SharedApplication.Delegate.GetWindow().View

  • 使用NavigationController:

    NavigationController.View