UIView
dan semua subkelasnya memiliki properti frame
dan bounds
. Apa bedanya?
UIView
dan semua subkelasnya memiliki properti frame
dan bounds
. Apa bedanya?
Jawaban:
Batas - batas sebuah UIView adalah persegi panjang , dinyatakan sebagai lokasi (x, y) dan ukuran (lebar, tinggi) relatif terhadap sistem koordinatnya sendiri (0,0).
The bingkai sebuah UIView adalah persegi panjang , dinyatakan sebagai lokasi (x, y) dan ukuran (lebar, tinggi) relatif terhadap SuperView itu terkandung di dalamnya.
Jadi, bayangkan sebuah tampilan yang memiliki ukuran 100x100 (lebar x tinggi) diposisikan pada 25,25 (x, y) dari superview-nya. Kode berikut mencetak batas dan bingkai tampilan ini:
// This method is in the view controller of the superview
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"bounds.origin.x: %f", label.bounds.origin.x);
NSLog(@"bounds.origin.y: %f", label.bounds.origin.y);
NSLog(@"bounds.size.width: %f", label.bounds.size.width);
NSLog(@"bounds.size.height: %f", label.bounds.size.height);
NSLog(@"frame.origin.x: %f", label.frame.origin.x);
NSLog(@"frame.origin.y: %f", label.frame.origin.y);
NSLog(@"frame.size.width: %f", label.frame.size.width);
NSLog(@"frame.size.height: %f", label.frame.size.height);
}
Dan output dari kode ini adalah:
bounds.origin.x: 0
bounds.origin.y: 0
bounds.size.width: 100
bounds.size.height: 100
frame.origin.x: 25
frame.origin.y: 25
frame.size.width: 100
frame.size.height: 100
Jadi, kita dapat melihat bahwa dalam kedua kasus, lebar dan tinggi tampilan adalah sama terlepas dari apakah kita melihat batas atau bingkai. Yang berbeda adalah pemosisian x, y pada tampilan. Dalam kasus batas, koordinat x dan y berada pada 0,0 karena koordinat ini relatif terhadap tampilan itu sendiri. Namun, koordinat frame x dan y relatif terhadap posisi tampilan dalam tampilan induk (yang sebelumnya kami katakan di 25,25).
Ada juga presentasi hebat yang meliput UIViews. Lihat slide 1-20 yang tidak hanya menjelaskan perbedaan antara frame dan batas tetapi juga menunjukkan contoh visual.
frame = lokasi dan ukuran tampilan menggunakan sistem koordinat tampilan induk
bounds = lokasi dan ukuran tampilan menggunakan sistem koordinatnya sendiri
Untuk membantu saya mengingat bingkai , saya memikirkan bingkai foto di dinding . Bingkai foto seperti batas tampilan. Saya dapat menggantung gambar di mana saja saya mau di dinding. Dengan cara yang sama, saya dapat menempatkan tampilan di mana saja saya inginkan di dalam tampilan induk (juga disebut superview). Tampilan induk seperti dinding. Asal usul sistem koordinat di iOS adalah kiri atas. Kita dapat menempatkan pandangan kita pada asal superview dengan mengatur koordinat xy frame tampilan ke (0, 0), yang seperti menggantung gambar kita di sudut kiri paling atas dinding. Untuk memindahkannya dengan benar, naikkan x, untuk turunkan, naikkan y.
Untuk membantu saya mengingat batasan , saya berpikir tentang lapangan bola basket di mana terkadang bola basket itu terlempar keluar dari batas . Anda menggiring bola di seluruh lapangan basket, tetapi Anda tidak terlalu peduli di mana lapangan itu sendiri. Bisa di gym, atau di luar di sekolah menengah, atau di depan rumah Anda. Itu tidak masalah. Anda hanya ingin bermain bola basket. Dengan cara yang sama, sistem koordinat untuk batas tampilan hanya peduli pada tampilan itu sendiri. Tidak tahu apa-apa tentang di mana tampilan berada di tampilan induk. Asal batas (titik (0, 0) secara default) adalah sudut kiri atas tampilan. Subview apa pun yang dimiliki tampilan ini ditata terkait dengan poin ini. Ini seperti membawa bola basket ke sudut kiri depan lapangan.
Sekarang kebingungan muncul ketika Anda mencoba membandingkan bingkai dan batas. Ini sebenarnya tidak seburuk yang terlihat pada awalnya. Mari kita gunakan beberapa gambar untuk membantu kita memahami.
Pada gambar pertama di sebelah kiri kita memiliki tampilan yang terletak di kiri atas tampilan induknya. Kotak kuning mewakili bingkai tampilan. Di sebelah kanan kita melihat tampilan lagi tetapi kali ini tampilan induk tidak ditampilkan. Itu karena batas tidak tahu tentang pandangan orang tua. Kotak hijau mewakili batas tampilan. Titik merah di kedua gambar mewakili asal bingkai atau batas.
Frame
origin = (0, 0)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
Jadi bingkai dan batasnya persis sama dalam gambar itu. Mari kita lihat contoh di mana mereka berbeda.
Frame
origin = (40, 60) // That is, x=40 and y=60
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
Jadi Anda dapat melihat bahwa mengubah koordinat xy dari frame akan memindahkannya dalam tampilan induk. Namun konten tampilan itu sendiri masih terlihat persis sama. Batasan tidak tahu bahwa ada sesuatu yang berbeda.
Hingga kini lebar dan tinggi bingkai dan batasnya persis sama. Tapi itu tidak selalu benar. Lihat apa yang terjadi jika kita memutar tampilan 20 derajat searah jarum jam. (Rotasi dilakukan dengan menggunakan transformasi. Lihat dokumentasi dan contoh tampilan dan layer ini untuk informasi lebih lanjut.)
Frame
origin = (20, 52) // These are just rough estimates.
width = 118
height = 187
Bounds
origin = (0, 0)
width = 80
height = 130
Anda dapat melihat bahwa batas-batasnya masih sama. Mereka masih tidak tahu apa yang telah terjadi! Nilai bingkai semuanya berubah, meskipun.
Sekarang sedikit lebih mudah untuk melihat perbedaan antara frame dan batas, bukan? Artikel yang Anda Mungkin Tidak Pahami bingkai dan batasan mendefinisikan bingkai tampilan sebagai
... kotak pembatas terkecil dari pandangan itu sehubungan dengan sistem koordinat orang tua itu, termasuk segala transformasi yang diterapkan pada pandangan itu.
Penting untuk dicatat bahwa jika Anda mengubah tampilan, maka bingkai menjadi tidak terdefinisi. Jadi sebenarnya, bingkai kuning yang saya gambar di sekitar batas hijau yang diputar pada gambar di atas tidak pernah benar-benar ada. Itu berarti jika Anda memutar, skala atau melakukan beberapa transformasi lain maka Anda tidak harus menggunakan nilai bingkai lagi. Anda masih dapat menggunakan nilai batas. Dokumentasi Apple memperingatkan:
Penting: Jika properti tampilan
transform
tidak mengandung transformasi identitas, kerangka tampilan tersebut tidak terdefinisi dan demikian juga hasil dari perilaku autoresizing-nya.
Agak disayangkan tentang autoresizing .... Ada sesuatu yang bisa Anda lakukan.
Saat memodifikasi
transform
properti tampilan Anda, semua transformasi dilakukan relatif terhadap titik tengah tampilan.
Jadi jika Anda perlu memindahkan tampilan di dalam induk setelah transformasi dilakukan, Anda dapat melakukannya dengan mengubah view.center
koordinat. Seperti frame
, center
menggunakan sistem koordinat tampilan induk.
Ok, mari singkirkan rotasi kita dan fokus pada batas. Sejauh ini batas asal selalu tetap di (0, 0). Tapi itu tidak harus. Bagaimana jika tampilan kita memiliki subview besar yang terlalu besar untuk ditampilkan sekaligus? Kami akan membuatnya UIImageView
dengan gambar besar. Ini adalah gambar kedua kami dari atas lagi, tetapi kali ini kita dapat melihat seperti apa keseluruhan tampilan subview kita.
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (0, 0)
width = 80
height = 130
Hanya sudut kiri atas gambar yang dapat masuk ke dalam batas tampilan. Sekarang lihat apa yang terjadi jika kita mengubah koordinat asal batas.
Frame
origin = (40, 60)
width = 80
height = 130
Bounds
origin = (280, 70)
width = 80
height = 130
Bingkai belum bergerak di superview tetapi konten di dalam bingkai telah berubah karena asal dari batas persegi dimulai pada bagian tampilan yang berbeda. Ini adalah keseluruhan ide di balik a UIScrollView
dan itu subclass (misalnya, a UITableView
). Lihat Memahami UIScrollView untuk penjelasan lebih lanjut.
Karena frame
mengaitkan lokasi tampilan dalam tampilan induknya, Anda menggunakannya saat Anda membuat perubahan luar , seperti mengubah lebarnya atau menemukan jarak antara tampilan dan bagian atas tampilan induknya.
Gunakan bounds
saat Anda membuat perubahan ke dalam , seperti menggambar sesuatu atau mengatur tampilan bawah dalam tampilan. Gunakan juga batas untuk mendapatkan ukuran tampilan jika Anda telah melakukan beberapa transfomasi.
Dokumen Apple
Pertanyaan StackOverflow terkait
Sumber daya lainnya
Selain membaca artikel di atas, sangat membantu saya untuk membuat aplikasi pengujian. Anda mungkin ingin mencoba melakukan hal serupa. (Saya mendapat ide dari kursus video ini tapi sayangnya itu tidak gratis.)
Berikut ini kode untuk referensi Anda:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myView: UIView!
// Labels
@IBOutlet weak var frameX: UILabel!
@IBOutlet weak var frameY: UILabel!
@IBOutlet weak var frameWidth: UILabel!
@IBOutlet weak var frameHeight: UILabel!
@IBOutlet weak var boundsX: UILabel!
@IBOutlet weak var boundsY: UILabel!
@IBOutlet weak var boundsWidth: UILabel!
@IBOutlet weak var boundsHeight: UILabel!
@IBOutlet weak var centerX: UILabel!
@IBOutlet weak var centerY: UILabel!
@IBOutlet weak var rotation: UILabel!
// Sliders
@IBOutlet weak var frameXSlider: UISlider!
@IBOutlet weak var frameYSlider: UISlider!
@IBOutlet weak var frameWidthSlider: UISlider!
@IBOutlet weak var frameHeightSlider: UISlider!
@IBOutlet weak var boundsXSlider: UISlider!
@IBOutlet weak var boundsYSlider: UISlider!
@IBOutlet weak var boundsWidthSlider: UISlider!
@IBOutlet weak var boundsHeightSlider: UISlider!
@IBOutlet weak var centerXSlider: UISlider!
@IBOutlet weak var centerYSlider: UISlider!
@IBOutlet weak var rotationSlider: UISlider!
// Slider actions
@IBAction func frameXSliderChanged(sender: AnyObject) {
myView.frame.origin.x = CGFloat(frameXSlider.value)
updateLabels()
}
@IBAction func frameYSliderChanged(sender: AnyObject) {
myView.frame.origin.y = CGFloat(frameYSlider.value)
updateLabels()
}
@IBAction func frameWidthSliderChanged(sender: AnyObject) {
myView.frame.size.width = CGFloat(frameWidthSlider.value)
updateLabels()
}
@IBAction func frameHeightSliderChanged(sender: AnyObject) {
myView.frame.size.height = CGFloat(frameHeightSlider.value)
updateLabels()
}
@IBAction func boundsXSliderChanged(sender: AnyObject) {
myView.bounds.origin.x = CGFloat(boundsXSlider.value)
updateLabels()
}
@IBAction func boundsYSliderChanged(sender: AnyObject) {
myView.bounds.origin.y = CGFloat(boundsYSlider.value)
updateLabels()
}
@IBAction func boundsWidthSliderChanged(sender: AnyObject) {
myView.bounds.size.width = CGFloat(boundsWidthSlider.value)
updateLabels()
}
@IBAction func boundsHeightSliderChanged(sender: AnyObject) {
myView.bounds.size.height = CGFloat(boundsHeightSlider.value)
updateLabels()
}
@IBAction func centerXSliderChanged(sender: AnyObject) {
myView.center.x = CGFloat(centerXSlider.value)
updateLabels()
}
@IBAction func centerYSliderChanged(sender: AnyObject) {
myView.center.y = CGFloat(centerYSlider.value)
updateLabels()
}
@IBAction func rotationSliderChanged(sender: AnyObject) {
let rotation = CGAffineTransform(rotationAngle: CGFloat(rotationSlider.value))
myView.transform = rotation
updateLabels()
}
private func updateLabels() {
frameX.text = "frame x = \(Int(myView.frame.origin.x))"
frameY.text = "frame y = \(Int(myView.frame.origin.y))"
frameWidth.text = "frame width = \(Int(myView.frame.width))"
frameHeight.text = "frame height = \(Int(myView.frame.height))"
boundsX.text = "bounds x = \(Int(myView.bounds.origin.x))"
boundsY.text = "bounds y = \(Int(myView.bounds.origin.y))"
boundsWidth.text = "bounds width = \(Int(myView.bounds.width))"
boundsHeight.text = "bounds height = \(Int(myView.bounds.height))"
centerX.text = "center x = \(Int(myView.center.x))"
centerY.text = "center y = \(Int(myView.center.y))"
rotation.text = "rotation = \((rotationSlider.value))"
}
}
bounds
vs frame
tampilan.
coba jalankan kode di bawah ini
- (void)viewDidLoad {
[super viewDidLoad];
UIWindow *w = [[UIApplication sharedApplication] keyWindow];
UIView *v = [w.subviews objectAtIndex:0];
NSLog(@"%@", NSStringFromCGRect(v.frame));
NSLog(@"%@", NSStringFromCGRect(v.bounds));
}
output dari kode ini adalah:
orientasi case case adalah Portrait
{{0, 0}, {768, 1024}}
{{0, 0}, {768, 1024}}
orientasi case case adalah Landscape
{{0, 0}, {768, 1024}}
{{0, 0}, {1024, 768}}
jelas, Anda dapat melihat perbedaan antara frame dan batas
bingkai adalah asal (sudut kiri atas) dan ukuran tampilan dalam sistem koordinat tampilan supernya, ini berarti bahwa Anda menerjemahkan tampilan dalam tampilan supernya dengan mengubah asal bingkai, batas di sisi lain adalah ukuran dan asal dalam memiliki sistem koordinat, jadi secara default asal batasnya adalah (0,0).
sebagian besar waktu bingkai dan batas adalah kongruen, tetapi jika Anda memiliki tampilan bingkai ((140,65), (200.250)) dan batas ((0,0), (200.250)) misalnya dan tampilan miring sehingga berdiri di sudut kanan bawahnya, maka batas-batasnya akan tetap ((0,0), (200.250)), tetapi bingkainya tidak.
bingkai akan menjadi kotak terkecil yang merangkum / mengelilingi tampilan, sehingga bingkai (seperti dalam foto) akan menjadi ((140,65), (320,320)).
Perbedaan lain adalah misalnya jika Anda memiliki superView yang batasnya adalah ((0,0), (200.200)) dan superView ini memiliki subView yang bingkainya ((20,20), (100.100)) dan Anda mengubah batas superView. ke ((20.20), (200.200)), maka frame subView akan tetap ((20.20), (100.100)) tetapi diimbangi oleh (20.20) karena sistem koordinat superview diimbangi oleh (20, 20).
Saya harap ini membantu seseorang.
subView
frame relatif terhadapnya superView
. mengubah superView
batas menjadi apa pun tidak akan membuat subView
asal bertepatan dengan itu superView
.
Biarkan saya menambahkan 5 sen saya.
Bingkai digunakan oleh tampilan induk tampilan untuk menempatkannya di dalam tampilan induk.
Bound digunakan oleh tampilan itu sendiri untuk menempatkan kontennya sendiri (seperti tampilan gulir saat menggulir). Lihat juga clipsToBounds . Batas juga dapat digunakan untuk memperbesar / memperkecil konten tampilan.
Analogi:
Bingkai ~
Batas layar TV ~ Kamera (memperbesar, memindahkan, memutar)
Jawaban di atas sangat menjelaskan perbedaan antara Bounds dan Frames.
Batas: Ukuran tampilan dan lokasi sesuai sistem koordinatnya sendiri.
Bingkai: Ukuran tampilan dan Lokasi relatif terhadap SuperView-nya.
Lalu ada kebingungan bahwa dalam kasus Bounds the X, Y akan selalu menjadi "0". Ini tidak benar . Ini dapat dipahami di UIScrollView dan UICollectionView juga.
Ketika batas x, y bukan 0.
Mari kita asumsikan kita memiliki UIScrollView. Kami telah menerapkan pagination. UIScrollView memiliki 3 halaman dan lebar ContentSize-nya adalah tiga kali Lebar Layar (anggap ScreenWidth adalah 320). Ketinggiannya konstan (anggap 200).
scrollView.contentSize = CGSize(x:320*3, y : 200)
Tambahkan tiga UIImageViews sebagai subViews dan perhatikan dengan cermat nilai x frame
let imageView0 = UIImageView.init(frame: CGRect(x:0, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView1 : UIImageView.init( frame: CGRect(x:320, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
let imageView2 : UIImageView.init(frame: CGRect(x:640, y: 0 , width : scrollView.frame.size.width, height : scrollView.frame.size.height))
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
scrollView.addSubview(imageView0)
Halaman 0: Ketika ScrollView berada pada 0 Halaman maka Batas akan menjadi (x: 0, y: 0, lebar: 320, tinggi: 200)
Halaman 1: Gulir dan pindah ke Halaman 1.
Sekarang batasnya adalah (x: 320, y: 0, lebar: 320, tinggi: 200)
Ingat kami katakan sehubungan dengan Sistem koordinatnya sendiri. Jadi sekarang "Bagian yang Terlihat" dari ScrollView kami memiliki "x" -nya di 320. Lihatlah bingkai dari imageView1.
Sama untuk kasus UICollectionView. Cara termudah untuk melihat collectionView adalah dengan menggulirnya dan mencetak / mencatat batas-batasnya dan Anda akan mendapatkan idenya.
Semua jawaban di atas benar dan ini adalah pendapat saya tentang ini:
Untuk membedakan antara frame dan batas, pengembang CONCEPTS harus membaca:
- relatif terhadap superview (satu tampilan induk) itu terkandung dalam = FRAME
- relatif terhadap sistem koordinatnya sendiri, menentukan lokasi subview = BOUNDS
"batas" membingungkan karena memberi kesan bahwa koordinat adalah posisi tampilan yang ditetapkan. Tapi ini dalam hubungan dan disesuaikan sesuai dengan konstanta bingkai.
Frame vs terikat
frame = lokasi dan ukuran tampilan menggunakan sistem koordinat tampilan induk
bounds = lokasi dan ukuran tampilan menggunakan sistem koordinatnya sendiri
Tampilan melacak ukuran dan lokasinya menggunakan dua persegi panjang: persegi panjang bingkai dan persegi panjang batas. Bingkai persegi menentukan lokasi dan ukuran tampilan di superview menggunakan sistem koordinat superview. Bound rectangle mendefinisikan sistem koordinat interior yang digunakan saat menggambar konten tampilan, termasuk asal dan penskalaan. Gambar 2-1 menunjukkan hubungan antara bingkai persegi panjang, di sebelah kiri, dan persegi panjang batas, di sebelah kanan. "
Singkatnya, bingkai adalah gagasan pandangan superview, dan batas adalah gagasan pandangan sendiri. Memiliki beberapa sistem koordinat, satu untuk setiap tampilan, adalah bagian dari hierarki tampilan.