Как "найти" собственное ограничение?
Скажем, у меня есть UIView,
class CleverView: UIView
В пользовательском классе я хочу сделать это:
func changeWidth() {
let c = ... find my own layout constraint, for "width"
c.constant = 70 * Gameinfo.ImportanceOfEnemyFactor
}
Точно так же я хочу иметь возможность "найти", как это, ограничение (или, я думаю, все ограничения могут быть более одного), прикрепленные к одному из четырех ребер.
Итак, просмотрите все ограничения, прилагаемые ко мне, и найдите любые значения ширины/высоты или действительно относящиеся к данному (скажем, "левому" ) краю.
Любые идеи?
Возможно, стоит отметить этот вопрос
Пожалуйста, обратите внимание, что (очевидно) я спрашиваю, как это сделать динамически/программно.
(Да, вы можете сказать "ссылка на ограничение" или "использовать идентификатор" - вся суть QA заключается в том, как найти их "на лету" и работать динамически.)
Если вы новичок в ограничениях, обратите внимание, что .constraints
просто дает вам концы, хранящиеся "там".
Ответы
Ответ 1
Есть действительно два случая:
- Ограничения относительно размера представления или отношения к представлениям потомков сохраняются сами по себе
- Ограничения между двумя представлениями сохраняются в представлении "самый низкий общий предок"
Повторить. Для ограничений, которые находятся между двумя видами. iOS действительно хранит их у самого низкого общего предка. Таким образом, ограничение вида всегда можно найти, выполнив поиск всех предков представления.
Таким образом, нам нужно проверить само представление и все его супервизоры для ограничений. Один из подходов может быть:
extension UIView {
// retrieves all constraints that mention the view
func getAllConstraints() -> [NSLayoutConstraint] {
// array will contain self and all superviews
var views = [self]
// get all superviews
var view = self
while let superview = view.superview {
views.append(superview)
view = superview
}
// transform views to constraints and filter only those
// constraints that include the view itself
return views.flatMap({ $0.constraints }).filter { constraint in
return constraint.firstItem as? UIView == self ||
constraint.secondItem as? UIView == self
}
}
}
Вы можете применять все виды фильтров после получения всех ограничений в отношении представления, и я думаю, что это самая сложная часть. Некоторые примеры:
extension UIView {
// Example 1: Get all width constraints involving this view
// We could have multiple constraints involving width, e.g.:
// - two different width constraints with the exact same value
// - this view width equal to another view width
// - another view height equal to this view width (this view mentioned 2nd)
func getWidthConstraints() -> [NSLayoutConstraint] {
return getAllConstraints().filter( {
($0.firstAttribute == .width && $0.firstItem as? UIView == self) ||
($0.secondAttribute == .width && $0.secondItem as? UIView == self)
} )
}
// Example 2: Change width constraint(s) of this view to a specific value
// Make sure that we are looking at an equality constraint (not inequality)
// and that the constraint is not against another view
func changeWidth(to value: CGFloat) {
getAllConstraints().filter( {
$0.firstAttribute == .width &&
$0.relation == .equal &&
$0.secondAttribute == .notAnAttribute
} ).forEach( {$0.constant = value })
}
// Example 3: Change leading constraints only where this view is
// mentioned first. We could also filter leadingMargin, left, or leftMargin
func changeLeading(to value: CGFloat) {
getAllConstraints().filter( {
$0.firstAttribute == .leading &&
$0.firstItem as? UIView == self
}).forEach({$0.constant = value})
}
}
//edit: расширенные примеры и разъяснения их пояснений в комментариях
Ответ 2
Я думаю, вы можете работать с constraints свойством UIView
. constraints
в основном возвращает массив ограничений, непосредственно назначенных UIView. Он не сможет получить ограничения, связанные с супервизором, такие как ведущий, конечный, верхний или нижний, но ограничения ширины и высоты удерживаются самим просмотром. Для ограничений супервизора вы можете перебирать ограничения супервизора. Допустим, у умного вида эти ограничения:
![введите описание изображения здесь]()
class CleverView: UIView {
func printSuperViewConstriantsCount() {
var c = 0
self.superview?.constraints.forEach({ (constraint) in
guard constraint.secondItem is CleverView || constraint.firstItem is CleverView else {
return
}
c += 1
print(constraint.firstAttribute.toString())
})
print("superview constraints:\(c)")
}
func printSelfConstriantsCount() {
self.constraints.forEach { (constraint) in
return print(constraint.firstAttribute.toString())
}
print("self constraints:\(self.constraints.count)")
}
}
Выход
топ
ведущий
задний
ограничения супервизора: 3
высота
self constraints: 1
В принципе, вы можете посмотреть класс NSLayoutConstraint, чтобы получить информацию об определенном ограничении.
Чтобы напечатать имя ограничений, мы можем использовать это расширение
extension NSLayoutAttribute {
func toString() -> String {
switch self {
case .left:
return "left"
case .right:
return "right"
case .top:
return "top"
case .bottom:
return "bottom"
case .leading:
return "leading"
case .trailing:
return "trailing"
case .width:
return "width"
case .height:
return "height"
case .centerX:
return "centerX"
case .centerY:
return "centerY"
case .lastBaseline:
return "lastBaseline"
case .firstBaseline:
return "firstBaseline"
case .leftMargin:
return "leftMargin"
case .rightMargin:
return "rightMargin"
case .topMargin:
return "topMargin"
case .bottomMargin:
return "bottomMargin"
case .leadingMargin:
return "leadingMargin"
case .trailingMargin:
return "trailingMargin"
case .centerXWithinMargins:
return "centerXWithinMargins"
case .centerYWithinMargins:
return "centerYWithinMargins"
case .notAnAttribute:
return "notAnAttribute"
}
}
}
Ответ 3
Можете сохранить кого-то набрав........
Основываясь на ответе на выигрыш в битках, вот как именно получить
все ограничения типа "дробная ширина другого вида"
все ограничения типа "ширина фиксированной точки"
Итак..
fileprivate extension UIView {
func widthAsPointsConstraints()->[NSLayoutConstraint] {}
func widthAsFractionOfAnotherViewConstraints()->[NSLayoutConstraint] {}
}
Полный код ниже. Конечно, вы можете сделать "высоту" таким же образом.
Итак, используйте их вот так...
let cc = someView.widthAsFractionOfAnotherViewConstraints()
for c in cc {
c.changeToNewConstraintWith(multiplier: 0.25)
}
или
let cc = someView.widthAsPointsConstraints()
for c in cc {
c.constant = 150.0
}
Кроме того, внизу я вставил в простой демонстрационный код пример вывода...
![введите описание изображения здесь]()
Вот кодекс. V2...
fileprivate extension UIView { // experimental
func allConstraints()->[NSLayoutConstraint] {
var views = [self]
var view = self
while let superview = view.superview {
views.append(superview)
view = superview
}
return views.flatMap({ $0.constraints }).filter { constraint in
return constraint.firstItem as? UIView == self ||
constraint.secondItem as? UIView == self
}
}
func widthAsPointsConstraints()->[NSLayoutConstraint] {
return self.allConstraints()
.filter({
( $0.firstItem as? UIView == self && $0.secondItem == nil )
})
.filter({
$0.firstAttribute == .width && $0.secondAttribute == .notAnAttribute
})
}
func widthAsFractionOfAnotherViewConstraints()->[NSLayoutConstraint] {
func _bothviews(_ c: NSLayoutConstraint)->Bool {
if c.firstItem == nil { return false }
if c.secondItem == nil { return false }
if !c.firstItem!.isKind(of: UIView.self) { return false }
if !c.secondItem!.isKind(of: UIView.self) { return false }
return true
}
func _ab(_ c: NSLayoutConstraint)->Bool {
return _bothviews(c)
&& c.firstItem as? UIView == self
&& c.secondItem as? UIView != self
&& c.firstAttribute == .width
}
func _ba(_ c: NSLayoutConstraint)->Bool {
return _bothviews(c)
&& c.firstItem as? UIView != self
&& c.secondItem as? UIView == self
&& c.secondAttribute == .width
}
// note that .relation could be anything: and we don't mind that
return self.allConstraints()
.filter({ _ab($0) || _ba($0) })
}
}
extension NSLayoutConstraint {
// typical routine to "change" multiplier fraction...
@discardableResult
func changeToNewConstraintWith(multiplier:CGFloat) -> NSLayoutConstraint {
//NSLayoutConstraint.deactivate([self])
self.isActive = false
let nc = NSLayoutConstraint(
item: firstItem as Any,
attribute: firstAttribute,
relatedBy: relation,
toItem: secondItem,
attribute: secondAttribute,
multiplier: multiplier,
constant: constant)
nc.priority = priority
nc.shouldBeArchived = self.shouldBeArchived
nc.identifier = self.identifier
//NSLayoutConstraint.activate([nc])
nc.isActive = true
return nc
}
}
Просто пример демонстрации...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
_teste()
delay(5) {
print("changing any 'fraction fo another view' style widths ...\n\n")
let cc = self.animeHolder.widthAsFractionOfAnotherViewConstraints()
for c in cc {
c.changeToNewConstraintWith(multiplier: 0.25)
}
self._teste()
}
delay(10) {
print("changing any 'points' style widths ...\n\n")
let cc = self.animeHolder.widthAsPointsConstraints()
for c in cc {
c.constant = 150.0
}
self._teste()
}
}
func _teste() {
print("\n---- allConstraints")
for c in animeHolder.allConstraints() {
print("\n \(c)")
}
print("\n---- widthAsPointsConstraints")
for c in animeHolder.widthAsPointsConstraints() {
print("\n \(c)\n \(c.multiplier) \(c.constant)")
}
print("\n---- widthAsFractionOfAnotherViewConstraints")
for c in animeHolder.widthAsFractionOfAnotherViewConstraints() {
print("\n \(c)\n \(c.multiplier) \(c.constant)")
}
print("\n----\n")
}