Понимать convertRect: toView :, convertRect: FromView :, convertPoint: toView: и convertPoint: fromView: методы - PullRequest
122 голосов
/ 11 декабря 2011

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

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

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

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

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

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

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

Заранее спасибо.

РЕДАКТИРОВАТЬ: для тех, кто заинтересован, я создал простой фрагмент кода, чтобы понять эти методы.

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 выражается в координатах superview (self.window). Вывод следующий:

convertPoint: toView: {0, 0}

Ответы [ 9 ]

178 голосов
/ 11 декабря 2011

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

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

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

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

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

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

32 голосов
/ 18 октября 2016

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

import UIKit
import PlaygroundSupport

class 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

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

enter image description here

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

30 голосов
/ 13 июня 2012

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

Если вы хотите преобразовать прямоугольник подпредставления (aView - подпредставление [aView superview]) в координатное пространство другого представления (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];
14 голосов
/ 11 декабря 2011

Каждый вид в 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. Теперь возьмите точку ВНУТРИ систему координат V2, скажем, (20, 20). Теперь, что эта точка будет внутри системы координат V1? Для этого и нужны методы (конечно, вы можете рассчитать сами, но они сэкономят вам лишнюю работу). Для записи, точка в V1 будет (30, 30).

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

8 голосов
/ 08 января 2014

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

Мой контроллер просмотра имеет нормальный вид.

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

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

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];
}

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

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

3 голосов
/ 27 марта 2017

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

    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)

}
3 голосов
/ 25 мая 2012

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

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

Итак, в моем случае, у меня есть родительское представление под названием self.view, в этом self.view я добавил подпредставления с именем self.child1OfView, self.child2OfView. В self.child1OfView я добавил подпредставления под названием 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];
1 голос
/ 21 марта 2013

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

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

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

1 голос
/ 07 апреля 2012

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

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

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

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

или

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

Могу ли я понять, как это должно работать?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...