Повторно використовуйте uiview xib у розкадруванні


81

Зазвичай я люблю створювати та розробляти свої інтерфейси в інтерфейсі конструктора. Іноді мені потрібно створити єдине представлення в xib, яке можна використовувати повторно в декількох контролерах подання в раскадровці.

Відповіді:


132

Повторне використання та візуалізація xib у розкадруванні.

Перевірено за допомогою Swift 2.2 та Xcode 7.3.1

1 ---- Створіть новий UIView з назвою 'DesignableXibView'

  • Файл> Новий> Файл> Джерело> Клас какао-сенсорного> UIView

2 ---- Створіть відповідний файл xib з назвою 'DesignableXibView'

  • Файл> Новий> Файл> Інтерфейс користувача> Перегляд

3 ---- Встановіть власника файлу xib

  1. виберіть xib
  2. виберіть власника файлу
  3. встановіть для власного класу значення 'DesignableXibView' в інспекторі посвідчень.

Встановлення власника Xib в спеціальний клас

  • Примітка: Не встановлюйте власний клас подання на xib. Тільки власник файлу!

4 ---- Реалізація DesignableXibView

//  DesignableXibView.swift

import UIKit

@IBDesignable

class DesignableXibView: UIView {

    var contentView : UIView?

    override init(frame: CGRect) {
        super.init(frame: frame)
        xibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        xibSetup()
    }

    func xibSetup() {
        contentView = loadViewFromNib()

        // use bounds not frame or it'll be offset
        contentView!.frame = bounds

        // Make the view stretch with containing view
        contentView!.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]

        // Adding custom subview on top of our view (over any custom drawing > see note below)
        addSubview(contentView!)
    }

    func loadViewFromNib() -> UIView! {

        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

        return view
    }

}

5 ---- Перевірте свій багаторазовий вигляд у розкадруванні

  1. Відкрийте свою раскадровку
  2. Додайте подання
  3. Встановіть спеціальний клас цього подання
  4. почекай секунду ... БУМ !!

Візуалізація Xib всередині контролера перегляду раскадровки


1
Дякую! Можливо, ви захочете додати приклад, використовуючи обмеження за допомогою Swift, як це було зроблено у вашій відповіді Obj-C (з VFL та без нього).
Еван Р,

3
Я відповів на власне запитання, схоже, я налаштував торгові точки ДО того, як прочитав ці статті SO, в яких говорилося, що встановити власника файлу, а не подання. Це спрацювало після того, як я відключив усі розетки, переконався, що власник файлу був правильним, а потім знову додав усі розетки.
SuperDuperTango

4
Яка різниця між налаштуванням власника файлу та користувацьким класом?
Paul Brewczynski

9
Я також не бачу попередній перегляд у конструкторі інтерфейсів. Xcode 9
Яап Вейланд

3
Код працює під час запуску, але в моєму Xcode 9 під рядком модуля відображається "Не вдалося побудувати збірку"
ManuQiao,

76

НОВЕ! оновлена ​​відповідь з можливістю візуалізації безпосередньо в раскадровці (і швидко!)

Працює в Xcode 6.3.1

Створіть новий UIView з назвою 'ReuseableView'

  • Файл> Новий> Файл> Джерело> Клас какао-сенсорного> UIView

Створіть відповідний файл xib з назвою 'ReuseableView'

  • Файл> Новий> Файл> Інтерфейс користувача> Перегляд

Встановіть власника файлу xib

  1. виберіть xib
  2. виберіть власника файлу
  3. встановіть для власного класу значення «ReusableView» в інспекторі посвідчень. введіть тут опис зображення

    • Примітка: Не встановлюйте власний клас подання на xib. Тільки власник файлу!

Зробіть розетку з подання в ReuseableView.xib на ваш інтерфейс ReuseableView.h

  1. Відкрийте помічник редактора
  2. Control + Перетягніть з подання на ваш інтерфейс

введіть тут опис зображення

Додайте реалізацію initWithCoder для завантаження подання та додайте як підпрогляд.

- (id)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if (self) {

        // 1. load the interface
        [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
        // 2. add as subview
        [self addSubview:self.view];
        // 3. allow for autolayout
        self.view.translatesAutoresizingMaskIntoConstraints = NO;
        // 4. add constraints to span entire view
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];

    }
    return self;
}

введіть тут опис зображення

Перевірте свій багаторазовий вигляд у розкадруванні

  1. Відкрийте свою раскадровку
  2. Додайте подання
  3. Встановіть спеціальний клас цього подання

введіть тут опис зображення

Біжи і спостерігай! введіть тут опис зображення


@Garfbargle Додаючи поверх цього ... чи можна відобразити на розкадруванні піделементи xib? На вашому останньому кроці доданий вигляд виглядає як сірий квадрат, чи є спосіб його виправити? Дякую!
Андрес С

2
@ andres.cianio Так, однак вам доведеться застосувати зовсім інший підхід. Вам потрібно програмно побудувати подання та використовувати директиву IBDesignable. Це було спеціально для людей, які хотіли побудувати погляди візуально. Я не знаю способу побудови подання у xib візуально та його відображення в раскадровці. Я не був би здивований, якщо це стане можливим дуже скоро, хоча якщо це ще не зроблено.
Garfbargle

@Garfbargle спасибі ... Це врятувало мені життя, але зробити його в SB під час вставки вигляду було б чудово;)
Андрес С

2
Чудове пояснення! Але я маю для вас підказку: використовуйте UINib(nibName: nibName, bundle: nil).instantiateWithOwner(nil, options: nil)швидше, ніж версія NSBundle.
blackjacx

@Garfbargle Це, здається, задовольняє вимогу, що ви можете розробляти в IB і відображати його в раскадровці. В основному ви робите саме те, що зробили для XIB, і ставите, що @IBDesignable on the class.вам також потрібно реалізувати init(frame:)- але крім того, це працює досить добре! supereasyapps.com/blog/2014/12/15 / ...
wyu

53

Swift 3 і 4 Оновіть прийняту відповідь

1. Створіть новий UIView з назвою 'DesignableXibView'

  • Файл> Новий> Файл> Джерело> Клас какао-сенсорного> UIView

2. Створіть відповідний файл xib з назвою 'DesignableXibView'

  • Файл> Новий> Файл> Інтерфейс користувача> Перегляд

3. Встановіть власника файлу xib

Виберіть "DesignableXibView.xib"> "Власник файлу"> встановіть для "Custom Class" значення "DesignableXibView" в інспекторі посвідчень.

  • Примітка: Не встановлюйте власний клас подання на xib. Тільки власник файлу!

4. Реалізація DesignableXibView

    import UIKit

@IBDesignable

class DesignableXibView: UIView {

    var contentView : UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        xibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        xibSetup()
    }

    func xibSetup() {
        contentView = loadViewFromNib()

        // use bounds not frame or it'll be offset
        contentView.frame = bounds

        // Make the view stretch with containing view
        contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]

        // Adding custom subview on top of our view 
        addSubview(contentView)
    }

    func loadViewFromNib() -> UIView! {

        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView

        return view
    }
} 

5 Перевірте свій багаторазовий вигляд у розкадруванні

  • Відкрийте свою раскадровку

  • Додайте подання

  • Встановіть спеціальний клас цього подання


2
Я не можу змусити його працювати в Xcode8 / Swift3. Чудово працює під час запуску програми, але не відображається в раскадровці.
неглибокий Думав,

Для мене чудово працює. Якщо ви намагаєтесь переглянути зміни на xib, він не відображатиметься. Додайте подання всередині іншого контролера, а потім встановіть для нього клас DesignableXibView. Створіть проект, щоб переглянути зміни.
harsh_v

1
Працював і у мене. Просто потрібно було додати DesignableXibView до моєї поточної розкадрування, як описано в кроці 5 допису Garfbargle
Tinkerbell

Тільки я отримую "Не вдалося завантажити NIB у комплекті" під час виконання?
Джонні,

1
@Jonny, я думаю, String(describing: type(of: self))слід робити це безпечно
harsh_v

11

Функція initWithCoder в swift 2, якщо хтось має проблеми з її перекладом:

required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        UINib(nibName: String(self.dynamicType), bundle: NSBundle.mainBundle()).instantiateWithOwner(self, options: nil)
        self.addSubview(view)
        self.view.translatesAutoresizingMaskIntoConstraints = false
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterY , metrics: nil, views: ["view": self.view]))
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterX , metrics: nil, views: ["view": self.view]))
    }

Дякую! Можливо, ви захочете додати приклад, який не використовує VFL, для тих, хто не хоче його використовувати.
Еван Р

3

Для тих, хто намагається адаптувати прийняту відповідь (@Garfbargle) до Objective-C

Простого перетворення Swiftна Objective-Cнедостатньо для того, щоб це працювало. Мені було важко дозволити живий рендеринг в Storyboard.

Після перекладу цілого коду подання добре завантажується під час роботи на пристрої (або симуляторі), але рендеринг в реальному часі в Storyboard не працює. Причиною цього є те, що я використовував, [NSBundle mainBundle]тоді як Interface Builder не має доступу до mainBundle. Натомість вам доведеться використовувати [NSBundle bundleForClass:self.classForCoder]. БУМ, прямий рендеринг працює зараз!

Примітка: якщо у вас виникли проблеми з автоматичним Safe Area Layout Guidesмакетом , спробуйте відключити в Xib.

Для вашої зручності я залишаю тут весь свій код, щоб вам просто потрібно було скопіювати / вставити (для всього процесу слідкуйте за оригінальною відповіддю ):

BottomBarView.h

#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface BottomBarView : UIView

@end

BottomBarView.m

#import "BottomBarView.h"

@interface BottomBarView() {
    UIView *contentView;
}

@end


@implementation BottomBarView

-(id) initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self xibSetup];
    }
    return self;
}

-(id) initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self xibSetup];
    }
    return self;
}

-(void) xibSetup {
    contentView = [self loadViewFromNib];

    contentView.frame = self.bounds;

    contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [self addSubview:contentView];
}

-(UIView*) loadViewFromNib {
    NSBundle *bundle = [NSBundle bundleForClass:self.classForCoder]; //this is the important line for view to render in IB
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:bundle];
    UIView *view = [nib instantiateWithOwner:self options:nil][0];

    return view;
}

@end

Скажіть, якщо ви стикаєтесь із деякими проблемами, але це мало не зразу вийти :)


0

Якщо комусь цікаво, ось версія Xamarin.iOS коду Крок 4 @Garfbargle

public partial class CustomView : UIView
    {
        public ErrorView(IntPtr handle) : base(handle)
        {

        }

        [Export("awakeFromNib")]
        public override void AwakeFromNib()
        {
            var nibObjects = NSBundle.MainBundle.LoadNib("CustomView", this, null);
            var view = (UIView)Runtime.GetNSObject(nibObjects.ValueAt(0));
            view.Frame = Bounds;
            view.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;

            AddSubview(rootView);
        }
    }

0

Ось відповідь, яку ви весь час хотіли. Ви можете просто створити свій CustomViewклас, створити його головний екземпляр у xib з усіма підпроглядами та торговими точками. Тоді ви можете застосувати цей клас до будь-яких екземплярів на ваших раскадровках або інших xibs.

Не потрібно возитися з власником файлу, підключати розетки до проксі-сервера, модифікувати xib особливим чином або додавати екземпляр власного подання як підпрогляд самого себе.

Просто зробіть це:

  1. Імпортуйте фреймворк BFWControls
  2. Змініть свій суперклас з UIViewна NibView(або з UITableViewCellна NibTableViewCell)

Це воно!

Він навіть працює з IBDesignable, щоб зробити ваш власний вигляд (включаючи підпрогляди з xib) під час проектування в розкадровці.

Детальніше про це ви можете прочитати тут: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

І ви можете отримати фреймворк BFWControls з відкритим кодом тут: https://github.com/BareFeetWare/BFWControls

Том 👣

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.