Storyboard access UIControl property through global value - ios

I have an iPhone app with a number of view controllers. Their respective UI is setup in the main storyboard file. For consistency, I have defined some global values for textfield heights, font sizes, button heights etc. that will be used by many controls. The current way of setting up these controls is by initialising them in the storyboard and then in the respective view controller, within viewDidAppear, the global values for heights, fonts and the like are accessed and assigned as the remaining properties of these controls.
But this process is fragmented and prone to error. Is there a better way of doing this? Is it possible to have access to global values, defined in swift files, within the storyboard? For instance, could the font of a textfield pointed to by the Attributes Inspector be actually referencing a global value?
Thanks

There is no way to set what you want , if you have a pre-defined height/Font , then set it directly in attributes inspector , if you want to configure for different devices you can try with size classes
Or
Create sub classes of the elements you want ( UITextField / UIIButton ) , and assign them to class name in IB

A possible solution would be to pre-process the story board file. Run a script that searches the related XML elements in the storyboard file and replaces them with the correct values. You may want to add it as a run-script phase so it would run before each compilation (I have not tried that one but it seems possible, though you would not see the fixed values in storyboard while developing), or just run it manually from the command line.

Related

Do I really need to add the same element twice, one for each UIStoryboard language file?

I am working in an internationalized Xcode project with two UIStoryboard files en.lproj/Main.storyboard and nl.lproj/Main.storyboard.
Is the consequence of this, that if I want to add a new element, say a UIButton to the storyboard, I have to add it twice, one for each file (and thus set the constraints etc also twice)?
This is how it looks in the navigator:
But selecting the base file actually selects the en.lproj/Main.storyboard, and after adding a button there it looks like I have to follow the same process in the other file.
Is this just the way it works?
No. The usual way is that you only have one storyboard that contains all the UI elements, constraints etc. in your "Base" language (e.g. English). For every additional language (e.g. German) you have a strings file that contains translations for all the texts (buttons, labels etc.) in your storyboard.

Same IBOutlet from different storyboards?

I have multiple storyboards in my project, each having an almost identical view controller (only dimensions are different). The storyboard used depends on the device in use. What I want to do is have the equivalent element from each storyboard under the same IBOutlet. This way, whatever I do to an element in the storyboard being used, the same would be done for all other storyboards. This is instead of creating an IBOutlet for the same element in each different storyboard.
For example, we may have two buttons, one in each storyboard. They are meant to be the same button but in different sizes, I set this button's alpha to 0 at one point the in Swift file. How could I do this for both buttons under one name (the same IBOutlet)? I know this means doing something on a storyboard which isn't even being used and therefore not accessible on the device, and I'm not sure whether it'll spit up an error or not. Surely this is a way around this though, because there are apps which use multiple storyboards.
I could imagine possibly stating if (storyboard == xnamex) {execute code for specific storyboard}, but this would mean having multiple if statement with the whole code repeated for different storyboards, and having to create an IBOutlet for each element, which is unrealistic. How would I get around doing this?
Many thanks.
If it's exactly the same button except as you mention the size on it. You can just pull an outlet to the same name and they both will be contained in there. As mentioned [Multiple buttons connected buttons best practise you pull the outlets to the same place and then action to the same place as well. However. Sometimes the action can be tricky, if you get problem there, just create a new action with the exact same name and then remove it. It will still be connected to the same name.

Interface Builder with predefined constant values

Is it possible to use predefined constant values in Interface Builder?
eg. If I define MyConstantWidth = 10; in a header file, can I then use it in IB? I expect it doesn't work but maybe somebody has a clever solution.
As far as I know Interface Builder is a one-way ticket.
You can only feed the .m with information (via runtime attributes) and you can't use info from your .m/.h to feed the IB.
Conceptually, placing values in your code to use in IB goes against the whole idea of separating visuals from the coded logic.
I do recognize that using "named" values within IB would be helpful to maintain consistency and facilitating updates. Colors are my personal pet peeve on this front.
If you aim to programatically change attributes that merely have an initial value in IB, outlets are probably your only option.
You can also use a hidden controls or low priority constraints to express an arbitrary value that you map to via an outlet. I use that sometimes to toggle between constraint values by creating two additional low priority constraints and copying their .constant value to the high priority one when appropriate. This keeps all the values in IB and close to each other (and more importantly out of the code).

Changing a UILable to UITextView with out removing and adding from the object library

Is it possible in Xcode to change a UILabel to a UITextView without having to delete the label and then add the textview. I thought maybe you could change the class but it can only find UILabel.
Lets say i have added 10 labels, added all the constraints and then realised i needed textViews. rather than removing them all can I somehow change the objects class which then updates the IB?
This is a significant annoyance with IB. If you change a view object in the identity inspector, it sort of works. The problem is that the properties of the object in IB are set up when it is first added. IB will continue to show the settings for the old type.
If you change a view to a different type, the properties shown in the Attributes inspector and the other tabs in IB don't change like they should.
If on the other hand you delete the previous object and add an instance of the new object type, your constraints, outlets, and actions are all lost.
I discovered the other day that if I selected a group of objects in IB and select "embed in view" then it removes all the constraints from the newly embedded objects. ARGGGH! I had a fairly complex set of views and controls and I had their layout and constraints all set up so they looked and resized correctly, and then I realized I needed them to be enclosed in a view. After selecting "embed in view" I was back at the beginning again, and probably wasted 15 or 20 minutes getting the constraints right again.
While I agree with the other answers regarding using caution when changing the XML directly, I have found the need to make a similar change, and have done so, keeping my constraints intact and have been successful.
Make sure you are using source control so that you can abandon your changes if needed.
I suggest making the properties of your modified control match a working control of the new type created in interface builder.
Example
To change from Label to Text Field, you would change the "label" tag in the XML to "textField."
<label opaque="NO" ... </label>
becomes
<textField opaque:NO ... </textField>
where "..." is the rest of the attributes and elements of the control.
Once the element tag change is completed, modify the attributes of the element to match a textField (in this case) to another Text Field created in interface builder. Be careful that the id attribute is not changed; the id attribute is how your constraints are mapped to the control.
Save the file and then reopen the file in interface builder. If interface builder can parse the file, then you can resume development as normal.
Again, use caution, but this can be a big time saver.
Edit the storyboard as plain xml is a very bad idea. If you want text view instead of UILabel is because you expect different behavior for each one, so I think the best solution is to remove and start again with text views.

Accessibility Identifier Generation

Certain testing and UI libraries require accessibility identifiers to be set for UI components in storyboards or xibs. I'd like to convert an entire project with some convention like using the mapped property name for the identifier, but anything would be useful. Is there a way to automatically generate accessibility identifiers for xib/storyboard defined views in a project?
I don't know of a way to do this in Xcode so I think you'd have to write a small script or program to manipulate the plist files directly.
Another alternative is to set the identifiers programatically. You could write a helper function to walk the view hierarchy and for any elements where an accessibility label is set, set the identifier (using whatever convention you like), this would be called from viewDidLoad.

Resources