请参阅GK100 的答案,但让我们用下面的 Q/A(s) 来扩展它。
如何在情节提要中进行恒定的自动调整大小?
尚不支持基于百分比的常量,人们尝试不同的解决方法,例如:
- 创建不可见
UIView
的(s)作为具有宽度/高度的间隔,并指定一个乘数对Safe Area
大小。
- 人们甚至在代码中手动覆盖 Storyboard 约束:
NSLayoutConstraint(item: myView, attribute: .top,
relatedBy: .equal, toItem: myOtherView, attribute: .bottom,
multiplier: 1,
constant: UIScreen.main.bounds.width * 0.05)
虽然上述两个都令人头疼,但我的解决方案的缺点是您至少需要硬编码shortDimensionInDesignTime
到您首选Interface Builder
的预览大小(但比我的PercentConstraint
班级好得多)。
用法:
- 将以下
ResponsiveConstraint
类的代码添加到您的项目中。
Storyboard
在编辑器中选择您的约束。
- 在
Identity Inspector
中,ResponsiveConstraint
用作类。
import Foundation
/// Auto-Resize based Layout-Constraint.
/// Later at run-time, resizes initial constant to screen's short dimension.
///
/// For Mac's Catalyst. remember to set size-restrictions to something like:
/// ```
/// let portrait: CGSize = ResponsiveConstraint.maxSize;
/// scene.sizeRestrictions?.maximumSize = portrait;
/// scene.sizeRestrictions?.minimumSize = portrait;
/// ```
///
/// WARNING: supports only Storyboard (by use as class in Identity Inspector),
/// but there is no harm in using static provided field and methods.
///
public class ResponsiveConstraint: NSLayoutConstraint {
var actualConstant: CGFloat = 0;
public var isValid: Bool { actualConstant > 0 }
/// Preset shorter dimension of screen, within Design-Time only
/// (i.e. it's your Interface-Builder's preview size).
public static let shortDimensionInDesignTime: CGFloat = 414;
public static let longDimensionInDesignTime: CGFloat = 896;
/// Normally same as "UIScreen.main.bounds.size" variable (i.e. screen size).
/// But for Computers, we limit to a portrait-size (to look same as Mobile).
public static var maxSize: CGSize {
#if targetEnvironment(macCatalyst)
return .init(
width: Self.shortDimensionInDesignTime,
height: Self.longDimensionInDesignTime);
#else
return UIScreen.main.bounds.size;
#endif
}
public static var shortDimension: CGFloat {
let screenSize: CGSize = Self.maxSize;
return screenSize.width < screenSize.height
? screenSize.width : screenSize.height;
}
public static var longDimension: CGFloat {
let screenSize: CGSize = Self.maxSize;
return screenSize.height > screenSize.width
? screenSize.height : screenSize.width;
}
/// Does same as "init()" would, but for Storyboard.
override public func awakeFromNib() {
super.awakeFromNib()
// Backup real constant (and continue if it's valid).
self.actualConstant = self.constant
guard isValid else { return }
// Listens to screen rotate.
NotificationCenter.default.addObserver(self,
selector: #selector(onLayoutChange),
name: UIDevice.orientationDidChangeNotification,
object: nil)
// Apply for current screen size.
self.onLayoutChange();
}
deinit {
guard isValid else { return }
NotificationCenter.default.removeObserver(self)
}
/// Recalculates constant.
@objc public func onLayoutChange() {
guard isValid else { return }
self.constant = Self.resize(self.actualConstant)
}
/// Converts constant from DesignTime to fit current max view-size.
public static func resize(_ constant: CGFloat) -> CGFloat {
return constant / Self.shortDimensionInDesignTime * Self.shortDimension;
}
}
如何在情节提要中实现基于百分比的常量?
以下解决方案的缺点是需要在看到结果之前运行 App(即使在未来的 Xcode 版本中也可能不需要)。
用法:
- 将以下
PercentConstraint
类的代码添加到您的项目中。
Storyboard
在编辑器中选择您的约束。
- 在
Identity Inspector
中,PercentConstraint
用作类。
- 在
Attribute Inspector
,设置Percent of Screen
字段。
- (可选)运行应用程序以查看结果。
import Foundation
/// Percent based Layout-Constraint.
/// Later at run-time, replaces constant with percentage of screen.
///
/// WARNING: supports only Storyboard (used as class in Identity Inspector).
///
class PercentConstraint: NSLayoutConstraint {
@IBInspectable public var percentOfScreen: CGFloat = 0
var isValid: Bool { percentOfScreen > 0 }
var screenSize: CGSize { UIScreen.main.bounds.size }
override func awakeFromNib() {
super.awakeFromNib()
guard isValid else { return }
NotificationCenter.default.addObserver(self,
selector: #selector(onLayoutChange),
name: UIDevice.orientationDidChangeNotification,
object: nil)
self.onLayoutChange();
}
deinit {
guard isValid else { return }
NotificationCenter.default.removeObserver(self)
}
/// Recalculates constant.
@objc func onLayoutChange() {
guard isValid else { return }
switch self.firstAttribute {
case .height, .top, .topMargin, .bottom, .bottomMargin,
.centerY, .centerYWithinMargins,
.firstBaseline, .lastBaseline:
self.constant = screenSize.height * percentOfScreen / 100.0;
case .width, .leading, .leadingMargin, .trailing, .trailingMargin,
.left, .leftMargin, .right, .rightMargin,
.centerX, .centerXWithinMargins:
self.constant = screenSize.width * percentOfScreen / 100.0;
default:
break;
}
}
}
请注意,constant
在运行时会覆盖它,但您可能仍想设置它(以了解它在运行之前的样子)。