ios layout uiview

ios - Comprender convertRect: toView:, convertRect: FromView:, convertPoint: toView: y convertPoint: fromView: methods



layout uiview (9)

Estoy tratando de entender las funcionalidades de estos métodos. ¿Podrías proporcionarme un simple uso para entender su semántica?

De la documentación, por ejemplo, el método convertPoint: fromView: se describe de la siguiente manera:

Convierte un punto del sistema de coordenadas de una vista dada a la del receptor.

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

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

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

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

Gracias de antemano.

EDITAR: para los interesados, he creado un fragmento de código simple para comprender estos métodos.

UIView* view1 = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 150, 200)]; view1.backgroundColor = [UIColor redColor]; 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 coordenadas view1. El resultado es el siguiente:

convertPoint: fromView: {100, 100}

En el segundo, en cambio, el convertPoint se expresa en coordenadas superview (self.window). El resultado es el siguiente:

convertPoint: toView: {0, 0}


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

Cuando quiere convertir el rect de una subvista ( aView es una subvista de [aView superview] ) al espacio de coordenadas 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];


Cada vista en iOS tiene un sistema de coordenadas. Un sistema de coordenadas 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 se interesan se llama origen. Un punto está representado por (x, y). Por ejemplo, (2, 1) significa que el punto está a 2 píxeles a la izquierda, y 1 píxel abajo.

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

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

Para la pregunta de los 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 coordenadas de V1 donde debe ubicarse la esquina superior izquierda de V2, y 50, 50) es el ancho y la altura de V2. Ahora, tome un punto del sistema de coordenadas INSIDE V2, digamos (20, 20). Ahora, ¿cuál sería ese punto dentro del sistema de coordenadas de V1? Para eso están los métodos (por supuesto, pueden calcularse ellos mismos, pero les ahorran trabajo adicional). Para el registro, el punto en V1 sería (30, 30).

Espero que esto ayude.


Cada vista tiene su propio sistema de coordenadas, con un origen en 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 supervista.

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 a 0,0 en la subvista corresponde a un punto a las 20,30 en la supervista. Esta conversión es lo que están haciendo esos métodos.

Su ejemplo anterior no tiene sentido (no es un juego de palabras) ya que convierte un punto de una vista en sí mismo, por lo que no sucederá nada. En general, descubriría dónde estaba el punto de vista en relación con su supervista: para comprobar si una vista se estaba moviendo fuera de la pantalla, por ejemplo:

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

El "receptor" es un término objetivo-c estándar para el objeto que recibe el mensaje (los métodos también se conocen como mensajes) por lo que en mi ejemplo aquí el receptor es superview .


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

Mi controlador de vista tiene su vista normal.

Dentro de esa vista hay varias vistas de agrupación que hacen poco más que darle a sus vistas hijas una interacción limpia con restricciones de diseño automático.

Dentro de una de esas vistas de agrupamiento, tengo un botón Agregar que presenta un controlador de vista popover donde el usuario ingresa cierta información.

view --groupingView ----addButton

Durante la rotación del dispositivo, el controlador de vista se alerta a través de 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 proviene de la explicación dada por las primeras dos respuestas anteriores fue que el rect que necesitaba convertir era el límite del botón de agregar, no su marco.

No he intentado esto con una jerarquía de vistas más compleja, pero sospecho que al usar la vista proporcionada en la llamada al método (inView :) obtenemos las complicaciones de los tipos de fealdad en varias hojas.


Leí la respuesta y entendí la mecánica, pero creo que el último ejemplo no es correcto. De acuerdo con el documento API, la propiedad central de una vista contiene el punto central conocido de la vista en el sistema de coordenadas de la supervista.

Si este es el caso, entonces creo que no tendría sentido tratar de pedirle a la supervista que convierta el centro de una subvista DESDE el sistema de coordenadas de la subvista porque el valor no está en el sistema de coordenadas de la subvista. Lo que tendría sentido es hacer lo contrario, es decir, convertir el sistema de coordenadas de la supervista al de una subvista ...

Puedes hacerlo de dos maneras (ambas deben producir el mismo valor):

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

o

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

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


Puede ver el código a continuación para que pueda entender cómo funciona en realidad.

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.red 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) }


Siempre me resulta confuso, así que hice un patio de juegos donde puedes explorar visualmente lo que hace la función de convert . Esto se hace en Swift 3 y Xcode 8.1b:

importar importación de UIKit PlaygroundSupport

clase MyViewController: UIViewController {

override func viewDidLoad() { super.viewDidLoad() // Main view view.backgroundColor = .black view.frame = CGRect(x: 0, y: 0, width: 500, height: 500) // Red view let redView = UIView(frame: CGRect(x: 20, y: 20, width: 460, height: 460)) redView.backgroundColor = .red view.addSubview(redView) // Blue view let blueView = UIView(frame: CGRect(x: 20, y: 20, width: 420, height: 420)) blueView.backgroundColor = .blue redView.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 = view.convert(randomRect, from: yellowView) // Try also one of the following to get a feeling of how it works: // resultFrame = view.convert(randomRect, from: orangeView) // resultFrame = view.convert(randomRect, from: redView) // resultFrame = 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: view) // Same as what we did above, using "from:" // resultFrame = view.convert(randomRect, from: yellowView) // Also try: // resultFrame = orangeView.convert(randomRect, to: view) // resultFrame = redView.convert(randomRect, to: 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 view.addSubview(overlay) }

}

var ctrl = MyViewController () PlaygroundPage.current.liveView = ctrl.view

Recuerde mostrar el Editor Asistente ( ) para ver las vistas, debe verse así:

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


Un punto más importante sobre el uso de estas API. Asegúrese de que la cadena de vista primaria esté completa entre la rect que está convirtiendo y la vista hacia y desde. Por ejemplo, aView, bView y cView -

  • aView es una subvista de bView
  • queremos convertir aView.frame a cView

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


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

Una vista solo puede ver sus vistas secundarias y secundarias inmediatas. No puede ver las vistas de sus abuelos o sus nietos.

Entonces, en mi caso, tengo una vista de gran padre llamada self.view , en este self.view he agregado subvistas llamadas self.child1OfView , self.child2OfView . En self.child1OfView , he agregado subvistas llamadas self.child1OfView1 , self.child2OfView1 .
Ahora, si muevo físicamente self.child1OfView1 a un área fuera del límite de self.child1OfView a otro punto en self.view , luego a la calculadora de la nueva posición para self.child1OfView1 dentro de self.view:

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