启动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设置(属性选项卡)是......
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上测试过它。 (另外,异常行为完全出现在设备或模拟器上:模拟器没有区别。)
答案 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迭代中发生,并获取可能需要表示的所有更改。