如何定义一个 returns 元素数组的函数,这些元素的类型可以不同,但都必须符合某些协议规则?
How to define a function that returns an array of elements which can vary in types but all must conform to certain protocol rules?
我发现在多个 UIViewController 中,我正在设置各种可重用视图的访问权限,以便它们可以相应地更新它们的约束。我想创建一个名为 ChallengeFlowViewController
的超类 UIViewController
,以便它可以负责管理为支持轴更新的视图更新轴的调用。挑战就在这里。我如何定义一个方法或计算 属性 以便它可以被覆盖并且子类可以返回任意数量的不同视图,只要这些视图中的每一个都符合 HasViewModel 并且它们的 ViewModel 类型符合 HasAxis。
下面的实现有 axisSettables()
,但是它的实现方式要求返回的所有视图都是相同的视图类型。我希望允许视图类型的差异,只要它们都符合要求即可。
另一个问题,在 viewWillLayoutSubView
方法中,我收到错误:Generic parameter 'T' could not be inferred
class ChallengeFlowViewController: UIViewController {
/// override to ensure axises are set for views that adjust when rotated.
/// Styles the views if they conform to
func axisSettables<T: HasViewModel>() -> [T?] where T.ViewModel: HasAxis {
[]
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
for view in axisSettables() {
view?.setAxis()
}
}
}
protocol HasViewModel {
associatedtype ViewModel
var viewModel: ViewModel { get set }
init()
}
extension HasViewModel {
init(viewModel: ViewModel) {
self.init()
self.viewModel = viewModel
}
mutating func setAxis(
from newAxis: NSLayoutConstraint.Axis = UIApplication.orientationAxis
) where ViewModel: HasAxis {
guard viewModel.axis != newAxis else { return }
viewModel.axis = newAxis
}
}
extension UIApplication {
/// This is orientation as it relates to constraints.
enum ConstraintOrientation {
/// portrait and portraitUpsideDown equal portrait
case portrait
/// landscapeleft and landscaperight equal landscape
case landscape
}
/// Unfortunately `UIDevice.current.orientation` is unreliable in determining orientation.
/// if you `print(UIDevice.current.orientation)` when the simulator or phone launches
/// with landscape, you will find that the print value can sometimes print as portrait.
/// if statusBarOrientation is unknown or nil the fallback is portrait, as it would be the safer orientation
/// for people who have difficulty seeing, for example, if a view's landscape setting splits the views in half
/// horizontally, the same would look narrow on portrait, wheras the portrait setting is more likely to span
/// the width of the window.
static var constraintOrientation: ConstraintOrientation {
guard #available(iOS 13.0, *) else {
return UIDevice.current.orientation.constraintOrientation
}
return statusBarOrientation == .landscapeLeft || statusBarOrientation == .landscapeRight ? .landscape : .portrait
}
@available(iOS 13.0, *)
static var statusBarOrientation: UIInterfaceOrientation? {
shared.windows.first(where: \.isKeyWindow)?.windowScene?.interfaceOrientation
}
static var orientationAxis: NSLayoutConstraint.Axis {
constraintOrientation == .landscape ? .horizontal : .vertical
}
}
protocol HasAxis {
var axis: NSLayoutConstraint.Axis { get set }
}
extension HasAxis {
var horizontal: Bool { axis == .horizontal }
var vertical: Bool { axis == .vertical }
}
阻止你使用异构数组的是 HasViewModel
协议,它有关联的类型,这迫使你将它用作 axisSettables
函数的通用参数,这导致有将 return 类型声明为同构数组。
如果您希望能够使用异构数组,则需要使用与 HasViewModel
协议互补的“常规”协议。例如:
protocol AxisSettable {
func setAxis()
}
class ChallengeFlowViewController: UIViewController {
/// override to ensure axises are set for views that adjust when rotated.
/// Styles the views if they conform to
func axisSettables() -> [AxisSettable] {
[]
}
这样你也可以将关注点分开,因为 axisSettables
不应该关心 returning 视图是否有视图模型,它应该只关心视图是否可以更新它们的轴.在实现级别,您可以保留 HasViewModel
协议,如果这对其他方面有帮助,只需添加对 AxisSettable
.
的一致性
我最终使用了 HasViewModel
协议的一个可选函数。
protocol HasViewModel {
optional mutating func setAxis(from newAxis: NSLayoutConstraint.Axis)
}
现在我可以 return 符合 HasViewModel
的视图
/// Override me
var hasViewModelArray: [HasViewModel] {
}
然后我可以在 viewWillLayoutSubviews
方法中选择性地使用该方法。
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
hasViewModelArray.forEach(\.setAxis?())
}
我发现在多个 UIViewController 中,我正在设置各种可重用视图的访问权限,以便它们可以相应地更新它们的约束。我想创建一个名为 ChallengeFlowViewController
的超类 UIViewController
,以便它可以负责管理为支持轴更新的视图更新轴的调用。挑战就在这里。我如何定义一个方法或计算 属性 以便它可以被覆盖并且子类可以返回任意数量的不同视图,只要这些视图中的每一个都符合 HasViewModel 并且它们的 ViewModel 类型符合 HasAxis。
下面的实现有 axisSettables()
,但是它的实现方式要求返回的所有视图都是相同的视图类型。我希望允许视图类型的差异,只要它们都符合要求即可。
另一个问题,在 viewWillLayoutSubView
方法中,我收到错误:Generic parameter 'T' could not be inferred
class ChallengeFlowViewController: UIViewController {
/// override to ensure axises are set for views that adjust when rotated.
/// Styles the views if they conform to
func axisSettables<T: HasViewModel>() -> [T?] where T.ViewModel: HasAxis {
[]
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
for view in axisSettables() {
view?.setAxis()
}
}
}
protocol HasViewModel {
associatedtype ViewModel
var viewModel: ViewModel { get set }
init()
}
extension HasViewModel {
init(viewModel: ViewModel) {
self.init()
self.viewModel = viewModel
}
mutating func setAxis(
from newAxis: NSLayoutConstraint.Axis = UIApplication.orientationAxis
) where ViewModel: HasAxis {
guard viewModel.axis != newAxis else { return }
viewModel.axis = newAxis
}
}
extension UIApplication {
/// This is orientation as it relates to constraints.
enum ConstraintOrientation {
/// portrait and portraitUpsideDown equal portrait
case portrait
/// landscapeleft and landscaperight equal landscape
case landscape
}
/// Unfortunately `UIDevice.current.orientation` is unreliable in determining orientation.
/// if you `print(UIDevice.current.orientation)` when the simulator or phone launches
/// with landscape, you will find that the print value can sometimes print as portrait.
/// if statusBarOrientation is unknown or nil the fallback is portrait, as it would be the safer orientation
/// for people who have difficulty seeing, for example, if a view's landscape setting splits the views in half
/// horizontally, the same would look narrow on portrait, wheras the portrait setting is more likely to span
/// the width of the window.
static var constraintOrientation: ConstraintOrientation {
guard #available(iOS 13.0, *) else {
return UIDevice.current.orientation.constraintOrientation
}
return statusBarOrientation == .landscapeLeft || statusBarOrientation == .landscapeRight ? .landscape : .portrait
}
@available(iOS 13.0, *)
static var statusBarOrientation: UIInterfaceOrientation? {
shared.windows.first(where: \.isKeyWindow)?.windowScene?.interfaceOrientation
}
static var orientationAxis: NSLayoutConstraint.Axis {
constraintOrientation == .landscape ? .horizontal : .vertical
}
}
protocol HasAxis {
var axis: NSLayoutConstraint.Axis { get set }
}
extension HasAxis {
var horizontal: Bool { axis == .horizontal }
var vertical: Bool { axis == .vertical }
}
阻止你使用异构数组的是 HasViewModel
协议,它有关联的类型,这迫使你将它用作 axisSettables
函数的通用参数,这导致有将 return 类型声明为同构数组。
如果您希望能够使用异构数组,则需要使用与 HasViewModel
协议互补的“常规”协议。例如:
protocol AxisSettable {
func setAxis()
}
class ChallengeFlowViewController: UIViewController {
/// override to ensure axises are set for views that adjust when rotated.
/// Styles the views if they conform to
func axisSettables() -> [AxisSettable] {
[]
}
这样你也可以将关注点分开,因为 axisSettables
不应该关心 returning 视图是否有视图模型,它应该只关心视图是否可以更新它们的轴.在实现级别,您可以保留 HasViewModel
协议,如果这对其他方面有帮助,只需添加对 AxisSettable
.
我最终使用了 HasViewModel
协议的一个可选函数。
protocol HasViewModel {
optional mutating func setAxis(from newAxis: NSLayoutConstraint.Axis)
}
现在我可以 return 符合 HasViewModel
/// Override me
var hasViewModelArray: [HasViewModel] {
}
然后我可以在 viewWillLayoutSubviews
方法中选择性地使用该方法。
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
hasViewModelArray.forEach(\.setAxis?())
}