2013-03-14 21 views
90

Sono consapevole che questa domanda è stata posta prima, ma le risposte sono contraddittorie e io sono confuso, quindi per favore non scottarmi.Modo corretto per caricare un pennino per una sottoclasse UIView

Desidero avere una sottoclasse riutilizzabile UIView nella mia app. Voglio descrivere l'interfaccia usando un file pennino.

Ora diciamo che è una vista di indicatore di caricamento con un indicatore di attività in esso. Vorrei che in alcuni eventi istanziare questa vista e animare la vista di un controller di visualizzazione. Potrei descrivere l'interfaccia della vista senza problemi a livello di codice, creando gli elementi a livello di codice e impostando la loro cornice all'interno di un metodo init ecc.

Come posso farlo usando un pennino però? Mantenimento delle dimensioni fornite nel generatore di interfacce senza dover impostare una cornice.

sono riuscito a fare in questo modo, ma sono sicuro che sia sbagliato (è solo una vista con un selettore in esso):

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

ma io sono sovrascrivere sé, e quando L'ho istanziato:

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

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

Devo impostare manualmente il frame anziché averlo raccolto da IB.

MODIFICA: desidero creare questa vista personalizzata in un controller di visualizzazione e avere accesso per controllare gli elementi della vista. Non voglio un nuovo controller di visualizzazione.

Grazie

EDIT: Non so se questo è delle migliori pratiche, sono sicuro che non lo è, ma questo è come ho fatto:

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

pratica corretta ancora desiderato

Se ciò è stato fatto utilizzando un controller di visualizzazione e init con il nome del pennino? Dovrei aver impostato il pennino in qualche metodo di inizializzazione UIView nel codice? O è quello che ho fatto ok?

+0

Ma non è la prima riga di codice quasi uguale a quella utilizzata nella domanda originale in 'InitWithDataSource'? In ogni caso questo funzionerà anche se si dà il proprietario come 'Nil'. – Rakesh

+0

Vale la pena notare che in questi giorni nel 99,9999% dei casi, si userebbero solo ** view container **, che ora vengono utilizzati ovunque e sempre in iOS. [how-to article] (http://stackoverflow.com/a/23403979/294884) – Fattie

+0

nota a margine che è possibile sostituire [NSString stringWithFormat: @ "% @", [FSASelectView class]] con NSStringFromClass ([Classe FSASelectView]) – naomimichiko

risposta

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

Lo sto utilizzando per inizializzare le visualizzazioni personalizzate riutilizzabili che ho.


noti che è possibile utilizzare "firstObject" alla fine c'è, è un po 'più pulito. "firstObject" è un metodo pratico per NSArray e NSMutableArray.

Ecco un tipico esempio di caricamento di un xib da utilizzare come intestazione di tabella. Nel file YourClass.m

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

Normalmente, nel TopArea.xib, è necessario fare clic sul file e Proprietario impostare il proprietario del file per YourClass. Quindi in realtà in YourClass.h si avranno le proprietà di IBOutlet. In TopArea.xib, è possibile trascinare i controlli su tali prese.

Non dimenticare che in TopArea.xib, potrebbe essere necessario clic sulla vista in sé e trascinare che in una certa presa di corrente, in modo da avere il controllo di essa, se necessario. (Un consiglio molto utile è che quando si sta facendo questo per le righe di celle tabella, è assolutamente avere a che fare -. È necessario collegare la vista stessa alla proprietà rilevanti nel codice)

+0

non c'è initWithNibName: metodo per un UIView, è solo per UIViewController – meronix

+0

Questo è per un controller di visualizzazione, voglio usare un UIView e avere il controller che lo crea per possederlo. –

+0

Mi dispiace, ho copiato il codice sbagliato nella fretta, ho aggiornato la risposta, per favore dai un'occhiata. – Premsuraj

24

seguire i seguenti passi

  1. Creare una classe denominata MyView .h/.m di tipo UIView.
  2. Creare uno xib con lo stesso nome MyView.xib.
  3. Ora modificare la classe Proprietario file su UIViewController da NSObject in xib. Guarda l'immagine sotto enter image description here
  4. Connetti la vista del file proprietario alla tua vista. Vedere l'immagine sotto enter image description here

  5. Modificare la classe della vista su MyView. Come 3.

  6. I controlli di luogo creano IBOutlet.

ecco il codice per caricare il Vista:

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

Speranza che aiuta.

Chiarimento:

UIViewController viene utilizzato per caricare il tuo XI ter e la vista che il UIViewController ha è in realtà MyView cui è stato assegnato nel XI ter MyView ..

Demo ho fatto una demo afferrare here

+0

Perché le persone stanno votando, sbagliato?Non ho ancora provato, ma sembra che non farà quello che voglio. –

+3

Chiunque voti, si prega di postare il motivo .. Grazie .. – iphonic

+0

Avevo down-votato, ma questo perché ho letto male la tua risposta (pensavo che stavi assegnando una sottoclasse UIView come proprietario del file). Mi dispiace per quello – Rakesh

2

Bene, è possibile inizializzare lo xib utilizzando un controller di visualizzazione e utilizzare viewController.view. o fallo nel modo in cui lo hai fatto. Solo creare una sottoclasse UIView come controller per UIView è una cattiva idea.

Se non si dispone di prese dalla visualizzazione personalizzata, è possibile utilizzare direttamente una classe UIViewController per inizializzarla.

Update: Nel tuo caso:

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 

altrimenti si deve fare un UIViewController (per renderlo come proprietario del file in modo che le uscite siano correttamente cablati).

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

Ovunque si desideri aggiungere la vista che è possibile utilizzare.

[parentView addSubview:yCustCon.view]; 

Tuttavia passando l'altro controller della vista (già utilizzato per un altro vista) come il proprietario durante il caricamento del XI ter non è una buona idea, come la proprietà vista del controller sarà cambiato e quando si desidera accedere al vista originale, non avrai un riferimento ad esso.

EDIT: Si dovrà affrontare questo problema se si dispone di configurare il nuovo XI ter con il proprietario del file come lo stesso principale UIViewController classe e legato la proprietà vista la nuova vista XI ter.

i.e;

  • YourMainViewController - gestisce - MainView
  • CustomView - ha bisogno di caricare da XI ter come e quando richiesto.

Il seguente codice causerà confusione in seguito, se lo si scrive all'interno della vista è stato caricato di YourMainViewController. Questo perché self.view da questo punto in poi farà riferimento al CustomView

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

Ok, quindi in pratica, le best practice affermano che ogni UIView dovrebbe avere un controller associato? –

+0

Non necessariamente. Ma quando si carica una vista da uno xib separato, questo sarebbe il modo senza problemi. I documenti Apple pre-IOS5 indicano che un controller di visualizzazione non deve essere utilizzato per controllare più di una scena (xib) e viceversa. – Rakesh

+0

Lo xib è in realtà solo la dimensione di una vista di avviso –

37

Se si desidera mantenere il vostro CustomView e la sua xib indipendente File's Owner, quindi attenersi alla seguente procedura

  • Lasciare il campo File's Owner vuoto.
  • Cliccare sulla vista reale in xib file del CustomView e impostare il Custom Class come CustomView (nome della classe di visualizzazione personalizzato)
  • Aggiungi IBOutlet in .h file della visualizzazione personalizzata.
  • Nel file .xib della visualizzazione personalizzata, fare clic su Visualizza e passare a Connection Inspector. Qui troverai tutti i tuoi IBOutlet che definisci nel file .h
  • Collegali con la rispettiva vista.

in .m di file della classe CustomView, sovrascrivere il metodo init come seguire

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

Ora, quando si desidera caricare il tuo CustomView, utilizzare la seguente riga di codice [[CustomView alloc] init];

+1

A partire da iOS 9, questa è l'unica soluzione elencata su questa pagina che funziona davvero per me (le altre soluzioni causano il crash). – lifjoy

+0

Basta notare che questo approccio non è compatibile con il caricamento della vista personalizzata da un XIB esistente poiché non chiama '-init'. Invece chiamerà 'initWithFrame:' ​​e '-awakeFromNib'. Se scrivi lo stesso codice per caricare la tua vista come nel metodo '-init', entrerai in un ciclo infinito. – Dustt

9

Rispondere mia Proprio domanda su 2 o qualcosa anni più tardi qui ma ...

Usa ap estensione rotocol in modo da poterlo fare senza alcun codice aggiuntivo per tutte le classi.

/* 

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() 
+0

non funziona più in Swift 2.1 –

6

In Swift:

Ad esempio, il nome della classe personalizzata è InfoView

In un primo momento, si creano file InfoView.xib e InfoView.swift come questo:

import Foundation 
import UIKit 

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

Poi set File's Owner a UIViewController come questo:

enter image description here

Rinominare il View-InfoView:

enter image description here

tasto destro del mouse File's Owner e collegare il vostro campo view con il InfoView:

enter image description here

Assicurarsi che il nome della classe è InfoView:

enter image description here

E dopo questo si può aggiungere l'azione al pulsante nella classe personalizzata senza alcun problema:

enter image description here

e l'utilizzo di questa classe personalizzato nel MainViewController:

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