UIView alt sınıfı için Uç yüklemenin doğru yolu


98

Bu sorunun daha önce sorulduğunun farkındayım ama cevaplar çelişkili ve kafam karışık, bu yüzden lütfen beni alevlendirmeyin.

UIViewUygulamamda yeniden kullanılabilir bir alt sınıfa sahip olmak istiyorum . Arayüzü bir uç dosyası kullanarak tanımlamak istiyorum.

Şimdi bunun, içinde bir etkinlik göstergesi olan bir yükleme göstergesi görünümü olduğunu varsayalım. Bir olayda bu görünümü somutlaştırmak ve bir görünüm denetleyicisinin görünümüne canlandırmak istiyorum. Görünümün arayüzünü programatik olarak, öğeleri programlı olarak oluşturarak ve çerçevelerini bir init yönteminin içine yerleştirerek vb.

Bunu bir uç kullanarak nasıl yapabilirim? Arayüz oluşturucuda verilen boyutu çerçeve ayarlamanıza gerek kalmadan korumak.

Bunu böyle yapmayı başardım, ama eminim yanlıştır (sadece içinde seçici olan bir manzara):

 - (id)initWithDataSource:(NSDictionary *)dataSource {
        self = [super init];
        if (self){
            self = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@", [self class]] owner:self options:nil] objectAtIndex:0];
            self.pickerViewData = dataSource;
            [self configurePickerView];
        }
        return self;
    }

Ama kendimin üzerine yazıyorum ve bunu örneklediğimde:

FSASelectView *selectView = [[FSASelectView alloc] initWithDataSource:selectViewDictionary];
    selectView.delegate = self;

    selectView.frame = CGRectMake(0, self.view.bottom + 50, [FSASelectView width], [FSASelectView height]);

Çerçeveyi IB'den almak yerine manuel olarak ayarlamam gerekiyor.

DÜZENLE: Bu özel görünümü bir görünüm denetleyicisinde oluşturmak ve görünümün öğelerini kontrol etmek için erişim sahibi olmak istiyorum. Yeni bir görüntü denetleyicisi istemiyorum.

Teşekkürler

DÜZENLEME: Bunun en iyi uygulama olup olmadığını bilmiyorum, eminim öyle değil, ama ben böyle yaptım:

FSASelectView *selectView = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@",[FSASelectView class]] owner:self options:nil] objectAtIndex:0];
    selectView.delegate = self;
    [selectView configurePickerViewWithData:ds];
    selectView.frame = CGRectMake(0, self.view.bottom + 50, selectView.width, selectView.height);
    selectView.alpha = 0.9;
    [self.view addSubview:selectView];
    [UIView animateWithDuration: 0.25 delay: 0 options:UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionCurveEaseInOut animations:^{
                            selectView.frame = CGRectMake(0, self.view.bottom - selectView.height, selectView.width, selectView.height);
                            selectView.alpha = 1;
                        } completion:^(BOOL finished) {
                        }];

Doğru uygulama hala isteniyor

Bu, uç adı ile bir görünüm denetleyicisi ve init kullanılarak mı yapılmalıydı? UIView başlatma yönteminde uçları kodda ayarlamalı mıyım? Yoksa yaptığım şey tamam mı?


Ancak ilk kod satırı, orijinal soruda kullandığınızla neredeyse aynı değil initWithDataSourcemi? Her neyse, sahibine 'Nil' olarak verseniz bile bu çalışacaktır.
Rakesh

Bugünlerde vakaların% 99,99999'unda, yalnızca artık her yerde ve her zaman iOS'ta kullanılan konteyner görünümlerini kullanacağımızı belirtmek gerekir . nasıl yapılır makale
Fattie

[NSString stringWithFormat: @ "% @", [FSASelectView class]] 'ı NSStringFromClass ([FSASelectView class]) ile değiştirebileceğinizi unutmayın
naomimichiko

Yanıtlar:


132
MyViewClass *myViewObject = [[[NSBundle mainBundle] loadNibNamed:@"MyViewClassNib" owner:self options:nil] objectAtIndex:0]

Sahip olduğum yeniden kullanılabilir özel görünümleri başlatmak için bunu kullanıyorum.


Buradaki "firstObject" i kullanabileceğinizi unutmayın, biraz daha temiz. "firstObject", NSArray ve NSMutableArray için kullanışlı bir yöntemdir.

Tablo başlığı olarak kullanmak üzere bir xib yüklemenin tipik bir örneğini burada bulabilirsiniz. YourClass.m dosyanızda

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    return [[NSBundle mainBundle] loadNibNamed:@"TopArea" owner:self options:nil].firstObject;
}

Normalde, içinde TopArea.xibDosya Sahibi'ne tıklar ve dosya sahibini Sınıfınız olarak ayarlarsınız . Daha sonra aslında YourClass.h dosyasında IBOutlet özelliklerine sahip olursunuz. İçinde TopArea.xib, kontrolleri bu çıkışlara sürükleyebilirsiniz.

Unutmayın , görünümün kendisine tıklayıp bunu bir prize sürüklemeniz TopArea.xibgerekebilir , böylece gerekirse kontrol edebilirsiniz. (Çok değerli bir ipucu, bunu tablo hücre satırları için yaptığınızda kesinlikle bunu yapmanız gerekir - görünümün kendisini kodunuzdaki ilgili özelliğe bağlamanız gerekir.)


initWithNibName: bir UIView yöntemi yoktur, bu sadece UIViewController içindir
meronix

Bu bir görünüm denetleyicisi için, bir UIView kullanmak ve ona sahip olmak için onu oluşturan denetleyiciye sahip olmak istiyorum.
Adam Waite

Üzgünüm, acelemle yanlış kodu kopyaladım, cevabı güncelledim, lütfen bir göz atın.
Premsuraj

Bunu doğru olarak işaretleyeceğim. En iyi uygulama mı bilmiyorum ama işe yarıyor.
Adam Waite

@Joe Blow Tümsek için özür dilerim, ama neden bunu yapmak zorundasın: "Unutma ki, TopArea.xib'de Görünüm'ün kendisine tıklayıp bunu bir prize sürüklemen gerekebilir, böylece kontrolü sizde tutarsınız , Eğer gerekliyse." Bunun yerine, bir UIView kendisi olduğu için alttaki görünüme erişmek için myViewObject'i kullanabilir misiniz? Neden bir mülke ihtiyaç var?
user1686342

39

Kendinizi CustomViewve ondan xibbağımsız tutmak istiyorsanız File's Owner, şu adımları izleyin

  • File's OwnerAlanı boş bırakın .
  • Gerçek görünümünde tıklayın xibaramalarınızdan dosyası CustomViewve amaçlarını set Custom Classolarak CustomView(özel görünüm sınıfı adına)
  • Ekle IBOutletyılında .hözel bakış dosyası.
  • Gelen .xibözel bakış dosyada, bakış tıklayın ve içeri Connection Inspector. Burada .hdosyada tanımladığınız tüm IBOutlet'lerinizi bulacaksınız
  • Onları kendi görünümleriyle birleştirin.

içinde .msenin içinde dosyanın CustomViewsınıfı geçersiz initaşağıdaki gibi bir yöntem

-(CustomView *) init{
    CustomView *result = nil;
    NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner:self options: nil];
    for (id anObject in elements)
    {
        if ([anObject isKindOfClass:[self class]])
        {
            result = anObject;
            break;
        }
    }
    return result;
}

Şimdi, dosyanızı yüklemek istediğinizde CustomView, aşağıdaki kod satırını kullanın [[CustomView alloc] init];


1
İOS 9 itibariyle, bu sayfada listelenen ve aslında benim için çalışan tek çözüm budur (diğer çözümler çökmeye neden olur).
lifjoy

Bu yaklaşımın, arama yapmadığı için mevcut bir XIB'den özel görünümünüzü yüklemeyle uyumlu olmadığını unutmayın -init. Bunun yerine initWithFrame:ve arayacak -awakeFromNib. Görünümünüzü yüklemek için -inityöntemdeki ile aynı kodu yazarsanız, sonsuz bir döngüye girersiniz.
Dustt

25

Aşağıdaki adımları izleyin

  1. MyView .h / .m türünde bir sınıf oluşturun UIView.
  2. Aynı adda bir xib oluşturun MyView.xib.
  3. Şimdi Dosya Sahibi sınıfını değiştirmek UIViewControllergelen NSObjectxib. Aşağıdaki resme bakın görüntü açıklamasını buraya girin
  4. Dosya Sahibi Görünümünü Görünümünüze bağlayın. Aşağıdaki resme bakın görüntü açıklamasını buraya girin

  5. Görünümünüzün sınıfını olarak değiştirin MyView. 3 ile aynı.

  6. Yer kontrolleri IBOutlet'leri oluşturur.

Görünümü yüklemek için gereken kod:

UIViewController *controller=[[UIViewController alloc] initWithNibName:@"MyView" bundle:nil];
MyView* view=(MyView*)controller.view;
[self.view addSubview:myview];

Umarım yardımcı olur.

Açıklama :

UIViewControllerxib'inizi ve UIViewControllersahip olduğunuz Görünümü MyView, MyView xib'de atadığınız görünümü yüklemek için kullanılır.

Demo Burada bir demo çekimi yaptım


İnsanlar neden buna oy veriyor, yanlış? Henüz denemedim ama bu istediğimi yapmayacak gibi görünüyor.
Adam Waite

Olumsuz oy verdim, ancak bu, cevabınızı yanlış okumama neden oldu (dosyanın sahibi olarak bir UIView alt sınıfı atadığınızı düşündüm). Bunun için üzgünüm.
Rakesh 13

2
Xib dosyanızda dosya sahibini yok sayın. Daha sonra, sadece bir MyView * view = [[UINib instantiateWithOwner: nil options: nil] lastObject] yaparak bir UIViewController oluşturmak zorunda kalmayabilirsiniz;
LightningStryk

1
@LightningStryk Evet elbette, ama bunu yapmanın başka bir yolu.
iphonic

1
Bu, yeniden kullanılabilir bir UIView alt sınıfı oluşturmaktır, bu amaçla bir UIViewController kullanmamaktır.
arielyz

11

2 yıl sonra falan kendi sorumu burada cevaplıyorum ama ...

Bir protokol uzantısı kullanır, böylece bunu tüm sınıflar için fazladan kod olmadan yapabilirsiniz.

/*

Prerequisites
-------------
- In IB set the view's class to the type hook up any IBOutlets
- In IB ensure the file's owner is blank

*/

public protocol CreatedFromNib {
    static func createFromNib() -> Self?
    static func nibName() -> String?
}

extension UIView: CreatedFromNib { }

public extension CreatedFromNib where Self: UIView {

    public static func createFromNib() -> Self? {
        guard let nibName = nibName() else { return nil }
        guard let view = NSBundle.mainBundle().loadNibNamed(nibName, owner: nil, options: nil).last as? Self else { return nil }
        return view
    }

    public static func nibName() -> String? {
        guard let n = NSStringFromClass(Self.self).componentsSeparatedByString(".").last else { return nil }
        return n
    }
}

// Usage:
let myView = MyView().createFromNib()

8

Swift'de:

Örneğin, özel sınıfınızın adı InfoView

İlk başta dosya oluşturursunuz InfoView.xibve InfoView.swiftşöyle:

import Foundation
import UIKit

class InfoView: UIView {
    class func instanceFromNib() -> UIView {
    return UINib(nibName: "InfoView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UIView
}

Sonra File's Ownerbunu UIViewControllerbeğenmeye ayarlayın :

görüntü açıklamasını buraya girin

Öğenizi şu Viewşekilde yeniden adlandırın InfoView:

görüntü açıklamasını buraya girin

Sağ tıklayarak File's Ownerve bağlantı viewinizle alanı InfoView:

görüntü açıklamasını buraya girin

Sınıf adının şu olduğundan emin olun InfoView:

görüntü açıklamasını buraya girin

Ve bundan sonra, herhangi bir sorun yaşamadan özel sınıfınızdaki düğmeye eylemi ekleyebilirsiniz:

görüntü açıklamasını buraya girin

Ve bu özel sınıfın kullanımınız MainViewController:

func someMethod() {
    var v = InfoView.instanceFromNib()
    v.frame = self.view.bounds
    self.view.addSubview(v)
}

2

Ya xib'i bir görünüm denetleyicisi kullanarak başlatabilir ve viewController.view'u kullanabilirsiniz. ya da yaptığınız gibi yapın. Yalnızca UIViewdenetleyici olarak bir alt sınıf yapmak UIViewkötü bir fikirdir.

Özel görünümünüzden herhangi bir çıkışınız yoksa, UIViewControlleronu başlatmak için doğrudan bir sınıfı kullanabilirsiniz .

Güncelleme: Sizin durumunuzda:

UIViewController *genericViewCon = [[UIViewController alloc] initWithNibName:@"CustomView"];
//Assuming you have a reference for the activity indicator in your custom view class
CustomView *myView = (CustomView *)genericViewCon.view;
[parentView addSubview:myView];
//And when necessary
[myView.activityIndicator startAnimating]; //or stop

Aksi takdirde, bir özel UIViewControlleryapmanız gerekir (çıkışların uygun şekilde kablolanması için dosyanın sahibi yapmak için).

YourCustomController *yCustCon = [[YourCustomController alloc] initWithNibName:@"YourXibName"].

Görünümü eklemek istediğiniz her yerde kullanabilirsiniz.

[parentView addSubview:yCustCon.view];

Ancak, xib yüklenirken başka bir görünüm denetleyicisini (zaten başka bir görünüm için kullanılıyor) sahip olarak geçirmek, denetleyicinin görünüm özelliği değişeceğinden ve orijinal görünüme erişmek istediğinizde, iyi bir fikir değildir. ona bir referans var.

DÜZENLEME: Yeni xib'inizi dosyanın sahibiyle aynı ana UIViewControllersınıfla kurduysanız ve view özelliğini yeni xib görünümüne bağladıysanız, bu sorunla karşılaşacaksınız .

yani;

  • YourMainViewController - yönetir - mainView
  • CustomView - gerektiği zaman xib'den yüklenmesi gerekir.

Aşağıdaki kod, içeriden yazarsanız, daha sonra kafa karışıklığına neden olacaktır YourMainViewController. Bunun nedeni self.view, bu noktadan itibaren özel görünümünüze

-(void)viewDidLoad:(){
  UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0];
}

Tamam, temel olarak, en iyi uygulama her UIView'ın ilişkili bir denetleyiciye sahip olması gerektiğini belirtir.
Adam Waite

Şart değil. Ancak ayrı bir xib'den bir görünüm yüklerken, bu sorunsuz bir yol olacaktır. IOS5 öncesi apple docs, bir görünüm denetleyicisinin birden fazla sahneyi (xib) kontrol etmek için kullanılmaması gerektiğini ve bunun tersini söylüyor.
Rakesh

Xib aslında yalnızca bir uyarı görünümünün boyutudur
Adam Waite

Ben şahsen yeni bir denetleyici yaratmanız gerekip gerekmediğinin kod / xib'in karmaşıklığına bağlı olduğunu düşünüyorum. Ortaya çıkan sorunları başarıyla çözebilirseniz ve gelecekteki değişikliklere bakmıyorsanız, bu bir sorun değildir. Ancak ayrı bir görüntü denetleyicisi oluşturmak sizin için pek çok sorunu çözecektir. Ve sizin durumunuzda sadece bir aktivite göstergesi olduğundan, bir UIViewController (cevapta belirtildiği gibi) nesnesini kolayca kullanabilirsiniz. Bunu uygun şekilde yapmak için cevabımı güncelleyeceğim.
Rakesh

1
Böylece arayüzü programlı yapmaktansa arayüz oluşturucuda çizebilirim. Bunu programlı olarak yapmak sorun değil, sadece bir UIView alt sınıfını nasıl alacağımı ve ona bir uç vermeyi öğrenmek istiyorum! Bu zor olmamalı.
Adam Waite
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.