ios - www - ¿Cómo “encontrar” tu propia restricción?
www.childrensplace.com en español (3)
Podría ahorrarle a alguien algo de escribir .......
Basado en la respuesta ganadora de recompensas de stakri, aquí es exactamente cómo obtener
Todas las restricciones del tipo "ancho fraccional de otra vista"
Todas las restricciones del tipo "ancho de punto fijo"
Asi que ..
fileprivate extension UIView {
func widthAsPointsConstraints()->[NSLayoutConstraint] {}
func widthAsFractionOfAnotherViewConstraints()->[NSLayoutConstraint] {}
}
Kode completo a continuación. Por supuesto, puedes hacer "altura" de la misma manera.
Por lo tanto, úsalos así ...
let cc = someView.widthAsFractionOfAnotherViewConstraints()
for c in cc {
c.changeToNewConstraintWith(multiplier: 0.25)
}
o
let cc = someView.widthAsPointsConstraints()
for c in cc {
c.constant = 150.0
}
Además, en la parte inferior pegué en un código de demostración simple, ejemplo de salida ...
Aquí está el kode. 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
}
}
Sólo un ejemplo de demostración ...
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")
}
Digamos que tengo una vista,
class CleverView: UIView
En la clase personalizada, quiero hacer esto:
func changeWidth() {
let c = ... find my own layout constraint, for "width"
c.constant = 70 * Gameinfo.ImportanceOfEnemyFactor
}
De manera similar, quiero poder "encontrar" así, la restricción (o supongo que, todas las restricciones, podría haber más de una) unida a uno de los cuatro bordes.
Por lo tanto, para revisar todas las restricciones que se me unen y encontrar las de ancho / alto, o incluso las relevantes para un borde dado (por ejemplo, "izquierda").
¿Algunas ideas?
Tal vez vale la pena señalar esta pregunta
Tenga en cuenta que (obviamente) estoy preguntando cómo hacerlo de forma dinámica / programática.
(Sí, puede decir "enlace a la restricción" o "usar una ID"; el punto principal del control de calidad es cómo encontrarlos sobre la marcha y trabajar de forma dinámica).
Si no está .constraints
con las restricciones, tenga en cuenta que .constraints
solo le da los fines almacenados "allí".
Realmente hay dos casos:
- Las restricciones con respecto al tamaño de una vista o las relaciones con las vistas descendientes se guardan en sí mismas
- Las restricciones entre dos vistas se guardan en el antepasado común más bajo de las vistas
Repetir. Para restricciones que se encuentran entre dos vistas. De hecho, iOS siempre los almacena en el antepasado común más bajo. Por lo tanto, siempre se puede encontrar una restricción de una vista buscando en todos los antepasados de la vista.
Por lo tanto, necesitamos verificar la vista en sí misma y todas sus supervisiones para ver si hay restricciones. Un enfoque podría ser:
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
}
}
}
Puede aplicar todo tipo de filtros después de obtener todas las restricciones sobre una vista, y supongo que esa es la parte más difícil. Algunos ejemplos:
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''s width equal to another view''s width
// - another view''s height equal to this view''s 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})
}
}
// editar: ejemplos mejorados y aclaró sus explicaciones en los comentarios
Supongo que puedes trabajar con developer.apple.com/documentation/uikit/uiview/… propiedad de UIView
. constraints
básicamente devuelven un conjunto de restricciones directamente asignadas a UIView. No podrá obtener las restricciones mantenidas por supervisión, tales como avance, finalización, superior o inferior, pero las restricciones de ancho y alto son mantenidas por la propia Vista. Para las restricciones de supervisión, puede recorrer las restricciones de supervisión. Digamos que la vista inteligente tiene estas limitaciones:
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)")
}
}
Salida :
parte superior
líder
al final
restricciones de supervisión: 3
altura
auto restricciones: 1
Básicamente, puede ver la clase NSLayoutConstraint para obtener la información sobre una restricción en particular.
Para imprimir el nombre de las restricciones, podemos usar esta extensión.
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"
}
}
}