Понять convertRect: toView:, convertRect: FromView:, convertPoint: toView: и convertPoint: fromView: методы

Я пытаюсь понять функциональность этих методов. Не могли бы вы предоставить мне простой способ для понимания их семантики?

В документации, например, метод convertPoint: fromView: описывается следующим образом:

Преобразует точку из системы координат данного вида в точку получателя.

Что означает система координат? Как насчет приемника ?

Например, имеет смысл использовать convertPoint: fromView:, как показано ниже:

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

Используя утилиту NSLog, я проверил, что значение p совпадает с центром view1.

Спасибо заранее.

EDIT: для тех, кого это интересует, я создал простой фрагмент кода, чтобы понять эти методы.

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));

В обоих случаях self.window является приемником. Но есть разница. В первом случае параметр convertPoint выражается в координатах view1. Вывод следующий:

convertPoint: fromView: {100, 100}

Во втором, вместо этого, convertPoint выражается в координатах superiew (self.window). Вывод следующий:

convertPoint: toView: {0, 0}

Ответ 1

Каждый вид имеет свою собственную систему координат - с началом в 0,0 и шириной и высотой. Это описано в прямоугольнике bounds вида. Тем не менее, frame представления будет иметь свое начало в точке в пределах прямоугольника границ его супервизора.

Внешний вид вашей иерархии представлений имеет начало координат 0,0, что соответствует левому верхнему углу экрана в iOS.

Если вы добавите subview на 20,30 к этому представлению, то точка в 0,0 в подвьюте соответствует точке 20,30 в супервизии. Это преобразование - это то, что делают эти методы.

Ваш пример выше бессмыслен (не предназначен для каламбура), поскольку он преобразует точку из представления в себя, поэтому ничего не произойдет. Вы чаще узнаете, где какая-то точка зрения была связана с его супервидом - чтобы проверить, перемещалось ли изображение с экрана, например:

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

"Приемник" является стандартным термином objective-c для объекта, который принимает сообщение (методы также известны как сообщения), поэтому в моем примере здесь приемник superview.

Ответ 2

Вот объяснение на простом английском языке.

Если вы хотите преобразовать прямоугольник subview (aView - это под-просмотр [aView superview]) в координатное пространство другого представления (self).

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

Ответ 3

Я всегда нахожу это запутанным, поэтому я создал игровую площадку, где вы можете визуально изучить, что делает функция convert. Это делается в Swift 3 и Xcode 8.1b:

import UIKit import PlaygroundSupport

класс 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 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

Не забудьте показать редактор-ассистент ( ), чтобы просмотреть просмотры, он должен выглядеть следующим образом:

введите описание изображения здесь

Не стесняйтесь приводить больше примеров здесь или в этот смысл.

Ответ 4

Каждый вид в iOS имеет систему координат. Система координат похожа на график, который имеет ось x (горизонтальная линия) и ось y (вертикальная линия). Точка, в которой линии пересекаются, называется началом. Точка представлена ​​(x, y). Например, (2, 1) означает, что точка имеет 2 пикселя влево и 1 пиксель вниз.

Здесь вы можете прочитать больше о системах координат - http://en.wikipedia.org/wiki/Coordinate_system

Но вам нужно знать, что в iOS у каждого представления есть СОБСТВЕННАЯ система координат, где верхний левый угол - это начало. Ось X возрастает вправо, а ось y продолжает увеличиваться.

Для вопроса о точках преобразования возьмите этот пример.

Существует вид, называемый V1, который имеет ширину 100 пикселей и высоту 100 пикселей. Теперь внутри этого есть еще один вид, называемый V2, в (10, 10, 50, 50), что означает, что (10, 10) является точкой в ​​системе координат V1, где должен располагаться верхний левый угол V2 и ( 50, 50) - ширина и высота V2. Теперь возьмите точку системы координат INSIDE V2, скажем (20, 20). Теперь, что бы эта точка была внутри системы координат V1? Для этого используются методы (конечно, вы можете рассчитать себя, но они сэкономит вам дополнительную работу). Для записи точка в V1 будет (30, 30).

Надеюсь, что это поможет.

Ответ 5

Спасибо всем за сообщение вопроса и ваши ответы: Это помогло мне разобраться в этом.

Контроллер моего представления имеет нормальный вид.

Внутри этого представления есть несколько видов группировки, которые делают немного больше, чем дать их дочерним представлениям чистое взаимодействие с ограничениями автоматического макета.

Внутри одного из этих представлений группировки у меня есть кнопка "Добавить", в которой представлен контроллер popover view, где пользователь вводит некоторую информацию.

view
--groupingView
----addButton

При вращении устройства контроллер вида предупреждается через вызов 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];
}

Существенная часть, полученная из объяснений, изложенных в первых двух ответах выше, заключалась в том, что я должен был преобразовать из рамки кнопки добавления, а не ее рамки.

Я не пробовал это с более сложной иерархией представлений, но я подозреваю, что, используя представление, представленное в вызове метода (inView:), мы сталкиваемся с осложнениями многоуровневых видов просмотра уродства.

Ответ 6

Я использовал этот пост для применения в моем случае. Надеюсь, это поможет другому читателю в будущем.

Вид может видеть только его непосредственные дочерние и родительские представления. Он не может видеть своих великих родителей или своих внуков.

Итак, в моем случае у меня есть представление с большим родителем под названием self.view, в этом self.view у меня есть subviews, называемые self.child1OfView, self.child2OfView. В self.child1OfView я добавил subviews под названием self.child1OfView1, self.child2OfView1.
Теперь, если я физически перемещаю self.child1OfView1 в область за пределами границы self.child1OfView в точку пыльника на self.view, то для калькуляции новой позиции для self.child1OfView1 в пределах self.view:

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

Ответ 7

Вы можете увидеть ниже код, чтобы вы могли понять, как он работает.

    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)

}

Ответ 8

Я прочитал ответ и понял механику, но я думаю, что последний пример неверен. Согласно API doc, свойство центра представления содержит известную центральную точку представления в системе координат superiews.

Если это так, то я думаю, что было бы нецелесообразно попросить супервизор преобразовать центр subview FROM из системы координат subview, потому что это значение не находится в системе координат subview. Было бы целесообразно сделать обратное, то есть преобразовать из системы координат супервизора в систему subview...

Вы можете сделать это двумя способами (оба должны давать одинаковое значение):

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

или

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

Неужели я понимаю, как это должно работать?

Ответ 9

Еще один важный момент в использовании этих API. Убедитесь, что цепочка родительского представления завершена между преобразованием rect, которое вы преобразовываете, и в/из представления. Например - aView, bView и cView -

  • aView - это подпункт bView
  • мы хотим преобразовать aView.frame в cView

Если мы попытаемся выполнить метод до того, как bView будет добавлен как подзадача cView, мы вернем ответ на бит. К сожалению, в этом случае защиты нет встроенных методов. Это может показаться очевидным, но об этом нужно знать в тех случаях, когда преобразование проходит через длинную цепочку родителей.