令人恐惧的是,layoutIfNeeded序列在iPad和iPhone上的表现不同;如何解决?

时间:2016-06-03 23:44:46

标签: ios ipad autolayout nslayoutconstraint

启动Xcode,为了清晰起见,只建议说9.3,通用应用程序。所以,比较9.3台iPad和9.3台iPhone。构建模拟器和设备 - 在两者上发布展品。

应用程序向四个方向旋转。

有一个典型的情况,你做这样的事情......

@IBOutlet weak var doorHeightPerScreen: NSLayoutConstraint!

var heightFraction:CGFloat = 0.6
    {
    didSet
        {
        if ( heightFraction > maxHeight ) { heightFraction = maxHeight }
        if ( heightFraction < minHeight ) { heightFraction = minHeight }

        let h = view.bounds.size.height
        spaceshipHeightPerScreen.constant = h * heightFraction

        self.view.layoutIfNeeded()  // holy!  read on....
        }
    }

在更改约束后注意layoutIfNeeded()

继续典型示例,您将拥有类似

的内容
override func viewDidLayoutSubviews()
    {
    super.viewDidLayoutSubviews()
    heightFraction = (heightFraction)
    // use "autolayout power" for perfection every pass.

    // now that basic height/position is set,
    save/load reactive positions...
    position detail stuff...
    }

检查出来......我整天都这样做,只碰巧使用了iPhone。

有趣的是,你不需要layoutIfNeeded调用。

@IBOutlet weak var doorHeightPerScreen: NSLayoutConstraint!
var heightFraction:CGFloat = 0.6
    {
    didSet
        {
        if ( heightFraction > maxHeight ) { heightFraction = maxHeight }
        if ( heightFraction < minHeight ) { heightFraction = minHeight }
        let h = view.bounds.size.height
        spaceshipHeightPerScreen.constant = h * heightFraction
        }
    }

工作正常。

然而,在一天结束时,我把它放在一些iPad上......一切都破了!

无论何时旋转横向/纵向,都会出现问题。

经过一次头脑刮擦后,我意识到你需要进行layoutIfNeeded调用,iPad上的 。这是在IDENTICAL OS上。

实际上,无论操作系统版本如何,行为都表现出它适用于所有iPhone /所有iPad。 WTF!

@IBOutlet weak var doorHeightPerScreen: NSLayoutConstraint!
var heightFraction:CGFloat = 0.6
    {
    didSet
        {
        if ( heightFraction > maxHeight ) { heightFraction = maxHeight }
        if ( heightFraction < minHeight ) { heightFraction = minHeight }
        let h = view.bounds.size.height
        spaceshipHeightPerScreen.constant = h * heightFraction
        self.view.layoutIfNeeded() //MUST HAVE, IN IPAD CASE!!!!!!
        }
    }

对我而言, 令人难以置信的麻烦 他们会以不同的方式工作。

我想知道的是,是否有某种设置可以使它们的工作方式相同?不知怎的,这可能是我的错吗?

两者之间是否存在其他已知的差异 - 或者确实“已知”存在这样的一些错误?

我无法想到我在任何地方做过任何奇怪或不寻常的事情,除非整个应用在第一个视图中有override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { return .All },如果您想要将设备颠倒过来是正常的;我怀疑它的相关性。除此之外,它是一个非常“干净”的新应用程序。

它给了我一种小故障的感觉 - 这太可怕了。

是什么导致这种情况?

根据RobM的问题,初始ViewController上的SimulatedMetrics设置(属性选项卡)是......

enter image description here

app的一般方案:第一个场景“General”是全屏,设备的大小。有一个“Live”容器,它的大小相同(使用“Trailing”等/约束为零)。在Live中,有一个容器视图“Quad”,它的大小也确实是“Live”,因此它也是全屏的。 Quad:UIViewController展示了我描述的问题。 Quad包含各种对象(图像,自定义控件等),它们位于视图中。当应用程序启动时,一切都很好。

在旋转设备(或类似设备)时:刚改变约束后(我不知道这是否相关):iPad需要layoutIfNeeded呼叫 IS (所有iPad),但iPhone(所有iPhone)需要 NOT 。模拟器和设备上的行为相同。

另一个例子......

我发现了另一个惊人的例子。

在UICollectionView中,自定义单元格(只是简单的静态大小的单元格)。如果您碰巧更改了约束(想象一下如果要调整单元格中的图标或产品大小)。

ON IPAD DO 必须确保在layoutIfNeeded中重新调整 <在第一次出现细胞时,em>将无法正常工作。

然而 ON IPHONE 它的表现肯定不同:如果您碰巧省略它,它会在第一次出现单元格之前“为您做”。

完全怪异!!

我在每台iPad和每部iPhone上测试过它。 (另外,异常行为完全出现在设备或模拟器上:模拟器没有区别。)

2 个答案:

答案 0 :(得分:1)

默认模拟指标大小为“推断”,(如果场景不是segue或关系的目标)为您提供600x600视图,这与任何屏幕尺寸都不对应iOS设备。您在某些时候将模拟的指标大小更改为“iPhone 5.5英寸”,可能与您的主要测试设备的大小相匹配。

从故事板(或xib)加载视图时,它会以故事板中的大小加载。然后可以通过其容器调整大小(如果它是应用程序的根视图,则为UIWindow,如果它是包含视图控制器的根视图,则为其超级视图)。 / p>

在您的情况下,听起来您的主要测试设备屏幕与故事板中的根视图大小相同,因此测试设备不必像您预期的那样运行尽可能多的布局

当您使用屏幕尺寸与故事板中的根视图大小不同的测试设备时,测试设备必须进行更多布局。

我没有尝试重现您的问题,所以我并没有声称这是对您所看到的内容的完整解释。可能涉及到iOS错误。不过,这可以解释为什么您的应用在不同设备上的行为方式不同。我相信这也是为什么Apple选择600x600的默认推断尺寸:因为没有设备屏幕是那么大,所有设备都必须做相同数量的布局。

答案 1 :(得分:1)

我无法重现您所看到的内容;看到一个完整的例子会很高兴。在我的模型中,我配置了一个视图控制器,其视图具有单个子视图,并具有控制子视图高度的约束。我根据视图大小更改了viewDidLayout中的子视图高度约束。 iPhone和iPad的行为相同,并且在任何视图上都没有调用layoutIfNeeded

那就是说,一旦视图完成布局,我认为你正在改变子视图约束 - 是吗?我认为更好的方法是通过viewWillTransitionToSize:withTransitionCoordinator:在您的子视图之前进行布局。

func viewWillTransitionToSize(_ size: CGSize,
    withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)

这样,视图层次结构的自动布局可以一次完成。仅在视图更改大小时调用此方法,因此在首次加载视图时不会调用此方法;您必须在其他地方设置初始约束 - 因为它们依赖于视图大小,您可以使用viewWillAppear

或者,(可能更正确),继承视图控制器的视图并覆盖updateConstraints。这是更改约束常量的最合适位置。

最后,在你的属性设置器中,你不应该调用view.layoutIfNeeded()。如果有的话,您可以设置view.setNeedsLayout(),以便布局在下一个runloop迭代中发生,并获取可能需要表示的所有更改。