iOS 坐标系统与转换方法

xdopnet 7年前
   <p style="text-align:center"><img src="https://simg.open-open.com/show/faa85cd8a51b8ff0bc861060b63b2d14.jpg"></p>    <p>有些人剛開始學iOS開發,都會通過Storyboard等可視化的工具來寫進行排版,相對經常通過code來撰寫App的人來說,可能對於UIKit中的座標系統只有很初步的認識。</p>    <p>如果未來想要更輕鬆迅速的通過code來進行constraint的設置、動畫的製作、互動效果的建立,這勢必要非常了解座標系統的原理。</p>    <h2> </h2>    <p>我們從這裡開始</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f1128f3b212055216e5d43c6f0ab0c35.png"></p>    <ul>     <li>左邊可以看到每一個View之間的原點距離都是(40, 40),灰色的最左上角為( 0, 0 )。</li>     <li>右邊說明greenView放在redView上、redView放在blueView上、blueView放在view(灰色)上。</li>    </ul>    <p>如果直接print(blue.frame),大家都知道會得到(40, 40, 250, 250)。</p>    <p>但是,如果綠色的長寬都為50,此時print(greenView.frame),你知道會得到什麼嗎?</p>    <p>如果你認為結果是(120, 120, 50, 50),那看來真的需要好好了解一下座標系統的原理了。</p>    <p>對了,你應該知道 iOS座標系統的原點在左上角 ,而 CoreGraphics座標系統的原點在左下角 吧,雖然這次不會講到CG的部分。</p>    <h2>UIKit座標系統</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/836bb7a684f987b0460649c86a298118.png"></p>    <p>其實 每一個View都有各自的座標系統 ,而frame的值是根據superView的座標系統而來的。根據上面的圖我們來舉例子。</p>    <p>注:View之間的間距仍然為 (40, 40)</p>    <h3>Frame</h3>    <p>Frame的值,是根據superView的座標系統而來。superView的左上角為原點(0, 0)。</p>    <ul>     <li>左邊的圖中,blueView是redView的superView,所以redView.frame的值為( 40, 40, 100, 100)。</li>     <li>右邊的圖中,redView是greenView的superView,所以greenView.frame的值為( 40, 40, 50, 50)。</li>    </ul>    <h3>Bounds</h3>    <p>Bounds的值,根據自身的座標系統而來。即初始化狀態自身左上角為原點(0, 0)。</p>    <ul>     <li>左邊的圖中,blueView.bounds的值為( 0, 0, 100, 100)</li>     <li>右邊的圖中,greenView.bounds值為(40, 40, 50, 50)</li>    </ul>    <h2>座標轉換</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b03b835064a011b2532b35a878657c72.png"></p>    <p>還是這張圖,我們可以很容易得到一個View相對superView的座標(通過「.frame」),但請思考看看。</p>    <ul>     <li>如何取得grrenView在blueView座標體系中的frame?</li>     <li>畫面中View的間距是 40。</li>    </ul>    <p>CALayer給不同座標系之間的圖層轉換提供了一些方法,通過這些方法可以快速的進行座標的轉換。</p>    <pre>  <code class="language-objectivec">    openfunc convert(_ point: CGPoint, to view: UIView?) -> CGPoint      openfunc convert(_ point: CGPoint, fromview: UIView?) -> CGPoint      openfunc convert(_ rect: CGRect, to view: UIView?) -> CGRect      openfunc convert(_ rect: CGRect, fromview: UIView?) -> CGRect  </code></pre>    <p>我們要如何拿到greenView在blueView座標體系中的frame呢?</p>    <p>如果直接print(greenView.frame)我們會得到( 40, 40, 80, 80),因為greenView的superView是redView。</p>    <p>那麼我們要拿到greenView位於blueView座標體系中的frame,就可以通過下面兩個方法。</p>    <pre>  <code class="language-objectivec">convertedFrame = blueView.convert(greenView.frame, from: redView)  convertedFrame = redView.convert(greenView.frame, to: view)  </code></pre>    <p>此時print(convertedFrame) 會得到 ( 120, 120, 80, 80)。</p>    <h2>判斷包含或重疊狀況</h2>    <p><img src="https://simg.open-open.com/show/0d350edc665c6cd9666ea44c6161da85.png" alt="iOS 坐标系统与转换方法" width="300" height="296"></p>    <p>在同一個座標系中</p>    <p>判斷是否包含</p>    <pre>  <code class="language-objectivec">func contains(_ rect2: CGRect) -> Bool  </code></pre>    <p>根據上圖的例子:</p>    <pre>  <code class="language-objectivec">        // contain test          var isContain = false          isContain = blueView.subviews.contains(redView) // true          isContain = blueView.subviews.contains(greenView) // false          isContain = greenView.subviews.contains(redView) // false  </code></pre>    <p>判斷是否重疊</p>    <pre>  <code class="language-objectivec">func intersects(_ rect2: CGRect) -> Bool  </code></pre>    <p>根據上圖的例子:</p>    <pre>  <code class="language-objectivec">        var isIntersect = false          isIntersect = blueView.frame.intersects(redView.frame) // ture          isIntersect = blueView.frame.intersects(greenView.frame) // true  </code></pre>    <h2>Playground</h2>    <p>為了方便你更好的研究座標轉換的關係,建議可以在playground上操作試試看。</p>    <pre>  <code class="language-objectivec">import UIKit  import PlaygroundSupport        class CoordinateController:UIViewController {      override func viewDidLoad() {          view.frame = CGRect(x: 0, y: 0, width: 320, height: 320)          view.backgroundColor = UIColor.lightGray                    // blueView          let blueView = UIView(frame: CGRect(x: 40, y: 40, width: 240, height: 240))          blueView.backgroundColor = UIColor(red: 74/255, green: 144/255, blue: 226/255, alpha: 1)          view.addSubview(blueView)                    // redView          let redView = UIView(frame: CGRect(x: 40, y: 40, width: 160, height: 160))          redView.backgroundColor = UIColor(red: 246/255, green: 110/255, blue: 101/255, alpha: 1)          blueView.addSubview(redView)                    // greenView          let greenView = UIView(frame: CGRect(x: 40, y: 40, width: 80, height: 80))          greenView.backgroundColor = UIColor(red: 80/255, green: 227/255, blue: 194/255, alpha: 1)          redView.addSubview(greenView)                    // whiteBox & convertedFrame          let whiteBoxView = UIView(frame: CGRect(x: 20, y: 20, width: 100, height: 40))          whiteBoxView.backgroundColor = UIColor.white          whiteBoxView.layer.borderColor = UIColor.black.cgColor          whiteBoxView.layer.borderWidth = 2  //        blueView.addSubview(whiteBoxView)                    var convertedFrame = CGRect.zero                    // convert          convertedFrame = view.convert(whiteBoxView.frame, from: blueView)  //        convertedFrame = blueView.convert(whiteBoxView.frame, to: view)  //        convertedFrame = view.convert(greenView.frame, from: redView)  //        convertedFrame = redView.convert(greenView.frame, to: view)  //        convertedFrame = view.convert(redView.frame, from: blueView)  //        convertedFrame = blueView.convert(greenView.frame, from: redView)                       // add randomFrame          whiteBoxView.frame = convertedFrame          view.addSubview(whiteBoxView)                          }  }     var viewController = CoordinateController()  PlaygroundPage.current.liveView = viewController.view  </code></pre>    <h2>推薦和參考</h2>    <p> </p>    <p>来自:https://ios.devdon.com/archives/620</p>    <p> </p>