4 Replies
      Latest reply: Sep 14, 2017 1:57 AM by PatriceRapaport RSS
      PatriceRapaport Level 1 Level 1 (0 points)

        I have views to present to mouser which can have two states: editing or noediting

        In non editing state, the user can only see informations.textfields are not editable and not bezeled:

         

        iif the user has authorization, he can push a button in the toolbar and the view enter editing mode:

         

         

         

        in some of my views, I have a combobox

        I have subclassed this combo so that in non editing mode, it is replaced by a NSTextField

         

        My code is the following (I just write what I think is necessary, for bigger clarity):

         

        class myControlCombo: NSComboBox {

         

            var myHiddenText: NSTextField!

            var state: etatWindow = .nonedition

         

          

            override var stringValue: String {

                get {

                    return super.stringValue

                }

                set (S) {

                    myHiddenText.stringValue = S

                    super.stringValue = S

                }

            }

          

            required init?(coder: NSCoder) {

                super.init(coder: coder)

              

                myHiddenText = NSTextField(frame: self.frame)

                myHiddenText.isBezeled = false

                myHiddenText.isEditable = false

                myHiddenText.font = self.font

                self.superview?.addSubview(myHiddenText)

            }

              

            override func draw(_ dirtyRect: NSRect) {

                if state == .nonedition {

                    myHiddenText.draw(dirtyRect)

                } else {

                    super.draw(dirtyRect)

                }

            }

          

            func setState (state: etatWindow, couleur: NSColor) {

                self.state = state

                if (state == .nonedition) {

                    isSelectable = false

                    //isBezeled = false

                    isButtonBordered = false

                    isEditable = false

                    backgroundColor = couleur

                    myHiddenText.isHidden = false

                } else {

                    backgroundColor = couleur

                    //isBezeled = true

                    isButtonBordered = true

                    isSelectable = true

                    isEditable = true

                    myHiddenText.isHidden = true

                }

            }

        }

         

        I have the following problem: in non editing mode, when the NSTextField is displayed instead of the combobox it is not properly aligned and the font is not correct

        Any Ideas why?

        • Re: subclassing NSComboBox
          QuinceyMorris Level 7 Level 7 (3,960 points)

          This seems like the wrong approach. You are not really modifying the NSComboBox. Instead, you are substituting a different control. All you really need is two separate controls — one NSComboBox, one NSTextField — with 'isHidden == true' on one of them at a time. No subclassing seems necessary.

           

          If you take that approach, you can design your UI with both controls visible (side-by-side, for example) and just hide one at run-time. That should make it easier to configure both in IB to have the correct alignment and appearance. If you are targeting recent macOS versions, you can even use a stack view to simplify management of their visibility.

           

          Subclassing ancient controls like NSTextField and NSComboBox is difficult, because they're so old that their implementations are quirky. It's hard to discover how to configure them properly in code.

            • Re: subclassing NSComboBox
              PatriceRapaport Level 1 Level 1 (0 points)

              I agree with you, subclassing controls is very difficult

               

              First, why do you speak about ancient controls? do new controls exist?

               

              I already subclass nstextfield, nexcombobox and nscheckbox, because I need them toperform some verifications once the user has finish with the control

              for instance some of them are mandatory, some of them require additional verification

               

              there is for instance the code I use in my subclassed control;

               

              func verifControl()->Bool {

                      let obligatoire: Any? = self.value(forKey: "obligatoire")

                      if (obligatoire != nil && (obligatoire as! Bool) == true ) {

                          if stringValue == "" {

                              let alert = NSAlert()

                              alert.messageText = "zone obligatoire"

                              alert.runModal()

                              return false

                          }

                      }

                      let nomMethode: String = "verif"+(self.identifier?.capitalized)!+"WithCtrl:"

                      let methode = Selector(nomMethode)

                      if myController != nil && myController.responds (to: methode) {

                          let res = myController.perform(methode, with:self as NSControl)

                          let value = Unmanaged<AnyObject>.fromOpaque(

                              res!.toOpaque()).takeUnretainedValue() as! Bool

                        

                          return value

                      }

                      else {

                          return true

                      }

                  }

               

              if the windowController contains a method verifIdent, the control whose identifier is ident will automatically call this method (this method can be very sofisticated, as verifying the data from a WebService)

              so I really need subclassing controls, and I have this problem with NSComboBox controls

               

              I code programs for a very long time (more than 30 years) but I am very new in Swift, XCode and OSX programmation

                • Re: subclassing NSComboBox
                  QuinceyMorris Level 7 Level 7 (3,960 points)

                  >> First, why do you speak about ancient controls? do new controls exist?

                   

                  NSControl and its subclasses represent an implementation strategy devised for macOS 10.0, and probably based on NeXTStep software designs that are even earlier — maybe 25 or 30 years old. Controls are actually wrapper views, around a functional core subclassing NSCell. By modern standards, the techniques are very unstructured, and (because they are old) there are many layers of "encrusted" behaviors and changes in behavior. You only have to look at the way that NSButton properties interact to create the various standard button appearances to realize how messy this has become over time.

                   

                  That means, as soon as you subclass a control, you are working in a very unpredictable environment. Even something apparently simple, like overriding the "draw()" method can have unforeseen consequences.

                   

                  That said, subclassing controls isn't impossible. In your case (in the code you posted), I don't see anything wrong, but it's hard to even know whether your code is setting the extra text field properties and alignment correctly. Any solution that treats controls as indivisible units is more likely to be robust. Also, as I noted, the essence of your original requirement was simply the setting of "isHidden" on two different controls, so subclassing hardly seems necessary.

                   

                  Regarding validation, it's not usual to put custom validation inside a control's implementation, since there exist other standard mechanisms. In particular, there is a validation mechanism built into KVC:

                   

                  https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/Validation.html

                   

                  That is, you provide validation methods with names of the form "validate<key>" where "<key>" is the name of the property. Then, typically, you use Cocoa bindings to link the value shown in the control to a property in your data model, and the bindings will invoke the validation whenever the control value changes.

                   

                  There is also a set of informal protocols (NSEditor/NSEditorRegistration) that mutable controls such as NSTextField implement. In effect, they register themselves with their view controller, and you can add code to your view controller subclass to trigger validation at appropriate times. Unfortunately, this mechanism is very poorly documented.

                   

                  These are all complex design issues, so I can't tell you the right answer for your situation. However, my general advice is that if you can avoid subclassing controls, you will likely end up with an easier and more robust solution.