iphone - welcome - xcode ui教學




如何使用用Interface Builder創建的nib文件加載UIView (16)

我試圖做一些細緻的事情,但應該是可能的。 所以這裡對你所有的專家來說都是一個挑戰(這個論壇是你們中許多人的一部分:))。

我創建了一個調查問卷“組件”,我想在NavigationContoller (我的QuestionManagerViewController )上加載它。 “組件”是一個“空的” UIViewController ,它可以根據需要回答的問題加載不同的視圖。

我這樣做的方式是:

  1. 創建Question1View對像作為UIView子類,定義一些IBOutlets
  2. 創建(使用Interface Builder) Question1View.xib (這裡是我的問題可能是 )。 我將UIViewControllerUIView都設置為Question1View類。
  3. 我將插座與視圖的組件(使用IB)相鏈接。
  4. 我重寫了我的QuestionManagerViewControllerinitWithNib ,如下所示:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
            // Custom initialization
        }
        return self;
    }
    

當我運行代碼時,出現此錯誤:

2009-05-14 15:05:37.152 iMobiDines [17148:20b] ***由於未捕獲的異常' NSInternalInconsistencyException ',原因:' -[UIViewController _loadViewFromNibNamed:bundle:]加載了“Question1View”筆尖,但視圖插座沒有設置。“

我確定有一種方法可以使用nib文件加載視圖,而無需創建viewController類。


@AVeryDev

6)將加載的視圖附加到視圖控制器的視圖:

[self.view addSubview:myViewFromNib];

據推測,有必要從視圖中刪除它以避免內存洩漏。

為了澄清:視圖控制器有幾個IBOutlets,其中一些連接到原始筆尖文件中的項目(如往常一樣),另一些連接到加載筆尖中的項目。 兩個筆尖都擁有相同的所有者類別。 加載的視圖覆蓋原始視圖。

提示:將裝入的筆尖中的主視圖的不透明度設置為零,然後它不會遮掩原始筆尖上的項目。


迅速

其實我對這個問題的解決方法是,在viewDidLoad中加載視圖,在我的CustonViewController中,我想使用這樣的視圖:

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

不要在loadView()方法中加載視圖! loadView方法用於為您的自定義ViewController加載視圖。


以前的答案沒有考慮到在iPhone SDK的2.0和2.1之間發生的NIB(XIB)結構的變化。 用戶內容現在從索引0開始,而不是1。

您可以使用2.1版本以上的所有版本(在IPHONE之前有兩個下劃線:

 // Cited from previous example
 NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
 int startIndex;
 #ifdef __IPHONE_2_1
 startIndex = 0;
 #else
 startIndex = 1;
 #endif
 QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
 myView.question = question;

我們對大多數應用程序使用與此類似的技術。

巴尼


你也可以使用UIViewController的initWithNibName而不是loadNibNamed。 我覺得這很簡單。

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

現在你只需要創建MySubView.xib和MySubView.h / m。 在MySubView.xib中,將文件的所有者類設置為UIViewController並將類視為MySubView。

您可以使用父級xib文件定位subview大小和位置。


對於所有需要管理自定義視圖的多個實例的那些,這是一個Outlet集合 ,我以這種方式合併和自定義@Gonso,@AVeryDev和@Olie的答案:

  1. 創建一個自定義MyView : UIView ,並將其設置為所需XIB中的根UIViewCustom Class ”;

  2. MyView創建你需要的所有插座 (現在這樣做,因為在第3點之後,IB會建議你將插座連接到UIViewController而不是我們想要的自定義視圖);

  3. 將你的UIViewController設置為自定義視圖XIB的文件所有者

  4. UIViewController為每個你想要的MyView實例添加一個新的UIViews ,並將它們連接到UIViewController創建一個Outlet集合 :這些視圖將作為自定義視圖實例的“包裝器”視圖;

  5. 最後,在UIViewControllerviewDidLoad中添加以下幾行:

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
    bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    for (id object in bundleObjects) {
        if ([object isKindOfClass:[MyView class]]){
            currView = (MyView *)object;
            break;
        }
    }

    [currView.myLabel setText:@"myText"];
    [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
    //...

    [currWrapperView addSubview:currView];
    [myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..

您不應該將視圖控制器的類設置為Interface Builder中的UIView的子類。 這絕對是你問題的一部分。 留下它作為UIViewController ,它的一些子類,或者你有的其他自定義類。

至於從xib載入一個視圖,我假設你必須擁有某種視圖控制器(即使它沒有擴展UIViewController ,它可能對你的需要來說太重了)作為文件的所有者來設置Interface Builder如果你想用它來定義你的界面。 我做了一些研究來證實這一點。 這是因為否則就無法訪問UIView中的任何接口元素,也沒有辦法讓事件觸發代碼中的自己的方法。

如果您將UIViewController用作視圖的文件所有者,則可以使用initWithNibName:bundle:來加載它並獲取視圖控制器對象。 在IB中,確保使用xib中的界面將view插座設置為view 。 如果您使用其他類型的對像作為文件所有者,則需要使用NSBundleloadNibNamed:owner:options:方法來加載nib,並將文件所有者的實例傳遞給該方法。 其所有屬性將根據您在IB中定義的網點進行適當設置。


我也想做類似的事情,這是我發現的:(SDK 3.1.3)

我有一個視圖控制器A(本身由Nav控制器擁有),它可以通過按下按鈕加載VC B:

在AViewController.m中

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

現在VC B有它的Bnib接口,但是當按下一個按鈕時,我想要進入一個'編輯模式',它有一個不同於另一個nib的UI,但我不想為編輯模式添加一個新的VC,我希望新的筆尖與我現有的B VC相關聯。

所以,在BViewController.m(按鈕按下方法)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

然後在另一個按鈕上按(退出編輯模式):

[editView removeFromSuperview];

我回到了我最初的Bnib。

這工作正常,但請注意我的EditMode.nib只有1個頂級obj,它是一個UIView obj。 無論這個筆尖中的文件所有者是否設置為BViewController或默認NSObject,但要確保文件所有者中的View Outlet未設置為任何內容。 如果是,那麼我得到一個exc_bad_access崩潰,xcode繼續加載6677堆棧幀,顯示內部UIView方法重複調用...所以看起來像一個無限循環。 (然而,View Outlet IS是在我原來的Bnib中設置的)

希望這可以幫助。


我會使用UINib來實例化一個自定義的UIView來重用

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

在這種情況下需要的文件是MyCustomView.xibMyCustomViewClass.hMyCustomViewClass.m請注意, [UINib instantiateWithOwner]返回一個數組,因此您應該使用反映您想要重用的UIView的元素。 在這種情況下,它是第一個元素。


我有理由做同樣的事情(以編程方式從XIB文件加載視圖),但我需要完全從UIView的子類的子類的上下文(即不以任何方式涉及視圖控制器)完成此操作。 為此,我創建了這個實用程序方法:

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {

    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
                                                    owner:myself options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:[myself class]]) {
            return object;
        }
    }  

    return nil;
} 

然後我從我的子類的initWithFrame方法調用它,如下所示:

- (id)initWithFrame:(CGRect)frame {

    self = [Utilities initWithNibName:@"XIB1" withSelf:self];
    if (self) {
        // Initialization code.
    }
    return self;
}

發布一般興趣; 如果任何人看到任何問題而沒有這樣做,請讓我知道。


我發現Aaron Hillegass(作者,導師,Cocoa ninja)的這篇博文非常有啟發性。 即使你沒有採用他的修改方法通過指定的初始化程序來加載NIB文件,你至少可以更好地理解正在發生的過程。 最近我一直在使用這種方法取得巨大成功!


最短版本:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];

沒有一個答案解釋瞭如何創建這個問題的根源的獨立XIB。 沒有Xcode 4選項來“創建新的XIB文件”。

去做這個

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

這看起來很簡單,但它可以節省你幾分鐘的時間,因為“XIB”這個詞不會出現在任何地方。


要以編程方式在Swift 4中從nib / xib加載視圖,請執行以下操作:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}

謝謝你們。 我確實找到了一種做我想做的事情的方式。

  1. 用你需要的IBOutlet創建你的UIView
  2. 在IB中創建xib,按照喜歡的方式設計它,並像下面這樣鏈接它:文件的所有者是類UIViewController (沒有自定義子類,但是是“真正的”子類)。 文件所有者的視圖連接到主視圖,並將其類聲明為步驟1)中的一個。
  3. 將您的控件與IBOutlet連接起來。
  4. DynamicViewController可以運行其邏輯來決定要加載的視圖/ xib。 一旦它做出決定,在loadView方法中就像這樣:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;
    

而已!

主包的loadNibNamed方法將負責初始化視圖並創建連接。

現在ViewController可以根據內存中的數據顯示一個視圖或另一個視圖,並且“父”屏幕不需要打擾這個邏輯。


這是應該更容易的事情。 我最終擴展了UIViewController並添加了一個loadNib:inPlaceholder:選擇器。 現在我可以說

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

以下是該類別的代碼(它與Gonso所描述的相同):

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end

這裡有一種方法可以在Swift中完成(目前在XCode 7 beta 5中編寫Swift 2.0)。

從您在Interface Builder中設置為“Custom Class”的UIView子類創建一個像這樣的方法(我的子類叫做RecordingFooterView):

class func loadFromNib() -> RecordingFooterView? {
    let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
    let nibObjects = nib.instantiateWithOwner(nil, options: nil)
    if nibObjects.count > 0 {
        let topObject = nibObjects[0]
        return topObject as? RecordingFooterView
    }
    return nil
}

那麼你可以這樣稱呼它:

let recordingFooterView = RecordingFooterView.loadFromNib()





interface-builder