Apakah ada acara pengubahan ukuran UIView?


102

Saya memiliki tampilan yang memiliki baris dan kolom tampilan gambar di dalamnya.

Jika tampilan ini diubah ukurannya, saya perlu mengatur ulang posisi tampilan gambar.

Tampilan ini adalah subview dari tampilan lain yang ukurannya diubah.

Adakah cara untuk mendeteksi kapan tampilan ini diubah ukurannya?


Kapan tampilan diubah ukurannya? Saat perangkat berputar, atau saat pengguna memutarnya menggunakan multi-touch?
Evan Mulawski

Tampilan ini diubah ukurannya saat pengguna mengetuk tombol di tampilan lain (tampilan master).
cinta langsung

Jawaban:


98

Seperti komentar Uli di bawah ini, cara yang tepat untuk melakukannya adalah menimpa layoutSubviewsdan mengatur tata letak imageView di sana.

Jika, karena alasan tertentu, Anda tidak dapat membuat subkelas dan menimpa layoutSubviews, observasi boundsseharusnya berhasil, bahkan saat menjadi agak kotor. Lebih buruk lagi, ada risiko dengan mengamati - Apple tidak menjamin KVO berfungsi pada kelas UIKit. Baca diskusi dengan teknisi Apple di sini: Kapan objek terkait dirilis?

jawaban asli:

Anda dapat menggunakan pengamatan nilai kunci:

[yourView addObserver:self forKeyPath:@"bounds" options:0 context:nil];

dan terapkan:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == yourView && [keyPath isEqualToString:@"bounds"]) {
        // do your stuff, or better schedule to run later using performSelector:withObject:afterDuration:
    }
}

2
Sungguh, meletakkan subviews adalah untuk apa layoutSubviews, jadi mengamati perubahan ukuran, sementara fungsional, adalah desain yang buruk IMO.
uliwitness

@uliwitness dengan baik mereka terus mengubah hal ini. Bagaimanapun, sekarang Anda harus menggunakan viewWillTransitiondll dll.
Dan Rosenstark

viewWillTransition untuk UIViewControllers, bukan UIView.
Armand

Apakah layoutSubviewsmasih menggunakan metode yang disarankan jika, bergantung pada ukuran tampilan saat ini, sub-tampilan yang berbeda perlu ditambahkan / dihapus dan batasan yang berbeda perlu ditambahkan / dihilangkan?
Chris Prince

1
Yah, saya pikir saya baru saja menjawab ini untuk kasus saya. Ketika saya melakukan pengaturan (tergantung pada ukuran) yang saya butuhkan dalam menimpa batas, itu berhasil. Ketika saya melakukannya di layoutSubviews, itu tidak.
Chris Prince

93

Dalam UIViewsubclass, pengamat properti dapat digunakan:

override var bounds: CGRect {
    didSet {
        // ...
    }
}

Tanpa subclassing, kunci-nilai observasi dengan kunci-jalur pintar akan melakukan:

var boundsObservation: NSKeyValueObservation?

func beginObservingBounds() {
    boundsObservation = observe(\.bounds) { capturedSelf, _ in
        // ...
    }
}

2
@Ali Mengapa Anda berpikir begitu?
Rudolf Adamkovič

pada perubahan bingkai beban tetapi sepertinya batas tidak (saya tahu itu aneh dan mungkin saya dong ada yang salah)
Ali

3
@ Ali: seperti yang telah disebutkan beberapa kali di sini: frameadalah properti hitung turunan dan runtime. jangan menimpa ini, kecuali Anda memiliki alasan yang sangat cerdas dan mengetahui untuk melakukannya. sebaliknya: gunakan boundsatau (bahkan lebih baik) layoutSubviews.
auco

3
Cara yang bagus untuk membuat lingkaran. Terima kasih! override var bounds: CGRect { didSet { layer.cornerRadius = bounds.size.width / 2 }}
SimplGy

4
Mendeteksi boundsatau framemengubah tidak dijamin akan berhasil, bergantung pada tempat Anda meletakkan tampilan dalam hierarki tampilan. Saya akan menggantinya layoutSubviews. Lihat ini dan ini jawaban.
HuaTham

31

Buat subclass UIView, dan timpa layoutSubviews


11
Masalahnya adalah bahwa subview tidak hanya dapat mengubah ukurannya, tetapi juga dapat menganimasikan perubahan ukuran tersebut. Saat UIView menjalankan animasi, UIView tidak memanggil layoutSubviews setiap saat.
David Jeske

1
@DavidJeske UIViewAnimationOptions memiliki opsi yang disebut UIViewAnimationOptionLayoutSubviews. Saya pikir ini mungkin membantu. :)
Neal.Marlin

8

Swift 4 keypath KVO - Ini adalah cara saya mendeteksi rotasi otomatis dan berpindah ke panel samping iPad. Harus bekerja bekerja tampilan apa pun. Harus mengamati lapisan UIView.

private var observer: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()
    observer = view.layer.observe(\.bounds) { object, _ in
        print(object.bounds)
    }
    // ...
}
override func viewWillDisappear(_ animated: Bool) {
    observer?.invalidate()
    //...
}

Solusi ini berhasil untuk saya. Saya baru mengenal Swift dan saya tidak yakin dengan sintaks \ .bounds yang Anda gunakan di sini. Apa sebenarnya maksudnya itu?
WBuck


.layerberhasil! Tahukah Anda mengapa penggunaan view.observetidak berhasil?
d4Rk

@ d4Rk - Saya tidak tahu. Dugaan saya adalah bahwa batas lapisan kurang ambigu dibandingkan bingkai UIView. Misalnya, contentOffset UITableView akan memengaruhi koordinat akhir subView-nya. Tidak yakin.
Warren Stringer

INI ADALAH JAWABAN BESAR BAGI SAYA. Kasus saya adalah saya menggunakan AutoLayout untuk meletakkan tampilan saya. TAPI UICollectionViewFlowLayout memaksa Anda untuk memberikan itemSize eksplisit. tetapi ketika ukuran collectionView Anda berubah (seperti dalam kasus saya ketika saya menampilkan toolbar dengan AutoLayout) - itemSize tetap sama dan throws = [pengamat adalah pendekatan terbersih yang saya dapatkan sejauh ini. dan yang terbaik !!
Yitzchak

7

Anda dapat membuat subclass UIView dan mengganti

setFrame: (CGRect) bingkai

metode. Ini adalah metode yang dipanggil saat bingkai (yaitu ukuran) tampilan diubah. Lakukan sesuatu seperti ini:

- (void) setFrame:(CGRect)frame
{
  // Call the parent class to move the view
  [super setFrame:frame];

  // Do your custom code here.
}

Masuk akal dan fungsional. Panggilan yang bagus.
El Zorko

1
FYI Ini tidak bekerja untuk subclass UIImageView. Info lebih lanjut di sini: stackoverflow.com/questions/19216684/…
William Entriken

Juga, setFrame:tidak dipanggil pada UITextViewsubkelas saya selama pengubahan ukuran yang disebabkan oleh rotasi otomatis layoutSubviews:. Catatan: Saya menggunakan tata letak otomatis & iOS 7.0.
ma11hew28

3
tidak pernah menimpa setFrame:. frameadalah properti turunan. Lihat jawaban saya
Adlai Holler

7

Cukup tua tapi masih pertanyaan yang bagus. Dalam kode sampel Apple, dan di beberapa subkelas UIView pribadi mereka, mereka mengganti setBounds secara kasar seperti:

-(void)setBounds:(CGRect)newBounds {
    BOOL const isResize = !CGSizeEqualToSize(newBounds.size, self.bounds.size);
    if (isResize) [self prepareToResizeTo:newBounds.size]; // probably saves 
    [super setBounds:newBounds];
    if (isResize) [self recoverFromResizing];
}

Mengganti setFrame:bukanlah ide yang bagus. framediturunkan dari center,, boundsdan transform, jadi iOS tidak akan selalu memanggil setFrame:.


2
Ini benar (dalam teori). Namun, setBounds:tidak dipanggil baik saat menyetel properti frame (setidaknya di iOS 7.1). Ini bisa menjadi optimasi yang ditambahkan Apple untuk menghindari pesan tambahan.
nschum

1
… Dan desain yang buruk di pihak Apple untuk tidak memanggil pengakses mereka sendiri.
osxdirk

1
Faktanya, keduanya frame dan boundsberasal dari tampilan yang mendasari CALayer; mereka hanya memanggil melalui pengambil lapisan. Dan setFrame:mengatur bingkai lapisan, sementara setBounds:mengatur batas lapisan. Jadi, Anda tidak bisa begitu saja menimpa salah satunya. Juga, layoutSubviewsdipanggil secara berlebihan (tidak hanya pada perubahan geometri), jadi ini mungkin juga tidak selalu menjadi pilihan yang baik. Masih mencari ...
big_m

-3

Jika Anda berada dalam instance UIViewController, menimpa viewDidLayoutSubviewsakan berhasil.

override func viewDidLayoutSubviews() {
    // update subviews
}

Ukuran UIView berubah diperlukan, bukan UIViewController.
Gastón Antonio Montes

@ GastónAntonioMontes terima kasih untuk itu! Anda mungkin telah memperhatikan hubungan antara UIViewinstance dan UIViewControllerinstance. Jadi jika Anda memiliki UIViewinstance tanpa VC yang terpasang, jawaban lainnya bagus, tetapi jika Anda kebetulan terhubung ke VC, ini laki-laki Anda. Maaf itu tidak berlaku untuk kasus Anda.
Dan Rosenstark
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.