2014-07-07 21 views
26

Tôi đang cố gắng thực hiện điều khiển tùy chỉnh cập nhật trực tiếp trong Trình tạo giao diện bằng cách sử dụng tùy chọn IB_DESIGNABLE mới được mô tả here.xcode 6 IB_DESIGNABLE- không tải tài nguyên từ gói trong Trình tạo giao diện

- (void)drawRect:(CGRect)rect 
{ 
CGContextRef context = UIGraphicsGetCurrentContext(); 

CGRect myFrame = self.bounds; 
CGContextSetLineWidth(context, 10); 
CGRectInset(myFrame, 5,5); 
[[UIColor redColor] set]; 
UIRectFrame(myFrame); 

NSBundle *bundle = [NSBundle mainBundle]; 
NSString *plistPath; 

plistPath = [bundle pathForResource:@"fileExample" ofType:@"plist"]; 
UIImage *tsliderOff = [UIImage imageNamed:@"btn_slider_off.png"]; 

[tsliderOff drawInRect:self.bounds]; 
} 

Khi tôi chạy trong mô phỏng, tôi nhận được một hộp màu đỏ với hình ảnh của tôi ở trung tâm (như dự kiến): enter image description here

Nhưng khi tôi cố gắng sử dụng giao diện Builder, nó chỉ xuất hiện như một hộp màu đỏ (không có hình ảnh ở giữa): Rendered in Interface Builder

Khi tôi gỡ lỗi: Editor-> Debug Selected Views, nó cho thấy mọi thứ được tải từ gói là không. PlistPath và tsliderOff đều xuất hiện dưới dạng nil.

Tôi đảm bảo rằng btn_slider_off.png đã được đưa vào các mục tiêu-> myFrameWork-> Giai đoạn xây dựng-> Sao chép tài nguyên.

Bất kỳ ý tưởng nào tại sao Trình tạo giao diện không nhìn thấy tệp png trong khi chỉnh sửa, nhưng hiển thị ok khi chạy? "Tạo ra một giao diện tùy chỉnh mà ám trong Interface Builder" là một chút giới hạn nếu tôi không thể tải bất kỳ hình ảnh để render ...


chỉnh sửa dựa trên giải pháp bởi rickster

rickster chỉ tôi đến giải pháp - vấn đề là các tệp không tồn tại trong mainBundle, chúng nằm trong [NSBundle bundleForClass: [self class]]; Và có vẻ như [UIImage imageNamed: @ "btn_slider_off.png"] tự động sử dụng mainBundle.

Mã sau hoạt động!

#if !TARGET_INTERFACE_BUILDER 
NSBundle *bundle = [NSBundle mainBundle]; 
#else 
NSBundle *bundle = [NSBundle bundleForClass:[self class]]; 
#endif 
NSString *fileName = [bundle pathForResource:@"btn_slider_off" ofType:@"png"]; 
UIImage *image = [UIImage imageWithContentsOfFile:fileName]; 
[image drawInRect:self.bounds]; 
+2

Liệu này vẫn áp dụng đối với Xcode 6.0.1, các lớp học IB_DESIGNABLE mà không phải là bên trong một khuôn khổ ? Tôi không thể làm cho nó hoạt động được. Điều gì đó tồi tệ hơn, tôi không thể gỡ lỗi chế độ xem, khi tôi cố gắng gỡ lỗi nó bằng điểm ngắt Xcode gặp sự cố với quả bóng quay tròn của cái chết. –

Trả lời

48

Khi câu hỏi này được hỏi lần đầu, tạo điều khiển có thể thiết kế IB cần đóng gói trong mục tiêu khung. Bạn không cần phải làm điều đó nữa - vận chuyển Xcode 6.0 (và sau này) sẽ xem trước các điều khiển được thiết kế bởi IB từ mục tiêu ứng dụng của bạn. Tuy nhiên, vấn đề và giải pháp là như nhau.

Tại sao? [NSBundle mainBundle] trả về gói chính của ứng dụng hiện đang chạy. Khi bạn gọi điều đó từ một khung công tác, bạn sẽ nhận được một gói khác được trả lại dựa trên ứng dụng đang tải khung của bạn. Khi bạn chạy ứng dụng, ứng dụng của bạn sẽ tải khung. Khi bạn sử dụng điều khiển trong IB, một ứng dụng trợ giúp Xcode đặc biệt sẽ tải khung công tác. Ngay cả khi điều khiển có thể thiết kế IB của bạn nằm trong mục tiêu ứng dụng, Xcode đang tạo một ứng dụng trợ giúp đặc biệt để chạy kiểm soát bên trong IB.

Giải pháp? Hãy gọi số +[NSBundle bundleForClass:] thay vì (hoặc NSBundle(forClass:) trong Swift). Điều này giúp bạn có được gói chứa mã thực thi cho bất kỳ lớp nào bạn chỉ định. (Bạn có thể sử dụng [self class]/self.dynamicType ở đó, nhưng hãy cẩn thận kết quả sẽ thay đổi cho các lớp con được xác định trong các gói khác nhau.)

Nếu bạn đang sử dụng phương pháp tiếp cận khung - điều này có thể hữu ích cho một số ứng dụng mặc dù không còn cần thiết cho Các điều khiển có thể thiết kế bởi IB - tốt nhất là đặt các tài nguyên hình ảnh trong cùng một khuôn khổ với mã sử dụng chúng. Nếu mã khung công tác của bạn hy vọng sử dụng các tài nguyên được cung cấp vào thời gian chạy bởi bất kỳ ứng dụng nào tải khung, điều tốt nhất để làm cho việc thiết kế IB là giả mạo nó. Thực hiện phương thức prepareForInterfaceBuilder trong điều khiển của bạn và cho phép tải tài nguyên từ một địa điểm đã biết (như gói khung hoặc đường dẫn tĩnh trong không gian làm việc Xcode của bạn).

+3

Cảm ơn! [NSBundle bundleForClass: [self class]] giải quyết vấn đề – arinmorf

+0

Nhân tiện, việc đóng gói một điều khiển trong một khung công tác không còn cần thiết nữa. – Edgar

+0

Đây là một trợ giúp lớn đối với tôi, nhưng tôi tự hỏi liệu bạn có thể giúp tôi tiến thêm một bước nữa không. Tôi có nhiều mục tiêu và một tệp kiểu cho mỗi mục tiêu. Tôi có thể lấy đúng tệp khi chạy ứng dụng, nhưng với hiển thị trực tiếp, nó luôn có vẻ như chọn tệp cuối cùng. Chuyển mục tiêu, xác định các macro tiền xử lý, đổi tên tệp để có mục tiêu được nối vào cuối, tất cả dường như có cùng kết quả. Bất kỳ ý tưởng làm thế nào tôi có thể nhận được rendering trực tiếp để chọn lên mục tiêu mà tôi đã chọn ?? tôi đã bỏ lỡ một cái gì đó hoặc điều này sẽ không hoạt động? –

13

Tôi đã gặp phải sự cố tương tự trong Swift. Kể từ Xcode 6 beta 3, bạn không cần phải sử dụng một khuôn khổ để hiển thị trực tiếp. Tuy nhiên, bạn vẫn phải giải quyết vấn đề về gói cho Chế độ xem trực tiếp để Xcode biết nơi tìm thấy nội dung. Giả sử "btn_slider_off" là một tập hợp hình ảnh trong Images.xcassets thì bạn có thể làm điều này trong Swift để hiển thị trực tiếp và nó sẽ hoạt động khi ứng dụng được chạy bình thường.

let name = "btn_slider_off" 
let myBundle = NSBundle(forClass: self.dynamicType) 
// if you want to specify the class name you can do that instead 
// assuming the class is named CustomView the code would be 
// let myBundle = NSBundle(forClass: CustomView.self) 
let image = UIImage(named: name, inBundle: myBundle, compatibleWithTraitCollection: self.traitCollection) 
if let image = image { 
    image.drawInRect(self.bounds) 
} 
3

Cảnh báo về việc trên độ phân giải bó/path trong khi ở IB_DESIGNABLE:

XCode sẽ không giải quyết được một nguồn lực nếu cuộc gọi chứa trong khởi của UIView. Tôi chỉ có thể nhận được XCode để giải quyết đường dẫn trong khi trong drawRect:

Tôi đã tìm thấy một giải pháp tuyệt vời/dễ dàng cho các kỹ thuật phân giải gói ở trên, chỉ cần chỉ định một thuộc tính IBInspectableUIImage, sau đó chỉ định hình ảnh trong Trình tạo giao diện. Ví dụ:

@IBInspectable var mainImage: UIImage? 
@IBInspectable var sideImage: UIImage? 
3

Tôi đã khắc phục nó bằng cách sử dụng dòng mã sau đây:

let image = UIImage(named: "image_name", inBundle: NSBundle(forClass: self.dynamicType), compatibleWithTraitCollection: nil) 

Sau khi thực hiện nó theo cách này những hình ảnh xuất hiện một cách chính xác trong giao diện Builder

+0

Sửa lỗi dễ dàng và giải quyết vấn đề cũng nhanh chóng. Cảm ơn – Nirri

0

Tôi cũng có cùng một vấn đề: looking at this answer và theo dõi WWDC 2014 whats new in Interface Builder. Tôi giải quyết nó như thế:

- (void)prepareForInterfaceBuilder{ 
    NSArray *array = [[NSProcessInfo processInfo].environment[@"IB_PROJECT_SOURCE_DIRECTORIES"] componentsSeparatedByString:@":"]; 
    if (array.count > 0) { 
     NSString *str = array[0]; 
     NSString *newStr = [str stringByAppendingPathComponent:@"/MyImage.jpg"]; 
     image = [UIImage imageWithContentsOfFile:newStr]; 
    } 
} 
1

TRÊN SWIFT 3.0 mã của @rickster đã thay đổi để:

// DYNAMIC BUNDLE DEFINITION FOR DESIGNABLE CLASS 
let theBundle : Bundle = Bundle(for: type(of: self)) 

// OR ALTERNATEVLY BY PROVDING THE CONCRETE NAME OF DESIGNABLE CLASS 
let theBundle : Bundle = Bundle(for: RH_DesignableView.self) 

// AND THEN SUCCESSFULLY YOU CAN LOAD THE RESSOURCE 
let theImage : UIImage? = UIImage(named: "Logo", in: theBundle, compatibleWith: nil) 
Các vấn đề liên quan