Entender convertRect: toView :, convertRect: FromView :, convertPoint: toView: y convertPoint: fromView: methods

Estoy tratando de comprender las funcionalidades de estos methods. ¿Podrías darme una simple contraseña para comprender su semántica?

Desde la documentation, por ejemplo, convertPoint: fromView: method se describe de la siguiente manera:

Convierte un punto del sistema de coorderadas de una vista determinada a la del receptor.

¿Qué significa el sistema de coorderadas ? ¿Qué hay del receptor ?

Por ejemplo, ¿tiene sentido usar convertPoint: fromView: como el siguiente?

CGPoint p = [view1 convertPoint:view1.center fromView:view1]; 

Usando la utilidad NSLog, he verificado que el valor p coincide con el centro de view1.

Gracias de antemano.

EDITAR: para aquellos interesados, he creado un fragment de código simple para comprender estos methods.

 UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 200)]; view1.backgroundColor = [UIColor networkingColor]; NSLog(@"view1 frame: %@", NSStringFromCGRect(view1.frame)); NSLog(@"view1 center: %@", NSStringFromCGPoint(view1.center)); CGPoint originInWindowCoordinates = [self.window convertPoint:view1.bounds.origin fromView:view1]; NSLog(@"convertPoint:fromView: %@", NSStringFromCGPoint(originInWindowCoordinates)); CGPoint originInView1Coordinates = [self.window convertPoint:view1.frame.origin toView:view1]; NSLog(@"convertPoint:toView: %@", NSStringFromCGPoint(originInView1Coordinates)); 

En ambos casos self.window es el receptor. Pero hay una diferencia. En el primer caso, el parámetro convertPoint se expresa en coorderadas view1. La salida es la siguiente:

convertPoint: fromView: {100, 100}

En el segundo, en cambio, el punto de conversión se expresa en las coorderadas superview (self.window). La salida es la siguiente:

convertPoint: toView: {0, 0}

Cada vista tiene su propio sistema de coorderadas, con un origen a 0,0 y un ancho y alto. Esto se describe en el rectángulo de bounds de la vista. El frame de la vista, sin embargo, tendrá su origen en el punto dentro del rectángulo de límites de su supervisión.

La vista más externa de su jerarquía de vista tiene su origen en 0,0, que corresponde a la parte superior izquierda de la pantalla en iOS.

Si agrega una subvista a las 20,30 a esta vista, entonces un punto en 0,0 en la subvista corresponde a un punto a las 20,30 en la supervisión. Esta conversión es lo que están haciendo esos methods.

Su ejemplo anterior no tiene sentido (sin juego de palabras) ya que convierte un punto de una vista en sí mismo, por lo que no ocurrirá nada. Más comúnmente descubriría dónde estaba algún punto de una vista en relación con su supervisión: para probar si una vista se estaba alejando de la pantalla, por ejemplo:

 CGPoint originInSuperview = [superview convertPoint:CGPointZero fromView:subview]; 

El "receptor" es un término objective-c estándar para el object que recibe el post (los methods también se conocen como posts), por lo que en mi ejemplo aquí el receptor está superview .

Aquí hay una explicación en inglés sencillo.

Cuando desee convertir el rect de una subvista ( aView es una subvista de [aView superview] ) al espacio de coorderadas de otra vista ( self ).

 // So here I want to take some subview and put it in my view's coordinate space _originalFrame = [[aView superview] convertRect: aView.frame toView: self]; 

Siempre encuentro que esto es confuso, así que hice un patio donde puedes explorar visualmente lo que hace la function de convert . Esto se hace en Swift 3 y Xcode 8.1b:

 import UIKit import PlaygroundSupport class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Main view self.view.backgroundColor = .black self.view.frame = CGRect(x: 0, y: 0, width: 500, height: 500) // Red view let networkingView = UIView(frame: CGRect(x: 20, y: 20, width: 460, height: 460)) networkingView.backgroundColor = .networking self.view.addSubview(networkingView) // Blue view let blueView = UIView(frame: CGRect(x: 20, y: 20, width: 420, height: 420)) blueView.backgroundColor = .blue networkingView.addSubview(blueView) // Orange view let orangeView = UIView(frame: CGRect(x: 20, y: 20, width: 380, height: 380)) orangeView.backgroundColor = .orange blueView.addSubview(orangeView) // Yellow view let yellowView = UIView(frame: CGRect(x: 20, y: 20, width: 340, height: 100)) yellowView.backgroundColor = .yellow orangeView.addSubview(yellowView) // Let's try to convert now var resultFrame = CGRect.zero let randomRect: CGRect = CGRect(x: 0, y: 0, width: 100, height: 50) /* func convert(CGRect, from: UIView?) Converts a rectangle from the coordinate system of another view to that of the receiver. */ // The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view: resultFrame = self.view.convert(randomRect, from: yellowView) // Try also one of the following to get a feeling of how it works: // resultFrame = self.view.convert(randomRect, from: orangeView) // resultFrame = self.view.convert(randomRect, from: networkingView) // resultFrame = self.view.convert(randomRect, from: nil) /* func convert(CGRect, to: UIView?) Converts a rectangle from the receiver's coordinate system to that of another view. */ // The following line converts a rectangle (randomRect) from the coordinate system of yellowView to that of self.view resultFrame = yellowView.convert(randomRect, to: self.view) // Same as what we did above, using "from:" // resultFrame = self.view.convert(randomRect, from: yellowView) // Also try: // resultFrame = orangeView.convert(randomRect, to: self.view) // resultFrame = networkingView.convert(randomRect, to: self.view) // resultFrame = orangeView.convert(randomRect, to: nil) // Add an overlay with the calculated frame to self.view let overlay = UIView(frame: resultFrame) overlay.backgroundColor = UIColor(white: 1.0, alpha: 0.9) overlay.layer.borderColor = UIColor.black.cgColor overlay.layer.borderWidth = 1.0 self.view.addSubview(overlay) } } var ctrl = MyViewController() PlaygroundPage.current.liveView = ctrl.view 

Recuerde mostrar el Editor auxiliar ( ) para ver las vistas, debería verse así:

introduzca la descripción de la imagen aquí

Siéntase libre de contribuir con más ejemplos aquí o en este sentido .

Cada vista en iOS tiene un sistema de coorderadas. Un sistema de coorderadas es como un gráfico, que tiene un eje x (línea horizontal) y un eje y (línea vertical). El punto en el que las líneas interesect se llama origen. Un punto está representado por (x, y). Por ejemplo, (2, 1) significa que el punto está 2 píxeles a la izquierda y 1 píxel hacia abajo.

Puede leer más sobre los sistemas de coorderadas aquí: http://en.wikipedia.org/wiki/Coordinate_system

Pero lo que necesita saber es que, en iOS, cada vista tiene su PROPIO sistema de coorderadas, donde la esquina superior izquierda es el origen. El eje X continúa aumentando a la derecha, y el eje y continúa aumentando hacia abajo.

Para la pregunta sobre puntos de conversión, tome este ejemplo.

Hay una vista, llamada V1, que tiene 100 píxeles de ancho y 100 píxeles de alto. Ahora dentro de eso, hay otra vista, llamada V2, en (10, 10, 50, 50) lo que significa que (10, 10) es el punto en el sistema de coorderadas de V1 donde debe ubicarse la esquina superior izquierda de V2 y ( 50, 50) es el ancho y alto de V2. Ahora, tome un punto del sistema de coorderadas INSIDE V2, diga (20, 20). Ahora, ¿cuál sería ese punto dentro del sistema de coorderadas de V1? Para eso sirven los methods (por supuesto, usted puede calcularse a sí mismo, pero le ahorran trabajo adicional). Para que conste, el punto en V1 sería (30, 30).

Espero que esto ayude.

Gracias a todos por publicar la pregunta y sus respuestas: me ayudó a resolver esto.

Mi controller de vista tiene su vista normal.

Dentro de esa vista, hay una serie de vistas de agrupación que hacen poco más que proporcionar a sus vistas secundarias una interacción limpia con restricciones de layout automático.

Dentro de una de esas vistas de agrupación, tengo un button Agregar que presenta un controller de vista emergente donde el usuario ingresa cierta información.

 view --groupingView ----addButton 

Durante la rotation del dispositivo, el controller de vista se alerta mediante la llamada UIPopoverViewControllerDelegate popoverController: willRepositionPopoverToRect: inView:

 - (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView *__autoreleasing *)view { *rect = [self.addButton convertRect:self.addbutton.bounds toView:*view]; } 

La parte esencial que viene de la explicación dada por las primeras dos respuestas anteriores fue que el rectángulo que necesitaba para convertir era los límites del button Agregar, no su marco.

No he intentado esto con una jerarquía de vista más compleja, pero sospecho que al usar la vista suministrada en la llamada de método (inView :), sorteamos las complicaciones de los types de fealdad de vista de hojas de varios niveles.

Utilicé esta publicación para aplicar en mi caso. Espero que esto ayude a otro lector en el futuro.

Una vista solo puede ver sus vistas inmediatas de hijos y padres. No puede ver a sus abuelos ni a sus nietos.

Por lo tanto, en mi caso, tengo una vista de gran padre llamada self.view , en este self.view he agregado subvenciones llamadas self.child1OfView , self.child2OfView . En self.child1OfView , he agregado subvenciones llamadas self.child1OfView1 , self.child2OfView1 .
Ahora si muevo físicamente self.child1OfView1 a un área fuera del límite de self.child1OfView a otro spot en self.view , luego a calculator la nueva position para self.child1OfView1 dentro de self.view:

 CGPoint newPoint = [self.view convertPoint:self.child1OfView1.center fromView:self.child1OfView]; 

Puede ver el código a continuación para que pueda comprender cómo funciona realmente.

  let scrollViewTemp = UIScrollView.init(frame: CGRect.init(x: 10, y: 10, width: deviceWidth - 20, height: deviceHeight - 20)) override func viewDidLoad() { super.viewDidLoad() scrollViewTemp.backgroundColor = UIColor.lightGray scrollViewTemp.contentSize = CGSize.init(width: 2000, height: 2000) self.view.addSubview(scrollViewTemp) let viewTemp = UIView.init(frame: CGRect.init(x: 100, y: 100, width: 150, height: 150)) viewTemp.backgroundColor = UIColor.green self.view.addSubview(viewTemp) let viewSecond = UIView.init(frame: CGRect.init(x: 100, y: 700, width: 300, height: 300)) viewSecond.backgroundColor = UIColor.networking self.view.addSubview(viewSecond) self.view.convert(viewTemp.frame, from: scrollViewTemp) print(viewTemp.frame) /* First take one point CGPoint(x: 10, y: 10) of viewTemp frame,then give distance from viewSecond frame to this point. */ let point = viewSecond.convert(CGPoint(x: 10, y: 10), from: viewTemp) //output: (10.0, -190.0) print(point) /* First take one point CGPoint(x: 10, y: 10) of viewSecond frame,then give distance from viewTemp frame to this point. */ let point1 = viewSecond.convert(CGPoint(x: 10, y: 10), to: viewTemp) //output: (10.0, 210.0) print(point1) /* First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewSecond frame,then give distance from viewTemp frame to this rect. */ let rect1 = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), to: viewTemp) //output: (10.0, 210.0, 20.0, 20.0) print(rect1) /* First take one rect CGRect(x: 10, y: 10, width: 20, height: 20) of viewTemp frame,then give distance from viewSecond frame to this rect. */ let rect = viewSecond.convert(CGRect(x: 10, y: 10, width: 20, height: 20), from: viewTemp) //output: (10.0, -190.0, 20.0, 20.0) print(rect) } 

Leí la respuesta y entendí la mecánica pero creo que el ejemplo final no es correcto. De acuerdo con el API doc, la propiedad del centro de una vista contiene el punto central conocido de la vista en el sistema de coorderadas de la supervisión.

Si este es el caso, creo que no tendría sentido tratar de pedirle a la supervista que convierta el centro de una subvista DESDE el sistema de coorderadas de subvista porque el valor no está en el sistema de coorderadas de la subvista. Lo que tendría sentido es hacer lo opuesto, es decir, convertir del sistema de coorderadas de supervisión al de una subvista …

Puede hacerlo de dos maneras (ambas deberían arrojar el mismo valor):

 CGPoint centerInSubview = [subview convertPoint:subview.center fromView:subview.superview]; 

o

 CGPoint centerInSubview = [subview.superview convertPoint:subview.center toView:subview]; 

¿Estoy lejos para entender cómo debería funcionar esto?

Un punto más importante sobre el uso de estas API. Asegúrese de que la cadena de vista principal se complete entre el rectificador que está convirtiendo y la vista de input / salida. Por ejemplo, aView, bView y cView:

  • aView es una subvista de bView
  • Queremos convertir aView.frame en cView.

Si intentamos ejecutar el método antes de que bView se haya agregado como una subvista de cView, obtendremos una respuesta de litera. Lamentablemente, no hay protección incorporada en los methods para este caso. Esto puede parecer obvio, pero es algo a tener en count en los casos en que la conversión pasa por una larga cadena de padres.