Swift의 닙에서 UIV뷰 로드
다음은 맞춤형으로 사용할 펜촉을 로드하는 데 사용하는 목표-C 코드입니다.UIView
:
-(id)init{
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
return [subviewArray objectAtIndex:0];
}
스위프트에서 해당 코드는 무엇입니까?
나의 기여:
extension UIView {
class func fromNib<T: UIView>() -> T {
return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
}
}
그런 다음 이렇게 부릅니다.
let myCustomView: CustomView = UIView.fromNib()
..또는 심지어:
let myCustomView: CustomView = .fromNib()
원본 솔루션
- XIB와 SomeView라는 클래스를 만들었습니다(편리성과 가독성을 위해 같은 이름을 사용했습니다).저는 둘 다 UI 뷰를 기반으로 했습니다.
- XIB에서 "파일 소유자" 클래스를 (ID 검사기에서) SomeView로 변경했습니다.
- 저는 SomeView.swift에 UIView 아울렛을 만들어 XIB 파일의 최상위 뷰에 연결했습니다(편리하게 "뷰"라고 명명됨).그런 다음 필요에 따라 XIB 파일의 다른 컨트롤에 다른 콘센트를 추가했습니다.
- SomeView.swift에서 "init with code" 이니셜라이저 안에 XIB를 로드했습니다."자신"에게 어떤 것도 할당할 필요가 없습니다.XIB가 로드되는 즉시 최상위 보기를 포함한 모든 출구가 연결됩니다.유일하게 누락된 것은 보기 계층 구조에 최상위 보기를 추가하는 것입니다.
.
class SomeView: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
self.addSubview(self.view); // adding the top level view to the view hierarchy
}
...
}
이렇게 하면 닙에서 자체 로드되는 클래스를 얻을 수 있습니다.그러면 프로젝트에서 (인터페이스 빌더 또는 프로그래밍 방식으로) UIView를 사용할 수 있을 때마다 SomeView를 클래스로 사용할 수 있습니다.
업데이트 - Swift 3 구문 사용
다음 확장에서 xib를 로드하는 것은 인스턴스 메소드로 작성되며, 위와 같은 이니셜라이저에서 이 메소드를 사용할 수 있습니다.
extension UIView {
@discardableResult // 1
func fromNib<T : UIView>() -> T? { // 2
guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else { // 3
// xib not loaded, or its top view is of the wrong type
return nil
}
self.addSubview(contentView) // 4
contentView.translatesAutoresizingMaskIntoConstraints = false // 5
contentView.layoutAttachAll(to: self) // 6
return contentView // 7
}
}
- 모든 출구가 이미 연결되어 있는 경우에는 반환된 보기가 대부분 호출자에게 관심이 없기 때문에 폐기 가능한 반환 값을 사용합니다.
- UIView 유형의 선택적 개체를 반환하는 일반 메서드입니다.보기를 로드하지 못하면 0을 반환합니다.
- 현재 클래스 인스턴스와 이름이 같은 XIB 파일을 로드하려고 합니다.실패하면 0이 반환됩니다.
- 보기 계층에 최상위 수준 보기를 추가합니다.
- 이 선은 제약 조건을 사용하여 뷰를 레이아웃하는 것으로 가정합니다.
- 이 방법은 상단, 하단, 선행 및 후행 제약 조건을 추가합니다. - 모든 측면의 "자체"에 뷰를 부착합니다(자세한 내용은 https://stackoverflow.com/a/46279424/2274829 참조).
- 최상위 보기 반환
호출자 방법은 다음과 같습니다.
final class SomeView: UIView { // 1.
required init?(coder aDecoder: NSCoder) { // 2 - storyboard initializer
super.init(coder: aDecoder)
fromNib() // 5.
}
init() { // 3 - programmatic initializer
super.init(frame: CGRect.zero) // 4.
fromNib() // 6.
}
// other methods ...
}
- SomeClass는 SomeClass.xib 파일에서 내용을 로드하는 UIView 하위 클래스입니다."최종" 키워드는 선택 사항입니다.
- 보기가 스토리보드에서 사용되는 경우의 이니셜라이저입니다(일부 클래스를 스토리보드 보기의 사용자 정의 클래스로 사용).
- 보기가 프로그래밍 방식으로 생성될 때 사용하는 이니셜라이저(예:"let myView = SomeView()".
- 이 보기는 자동 레이아웃을 사용하여 레이아웃되므로 모두 0 프레임을 사용합니다.init(프레임: CGRect) {..자동 검색은 프로젝트에서 독점적으로 사용되기 때문에 메소드는 독립적으로 생성되지 않습니다.
- 6. 확장자를 사용하여 xib 파일 로드.
크레딧: 이 솔루션에서 일반적인 확장 기능을 사용한 것은 아래의 Robert의 답변에서 영감을 얻었습니다.
"보기"를 "내용 보기"로 변경하여 혼동을 방지합니다.또한 배열 첨자를 ".first"로 변경했습니다.
이제 돌아올 수 있습니다.-> Self
inswift는 이것을 조금 단순화하는 데 도움이 됩니다.스위프트 5에서 마지막으로 확인되었습니다.
extension UIView {
class func fromNib(named: String? = nil) -> Self {
let name = named ?? "\(Self.self)"
guard
let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
else { fatalError("missing expected nib named: \(name)") }
guard
/// we're using `first` here because compact map chokes compiler on
/// optimized release, so you can't use two views in one nib if you wanted to
/// and are now looking at this
let view = nib.first as? Self
else { fatalError("view of type \(Self.self) not found in \(nib)") }
return view
}
}
만약 당신이.xib
파일 및 하위 클래스는 동일한 이름을 공유하며 다음을 사용할 수 있습니다.
let view = CustomView.fromNib()
사용자 지정 이름이 있는 경우 다음을 사용합니다.
let view = CustomView.fromNib(named: "special-case")
참고:
"유형 보기를 찾을 수 없습니다." 오류가 발생하는 경우...그러면 보기의 클래스를 설정하지 않았습니다..xib
에서 합니다..xib
하고 를 누릅니다.cmd + opt + 4
그리고 그 안에class
를 합니다.
Swift 4 - 5.1 프로토콜 확장
public protocol NibInstantiatable {
static func nibName() -> String
}
extension NibInstantiatable {
static func nibName() -> String {
return String(describing: self)
}
}
extension NibInstantiatable where Self: UIView {
static func fromNib() -> Self {
let bundle = Bundle(for: self)
let nib = bundle.loadNibNamed(nibName(), owner: self, options: nil)
return nib!.first as! Self
}
}
입양
class MyView: UIView, NibInstantiatable {
}
이 구현에서는 Nib의 이름이 UIView 클래스와 동일하다고 가정합니다.예. MyView.xib.MyView에서 nibName()을 구현하여 이 동작을 수정하여 기본 프로토콜 확장 구현과 다른 이름을 반환할 수 있습니다.
xib에서 파일 소유자는 MyView이고 루트 보기 클래스는 MyView입니다.
사용.
let view = MyView.fromNib()
코드를 따르도록 해보세요.
var uiview :UIView?
self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
편집:
import UIKit
class TestObject: NSObject {
var uiview:UIView?
init() {
super.init()
self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
}
}
프로젝트에 많은 사용자 정의 뷰가 있는 경우 다음과 같은 클래스를 만들 수 있습니다.UIViewFromNib
스위프트 2.3
class UIViewFromNib: UIView {
var contentView: UIView!
var nibName: String {
return String(self.dynamicType)
}
//MARK:
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib()
}
//MARK:
private func loadViewFromNib() {
contentView = NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0] as! UIView
contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
contentView.frame = bounds
addSubview(contentView)
}
}
스위프트 5
class UIViewFromNib: UIView {
var contentView: UIView!
var nibName: String {
return String(describing: type(of: self))
}
//MARK:
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib()
}
//MARK:
func loadViewFromNib() {
let bundle = Bundle(for: UIViewFromNib.self)
contentView = UINib(nibName: nibName, bundle: bundle).instantiate(withOwner: self).first as? UIView
contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
contentView.frame = bounds
addSubview(contentView)
}
}
그리고 모든 수업에서 그냥 상속받습니다.UIViewFromNib
또한 당신은 무시할 수 있습니다.nibName
if 산의경 우재▁if경..xib
파일 이름이 다릅니다.
class MyCustomClass: UIViewFromNib {
}
Swift를 사용하여 다음 코드로 이를 달성했습니다.
class Dialog: UIView {
@IBOutlet var view:UIView!
override init(frame: CGRect) {
super.init(frame: frame)
self.frame = UIScreen.mainScreen().bounds
NSBundle.mainBundle().loadNibNamed("Dialog", owner: self, options: nil)
self.view.frame = UIScreen.mainScreen().bounds
self.addSubview(self.view)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
XIB 뷰 콘센트를 연결하여 신속하게 정의된 콘센트를 볼 수 있도록 하십시오.또한 First Responder를 사용자 정의 클래스 이름으로 설정하여 추가 콘센트 연결을 시작할 수 있습니다.
이것이 도움이 되길 바랍니다!
Xcode 7 베타 4, Swift 2.0 및 iOS9 SDK에서 테스트되었습니다.다음 코드는 xib를 uview에 할당합니다.스토리보드에서 이 사용자 지정 xib 보기를 사용할 수 있으며 IOutlet 개체에도 액세스할 수 있습니다.
import UIKit
@IBDesignable class SimpleCustomView:UIView
{
var view:UIView!;
@IBOutlet weak var lblTitle: UILabel!
@IBInspectable var lblTitleText : String?
{
get{
return lblTitle.text;
}
set(lblTitleText)
{
lblTitle.text = lblTitleText!;
}
}
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib ()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib ()
}
func loadViewFromNib() {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "SimpleCustomView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(view);
}
}
프로그래밍 방식으로 사용자 정의 보기 액세스
self.customView = SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
self.view.addSubview(self.customView!);
소스 코드 - https://github.com/karthikprabhuA/CustomXIBSwift
위의 솔루션을 기반으로 합니다.
이것은 모든 프로젝트 번들에서 작동하며 Nib()에서 전화를 걸 때 제네릭이 필요하지 않습니다.
스위프트 2
extension UIView {
public class func fromNib() -> Self {
return fromNib(nil)
}
public class func fromNib(nibName: String?) -> Self {
func fromNibHelper<T where T : UIView>(nibName: String?) -> T {
let bundle = NSBundle(forClass: T.self)
let name = nibName ?? String(T.self)
return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
}
return fromNibHelper(nibName)
}
}
스위프트 3
extension UIView {
public class func fromNib() -> Self {
return fromNib(nibName: nil)
}
public class func fromNib(nibName: String?) -> Self {
func fromNibHelper<T>(nibName: String?) -> T where T : UIView {
let bundle = Bundle(for: T.self)
let name = nibName ?? String(describing: T.self)
return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
}
return fromNibHelper(nibName: nibName)
}
}
다음과 같이 사용할 수 있습니다.
let someView = SomeView.fromNib()
이런 식으로.
let someView = SomeView.fromNib("SomeOtherNibFileName")
스위프트 4
".first as?"라고 쓰는 것을 잊지 마세요.사용자 정의 보기".
if let customView = Bundle.main.loadNibNamed("myXib", owner: self, options: nil)?.first as? CustomView {
self.view.addSubview(customView)
}
어디서나 사용하고 싶은 경우
최고의 해결책은 로버트 검메슨의 대답입니다.
extension UIView {
class func fromNib<T: UIView>() -> T {
return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
}
}
그런 다음 이렇게 부릅니다.
let myCustomView: CustomView = UIView.fromNib()
저는 이 솔루션을 선호합니다(@GK100일 경우 답변 기준).
- XIB와 SomeView라는 클래스를 만들었습니다(편리성과 가독성을 위해 같은 이름을 사용했습니다).저는 둘 다 UI 뷰를 기반으로 했습니다.
- XIB에서 "파일 소유자" 클래스를 (ID 검사기에서) SomeView로 변경했습니다.
- 저는 SomeView.swift에 UIView 아울렛을 만들어 XIB 파일의 최상위 뷰에 연결했습니다(편리하게 "뷰"라고 명명됨).그런 다음 필요에 따라 XIB 파일의 다른 컨트롤에 다른 콘센트를 추가했습니다.
에서 SomeView.swift의 XIB 했습니다.
init
또는init:frame: CGRect
이니셜라이저"자신"에게 어떤 것도 할당할 필요가 없습니다.XIB가 로드되는 즉시 최상위 보기를 포함한 모든 출구가 연결됩니다.유일하게 누락된 것은 보기 계층 구조에 최상위 보기를 추가하는 것입니다.class SomeView: UIView { override init(frame: CGRect) { super.init(frame: frame) NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil) self.addSubview(self.view); // adding the top level view to the view hierarchy } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil) self.addSubview(self.view); // adding the top level view to the view hierarchy } ... }
Swift를 사용하는 좋은 방법은 열거형을 사용하는 것입니다.
enum Views: String {
case view1 = "View1" // Change View1 to be the name of your nib
case view2 = "View2" // Change View2 to be the name of another nib
func getView() -> UIView? {
return NSBundle.mainBundle().loadNibNamed(self.rawValue, owner: nil, options: nil).first as? UIView
}
}
그런 다음 코드에서 다음을 간단히 사용할 수 있습니다.
let view = Views.view1.getView()
에 대해 됨:Swift 5
다음과 같이 정의합니다.
extension UIView {
public class func fromNib<T: UIView>() -> T {
let name = String(describing: Self.self);
guard let nib = Bundle(for: Self.self).loadNibNamed(
name, owner: nil, options: nil)
else {
fatalError("Missing nib-file named: \(name)")
}
return nib.first as! T
}
}
위의 내용은 다음과 같습니다.
let view: MyCustomView = .fromNib();
은 다과같번검다니색과 합니다.
MyCustomView
로드 그음에다.MyCustomView.nib
파일(파일이 존재하고 프로젝트에 추가된 경우).
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]
Swift 5 - 깔끔하고 사용하기 쉬운 익스텐션
[제작 프로젝트에서 붙여넣기 복사]
//
// Refactored by Essam Mohamed Fahmi.
//
import UIKit
extension UIView
{
static var nib: UINib
{
return UINib(nibName: "\(self)", bundle: nil)
}
static func instantiateFromNib() -> Self?
{
return nib.instantiate() as? Self
}
}
extension UINib
{
func instantiate() -> Any?
{
return instantiate(withOwner: nil, options: nil).first
}
}
사용.
let myCustomView: CustomView = .instantiateFromNib()
저는 그냥 이런 식으로 합니다.
if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
// Do something with myView
}
이 샘플은 메인 번들의 "MyView.xib" 니브에 있는 첫 번째 보기를 사용합니다.그러나 인덱스, 니브 이름 또는 번들(기본적으로 주)을 변경할 수 있습니다.
저는 위에서 제안한 답변처럼 뷰 init 메소드에 대한 견해를 깨우치거나 제네릭 메소드를 만들곤 했지만, 사용 사례가 종종 다르고 모든 경우를 커버하기 위해 제네릭 메소드가 UINib.instant 메소드를 사용하는 것만큼 복잡해진다는 것을 알아차렸기 때문에 더 이상 하지 않습니다.
일반적으로 뷰를 사용할 ViewController와 같은 팩토리 개체를 사용하거나 뷰를 여러 위치에서 사용해야 하는 경우 전용 팩토리 개체 또는 뷰 확장을 사용하는 것을 선호합니다.
이 예에서는 ViewController가 nib에서 보기를 로드합니다.동일한 뷰 클래스에 대해 서로 다른 레이아웃을 사용하도록 nib 파일을 변경할 수 있습니다. (이 코드는 좋은 코드가 아닙니다. 아이디어만 설명합니다.)
class MyViewController {
// Use "MyView-Compact" for compact version
var myViewNibFileName = "MyView-Standard"
lazy var myView: MyView = {
// Be sure the Nib is correct, or it will crash
// We don't want to continue with a wrong view anyway, so ! is ok
UINib.init(nibName: myViewNibFileName, bundle: nil).instantiate(withOwner: self)[0] as! MyView
}()
}
Logan의 답변의 신속한 3가지 버전
extension UIView {
public class func fromNib(nibName: String? = nil) -> Self {
return fromNib(nibName: nibName, type: self)
}
public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
return fromNib(nibName: nibName, type: T.self)!
}
public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
var view: T?
let name: String
if let nibName = nibName {
name = nibName
} else {
name = self.nibName
}
if let nibViews = Bundle.main.loadNibNamed(name, owner: nil, options: nil) {
for nibView in nibViews {
if let tog = nibView as? T {
view = tog
}
}
}
return view
}
public class var nibName: String {
return "\(self)".components(separatedBy: ".").first ?? ""
}
public class var nib: UINib? {
if let _ = Bundle.main.path(forResource: nibName, ofType: "nib") {
return UINib(nibName: nibName, bundle: nil)
} else {
return nil
}
}
}
프로토콜 및 프로토콜 확장(Swift 4.2)을 사용하여 보기를 프로그래밍 방식으로 로드하는 깨끗하고 선언적인 방법은 다음과 같습니다.
protocol XibLoadable {
associatedtype CustomViewType
static func loadFromXib() -> CustomViewType
}
extension XibLoadable where Self: UIView {
static func loadFromXib() -> Self {
let nib = UINib(nibName: "\(self)", bundle: Bundle(for: self))
guard let customView = nib.instantiate(withOwner: self, options: nil).first as? Self else {
// your app should crash if the xib doesn't exist
preconditionFailure("Couldn't load xib for view: \(self)")
}
return customView
}
}
다음과 같이 사용할 수 있습니다.
// don't forget you need a xib file too
final class MyView: UIView, XibLoadable { ... }
// and when you want to use it
let viewInstance = MyView.loadFromXib()
몇 가지 추가 고려 사항:
- 의 xib 파일이 합니다.
Custom Class
파일 소유자의 설정이 아닌 설정(및 거기서 설정된 아웃렛/액션). - 이 프로토콜/확장 기능은 사용자 정의 보기 또는 내부에서 외부로 사용할 수 있습니다.보기를 초기화할 때 다른 설정 작업이 있는 경우 내부적으로 사용할 수 있습니다.
- 사용자 정의 보기 클래스와 xib 파일의 이름이 같아야 합니다.
당신이 해야 할 일은 당신의 방법에서 init 메소드를 호출하는 것입니다.UIView
학생들
다음과 같은 방법으로 수행:
class className: UIView {
@IBOutlet var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
func setup() {
UINib(nibName: "nib", bundle: nil).instantiateWithOwner(self, options: nil)
addSubview(view)
view.frame = self.bounds
}
}
이제 뷰 컨트롤러에서 이 뷰를 하위 뷰로 추가하려면 viewcontroller.swift 파일에서 다음과 같이 하십시오.
self.view.addSubview(className())
위의 일부 답변과 유사하지만 보다 일관된 Swift3 UIView 확장 기능:
extension UIView {
class func fromNib<A: UIView> (nibName name: String, bundle: Bundle? = nil) -> A? {
let bundle = bundle ?? Bundle.main
let nibViews = bundle.loadNibNamed(name, owner: self, options: nil)
return nibViews?.first as? A
}
class func fromNib<T: UIView>() -> T? {
return fromNib(nibName: String(describing: T.self), bundle: nil)
}
}
이는 자체 명명된 nib뿐만 아니라 다른 nib/bundle에서도 클래스를 로드할 수 있는 편리성을 제공합니다.
스토리보드를 통해 이 작업을 수행할 수 있습니다. 보기에 적합한 제약 조건을 추가하기만 하면 됩니다.당신은 당신 자신의 관점을 하위 분류함으로써 이것을 쉽게 할 수 있습니다.BaseView
:
목표-C
BaseView.h
/*!
@class BaseView
@discussion Base View for getting view from xibFile
@availability ios7 and later
*/
@interface BaseView : UIView
@end
BaseView.m
#import "BaseView.h"
@implementation BaseView
#pragma mark - Public
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self prepareView];
}
return self;
}
#pragma mark - LifeCycle
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self prepareView];
}
return self;
}
#pragma mark - Private
- (void)prepareView
{
NSArray *nibsArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
UIView *view = [nibsArray firstObject];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:view];
[self addConstraintsForView:view];
}
#pragma mark - Add constraints
- (void)addConstraintsForView:(UIView *)view
{
[self addConstraints:@[[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0]
]];
}
@end
스위프트 4
import UIKit
class BaseView : UIView {
// MARK: - LifeCycle
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepareView()
}
override init(frame: CGRect) {
super.init(frame: frame)
prepareView()
}
internal class func xibName() -> String {
return String(describing: self)
}
// MARK: - Private
fileprivate func prepareView() {
let nameForXib = BaseView.xibName()
let nibs = Bundle.main.loadNibNamed(nameForXib, owner: self, options: nil)
if let view = nibs?.first as? UIView {
view.backgroundColor = UIColor.clear
view.translatesAutoresizingMaskIntoConstraints = false
addSubviewWithConstraints(view, offset: false)
}
}
}
UIView+Subview
public extension UIView {
// MARK: - UIView+Extensions
public func addSubviewWithConstraints(_ subview:UIView, offset:Bool = true) {
subview.translatesAutoresizingMaskIntoConstraints = false
let views = [
"subview" : subview
]
addSubview(subview)
var constraints = NSLayoutConstraint.constraints(withVisualFormat: offset ? "H:|-[subview]-|" : "H:|[subview]|", options: [.alignAllLeading, .alignAllTrailing], metrics: nil, views: views)
constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: offset ? "V:|-[subview]-|" : "V:|[subview]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views))
NSLayoutConstraint.activate(constraints)
}
}
저는 제약 조건을 추가하는 두 가지 변형 - 공통적인 것과 시각적 형식 언어 내 - 원하는 것을 선택하는 방법을 제공합니다 :)
또한 기본적으로 다음과 같이 가정합니다.xib
클래스.name 의 이름과 .아니오인 경우 - 그냥 변경xibName
매개 변수
는에서 당신의 를 하위 한다면,BaseView
당신은 IB에 어떤 뷰든 쉽게 넣을 수 있고 클래스를 지정할 수 있습니다.
Swift UIView 하위 클래스를 완전히 자체적으로 포함하고 Nib 사용의 구현 세부사항을 노출하지 않고 init 또는 init(frame:)를 사용하여 인스턴스화할 수 있는 기능이 있으면 프로토콜 확장을 사용하여 이를 달성할 수 있습니다.이 솔루션은 다른 많은 솔루션에서 제안하는 것처럼 중첩된 UIView 계층 구조를 방지합니다.
public class CustomView: UIView {
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var valueLabel: UILabel!
public convenience init() {
self.init(frame: CGRect.zero)
}
public override convenience init(frame: CGRect) {
self.init(internal: nil)
self.frame = frame
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
fileprivate func commonInit() {
}
}
fileprivate protocol _CustomView {
}
extension CustomView: _CustomView {
}
fileprivate extension _CustomView {
// Protocol extension initializer - has the ability to assign to self, unlike
// class initializers. Note that the name of this initializer can be anything
// you like, here we've called it init(internal:)
init(internal: Int?) {
self = Bundle.main.loadNibNamed("CustomView", owner:nil, options:nil)![0] as! Self;
}
}
class func loadFromNib<T: UIView>() -> T {
let nibName = String(describing: self)
return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}
let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
let shareView = nibs![0] as! ShareView
self.view.addSubview(shareView)
이 클래스를 수퍼 뷰로 사용
import UIKit
class ViewWithXib: UIView {
func initUI() {}
private func xibSetup() {
let view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
initUI()
}
private func loadViewFromNib() -> UIView {
let thisName = String(describing: type(of: self))
let view = Bundle(for: self.classForCoder).loadNibNamed(thisName, owner: self, options: nil)?.first as! UIView
return view
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
}
용도:
class HeaderView: ViewWithXib {
}
let header = HeaderView() // No need to load the view from nib, It will work
Logan의 답변을 기반으로 한 더욱 강력한 버전
extension UIView {
public class func fromNib(nibName: String? = nil) -> Self {
return fromNib(nibName: nibName, type: self)
}
public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
return fromNib(nibName: nibName, type: T.self)!
}
public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
var view: T?
let name: String
if let nibName = nibName {
name = nibName
} else {
name = self.nibName
}
if let nibViews = nibBundle.loadNibNamed(name, owner: nil, options: nil) {
if nibViews.indices.contains(nibIndex), let tog = nibViews[nibIndex] as? T {
view = tog
}
}
return view
}
public class var nibName: String {
return "\(self)".components(separatedBy: ".").first ?? ""
}
public class var nibIndex: Int {
return 0
}
public class var nibBundle: Bundle {
return Bundle.main
}
}
그리고 다음과 같이 사용할 수 있습니다.
class BaseView: UIView {
override class var nibName: String { return "BaseView" }
weak var delegate: StandardStateViewDelegate?
}
class ChildView: BaseView {
override class var nibIndex: Int { return 1 }
}
가장 편리한 구현입니다.여기서는 UIView가 아닌 클래스의 개체로 직접 돌아가려면 두 가지 방법이 필요합니다.
- 클래스로 표시된 viewId, 재정의 허용
- .xib에는 최상위 수준의 보기가 둘 이상 포함될 수 있으며, 이 상황도 올바르게 처리됩니다.
extension UIView {
class var viewId: String {
return String(describing: self)
}
static func instance(from bundle: Bundle? = nil, nibName: String? = nil,
owner: Any? = nil, options: [AnyHashable : Any]? = nil) -> Self? {
return instancePrivate(from: bundle ?? Bundle.main,
nibName: nibName ?? viewId,
owner: owner,
options: options)
}
private static func instancePrivate<T: UIView>(from bundle: Bundle, nibName: String,
owner: Any?, options: [AnyHashable : Any]?) -> T? {
guard
let views = bundle.loadNibNamed(nibName, owner: owner, options: options),
let view = views.first(where: { $0 is T }) as? T else { return nil }
return view
}
}
예:
guard let customView = CustomView.instance() else { return }
//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true
let bundle = Bundle(for: type(of: self))
let views = bundle.loadNibNamed("template", owner: self, options: nil)
self.view.addSubview(views?[0] as! UIView)
아래의 연장을 선호합니다.
extension UIView {
class var instanceFromNib: Self {
return Bundle(for: Self.self)
.loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
}
}
이 확장명과 상위 응답 확장명의 차이점은 상수나 변수를 저장할 필요가 없다는 것입니다.
class TitleView: UIView { }
extension UIView {
class var instanceFromNib: Self {
return Bundle(for: Self.self)
.loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
}
}
self.navigationItem.titleView = TitleView.instanceFromNib
로버트 검메슨의 대답은 완벽합니다.하지만 SPM이나 프레임워크에서 사용하려고 하면 작동하지 않습니다.
작동하기 위해 아래와 같이 수정했습니다.
internal class func fromNib<T: UIView>() -> T {
return Bundle.module.loadNibNamed(String(describing: T.self), owner: self, options: nil)![0] as! T
}
언급URL : https://stackoverflow.com/questions/24857986/load-a-uiview-from-nib-in-swift
'programing' 카테고리의 다른 글
문자열의 마지막 5자 가져오기 (0) | 2023.05.25 |
---|---|
xlwings와 openpyxl Reading Excel 워크북의 차이점 (0) | 2023.05.25 |
Windows에서 Git Bash의 기본 위치를 변경하려면 어떻게 해야 합니까? (0) | 2023.05.25 |
Git 콘솔을 어떻게 색칠합니까? (0) | 2023.05.25 |
8년 된 이 VBA 64비트 컴파일러 버그를 어떻게 고칠 수 있습니까? (0) | 2023.05.25 |