Cách tạo IBInspectable kiểu enum


83

enumkhông một Interface Builder thuộc tính runtime xác định. Phần sau không hiển thị trong Trình kiểm tra thuộc tính của Trình tạo giao diện:

enum StatusShape:Int {
    case Rectangle = 0
    case Triangle = 1
    case Circle = 2
}
@IBInspectable var shape:StatusShape = .Rectangle

Từ tài liệu: Bạn có thể đính kèm thuộc tính IBInspectable vào bất kỳ thuộc tính nào trong khai báo lớp, phần mở rộng lớp hoặc danh mục cho bất kỳ loại nào được hỗ trợ bởi các thuộc tính thời gian chạy do Bộ tạo giao diện xác định: boolean, số nguyên hoặc số dấu chấm động, chuỗi, chuỗi bản địa hóa, hình chữ nhật , điểm, kích thước, màu sắc, phạm vi và nil.

H: Làm cách nào để tôi có thể xem Trình enumkiểm tra thuộc tính của Trình tạo giao diện?


1
Enum ở đâu trong danh sách đó? Tại sao bạn nghĩ bạn có thể sử dụng enum?
Paulw

12
Sẽ thật tuyệt nếu có thể chọn một enumtrường hợp ngay từ IB mà tôi cho là, hoặc một UIFont, giống như các đối tượng UIKit gốc có thể.
SwiftArchitect

Câu trả lời:


75

Swift 3

@IBInspectable var shape:StatusShape = .Rectangle chỉ tạo một mục nhập trống trong Trình tạo giao diện:

Không có trong IB

Sử dụng bộ điều hợp, bộ điều hợp này sẽ hoạt động như cầu nối giữa Swift và Trình tạo giao diện.
shapeAdaptercó thể kiểm tra từ IB:

   // IB: use the adapter
   @IBInspectable var shapeAdapter:Int {
        get {
            return self.shape.rawValue
        }
        set( shapeIndex) {
            self.shape = StatusShape(rawValue: shapeIndex) ?? .Rectangle
        }
    }

Có sẵn trong IB

Không giống như cách tiếp cận biên dịch có điều kiện (sử dụng #if TARGET_INTERFACE_BUILDER), loại shapebiến không thay đổi theo đích, có khả năng yêu cầu thay đổi mã nguồn thêm để đối phó với các biến thể shape:NSIntegerso với shape:StatusShape:

   // Programmatically: use the enum
   var shape:StatusShape = .Rectangle

Hoàn thành mã

@IBDesignable
class ViewController: UIViewController {

    enum StatusShape:Int {
        case Rectangle
        case Triangle
        case Circle
    }

    // Programmatically: use the enum
    var shape:StatusShape = .Rectangle

    // IB: use the adapter
    @IBInspectable var shapeAdapter:Int {
        get {
            return self.shape.rawValue
        }
        set( shapeIndex) {
            self.shape = StatusShape(rawValue: shapeIndex) ?? .Rectangle
        }
    }
}

► Tìm giải pháp này trên GitHub .


Tại sao bạn lại giữ shapeAsIntdưới dạng tài sản lưu trữ hơn là tài sản tính toán?
nhgrif

Đúng. Tuy nhiên, bạn không thể tạo một thuộc tính writeonly... nhưng một thuộc tính được tính toán không tạo ra một biến phiên bản sao lưu như thuộc tính được lưu trữ của bạn.
nhgrif

Đã thêm giải pháp cửa hàng không hỗ trợ do @nhgrif đề xuất.
SwiftArchitect

1
Xin lỗi phản đối của tôi. Tôi nghĩ rằng tôi đã có một giải pháp làm việc với TARGET_INTERFACE_BUILDER nhưng tôi đã nhầm. Xin lỗi, chúc bạn may mắn ở đây trên SO
Dan Rosenstark

chuyển nhanh sang năm 2017 vấn đề này có còn giữ nguyên không?
NoSixties

40

Thay vì đặt enums có thể kiểm tra của bạn bằng int, bạn cũng có thể đặt chúng bằng chuỗi. Mặc dù không hoàn toàn thích hợp như trình đơn thả xuống, nhưng ít nhất tùy chọn này cung cấp một số mức độ dễ đọc.

Tùy chọn chỉ dành cho Swift:

// 1. Set up your enum
enum Shape: String {
    case Rectangle = "rectangle" // lowercase to make it case-insensitive
    case Triangle = "triangle"
    case Circle = "circle"
}


// 2. Then set up a stored property, which will be for use in code
var shape = Shape.Rectangle // default shape


// 3. And another stored property which will only be accessible in IB (because the "unavailable" attribute prevents its use in code)
@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'shape' instead.")
@IBInspectable var shapeName: String? {
    willSet {
        // Ensure user enters a valid shape while making it lowercase.
        // Ignore input if not valid.
        if let newShape = Shape(rawValue: newValue?.lowercased() ?? "") {
            shape = newShape
        }
    }
}

Đó khả năng cũng được điều này để làm việc với Objective-C là tốt, bằng cách thêm một initializer đến enum. Tuy nhiên, trình biên dịch sẽ chỉ hiển thị lỗi "không khả dụng" cho các thuộc tính chỉ IB của bạn trong mã nhanh.

Tùy chọn Swift với khả năng tương thích với obj-C:

@objc enum Shape: Int {
    case None
    case Rectangle
    case Triangle
    case Circle

    init(named shapeName: String) {
        switch shapeName.lowercased() {
        case "rectangle": self = .Rectangle
        case "triangle": self = .Triangle
        case "circle": self = .Circle
        default: self = .None
        }
    }
}

var shape = Shape.Rectangle // default shape

@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'shape' instead.")
@IBInspectable var shapeName: String? {
    willSet {
        if let newShape = Shape(rawValue: newValue?.lowercased() ?? "") {
            shape = newShape
        }
    }
}

2
Tôi thích Swift Onlyphiên bản này: nó cho phép học tiếng Anh thuần túy bằng IB .
SwiftArchitect

Có ai biết cách lấy các tùy chọn được trình bày dưới dạng danh sách thả xuống của chuỗi trong IB bằng phương pháp này không?
airowe

Tôi muốn biết nếu có thể làm thế nào để hoàn thành việc này bằng cách sử dụng trình đơn thả xuống.
Wael

19

Tôi không thể nhớ cú pháp nhanh, nhưng đây là cách tôi giải quyết nó trong obj-c

#if TARGET_INTERFACE_BUILDER
@property (nonatomic) IBInspectable NSInteger shape;
#else
@property (nonatomic) StatusShape shape;
#endif

1
Nơi duy nhất mà tôi có cụm điều kiện là một vị trí trong khai báo. Không có cụm điều kiện nào khác vì enum là một giá trị nguyên trong obj-c. Một điểm cần loại bỏ khi apple sửa lỗi này (giả sử rằng họ sẽ sửa.)
Jason Bobier

Đây là một dí dỏm và tuyệt vời giải pháp, và đề cập ở đây nshipster.com/ibinspectable-ibdesignable
Dan Rosenstark

7

Đối với năm 2020 - câu trả lời @SwiftArchitect được cập nhật cho hôm nay:

Đây là một ví dụ đầy đủ điển hình với tất cả các cú pháp ngày nay

nhập mô tả hình ảnh ở đây

import UIKit

@IBDesignable class ClipLabels: UILabel {
    
    enum Side: Int { case left, right }
    
    var side: Side = .left {
        didSet {
            common()
        }
    }
    
    @available(*, unavailable, message: "IB only")
    @IBInspectable var leftRight01: Int {
        get {
            return self.side.rawValue
        }
        set(index) {
            self.side = Side(rawValue: index) ?? .left
        }
    }
    

và chỉ là một ví dụ về việc sử dụng ...

switch side {
    case .left:
        textColor = .red
    case .right:
        textColor = .green
    }

Đối với Swift / iOS QA quan trọng này,

• câu trả lời rất cũ của @SwiftArchitect là hoàn toàn chính xác nhưng

• Tôi vừa cập nhật nó và thêm điều quan trọng "không khả dụng", điều này hiện có thể thực hiện được trong Swift.


4

Đây là một chủ đề cũ nhưng hữu ích. Tôi đã điều chỉnh câu trả lời của mình cho swift 4.0 và Xcode 9.0 - Swift 4 có một số vấn đề nhỏ với vấn đề này. Tôi đang gặp một biến @IBInspectable với kiểu enum và Xcode 9.0 không hài lòng, hiển thị cho tôi điều này "Không thể đánh dấu thuộc tính @IBInspectable vì kiểu của nó không thể đại diện trong Objective-c"

@Eporediese giải đáp một phần vấn đề này (cho swift3); sử dụng một thuộc tính cho bảng phân cảnh nhưng một enum thẳng cho phần còn lại của mã. Dưới đây là bộ mã hoàn chỉnh hơn cung cấp cho bạn một thuộc tính để làm việc với cả hai trường hợp.

enum StatusShape: Int {
  case Rectangle = 0
  case Triangle = 1
  case Circle = 2
}
var _shape:StatusShape = .Rectangle  // this is the backing variable

#if TARGET_INTERFACE_BUILDER
  @IBInspectable var shape: Int {    // using backing variable as a raw int

    get { return _shape.rawValue }
    set {
      if _shape.rawValue != newValue {
        _shape.rawValue = newValue
      }
    }
}
#else
var shape: StatusShape {  // using backing variable as a typed enum
  get { return _shape }
  set {
    if _shape != newValue {
      _shape = newValue
    }
  }
}
#endif

2

Giải pháp Swift 3 dựa trên SwiftArchitect

enum StatusShape: Int {
    case rectangle, triangle, circle
}
var statusShape: StatusShape = .rectangle
#if TARGET_INTERFACE_BUILDER
@IBInspectable var statusShapeIB: Int {
    get { 
        return statusShape.rawValue 
    }
    set { 
        guard let statusShape = StatusShape(rawValue: newValue) else { return }
        self.statusShape = statusShape
    }
}   //convenience var, enum not inspectable
#endif

2
Nếu bạn bọc thuộc tính có thể kiểm tra IB này trong một #if TARGET_INTERFACE_BUILDERkhối, nó sẽ chỉ ảnh hưởng đến việc hiển thị trong Trình tạo giao diện, không ảnh hưởng đến thời gian chạy. Điều này có thể dẫn đến hành vi không mong muốn và bạn sẽ luôn nhận được cảnh báo trong bảng điều khiển:Failed to set (statusShapeIB) user defined inspected property ...: this class is not key value coding-compliant for the key statusShapeIB.
Mischa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.