Design and Styling in iOS vs. Android
The fundamental of a good app is a great design. But achieving it is not always easy and straightforward.
This is why we need good practices to ease our effort in developing a nice looking app while keeping our code base clean and efficient.
Our practices include defining style elements (colors, fonts, themes) in dedicated classes and components, as follows 👇
In most cases, the color values are defined in a hexadecimal format. This format is not supported out of box by iOS, so an extension for UIColor class can solve this issue.
A good idea is to create a group where you can store those configs.
In a different file, you can extend one of the classes defined above.
Now you can easily load a font like this.
Resources for Styling
In Android, everything has its own place: colors are going to → res/values colors, dimensions to → res/values/dimensions, styles to → res/values/styles.
What is worth noting is that since Android support library 26 came along, fonts can be put inside → res/fonts and be used in XML, easing our way to display different fonts depending on the theme, or setting the font family to some textview inside a TabBar we can access only via a textAppearance attribute.
Just declare the font-family resource:
And the new font family is ready to use.
In iOS the views are measured using points, 1 point is not every time equal with 1 pixel.
Basically, points are an abstract way to measure views size. This is needed because different screens can have different screen resolutions which will result to different screen densities.
iOS uses 3 density factors:
- 1x → 1 point = 1 pixel
- 2x → 1 point = 4 pixels
- 3x → 1 point = 9 pixels
Keep mind that 2x means 2 pixels for 1 point on X coordinate and another 2 pixels on Y coordinate, that’s why there is a total of 4 pixels.
Themes in iOS
Themes are a good way to see if your styles are properly organized because it will force your app to use styles in a more abstracted way.
Your lovely color named “pureWhite”, will not be in a very good place if is used somewhere in your custom UIView class. Your custom UIView classes shouldn’t access the real names of your colors.
They should only use abstract color names, like “primaryColor” or “secondaryColor”.
Another nice tip is when you define the theme class, do not use an if check in every color getter. This can easily go wrong when your third theme comes into play because you will be forced to update all your getters to introduce that third color.
Instead, you can define some models to store all the colors and other style constants. This way, you will get rid of all the “if” checks and everything will more scalable and easy to read.
Themes in Android
The best way to define your themes is using ?attrs attributes, despite the more common (and error-prone) if-else method. One should:
1. Define it’s theme-dependent attributes
2. Define attributes values in every theme
3. Use them like no one is watching
To apply a new theme across the app, your Activities should extend a BaseActivity where a call to setTheme(currentTheme) is made before any setContentView() call. To notify all activities to change their theme, you should register your activity to some ThemeManager and call recreate on those activity instances when a theme should be changed.
User Interface Components in iOS
All those tips are completely useless if you don’t use UI components. You did everything wrong if you will copy and paste your styling code in all view controllers to style a page title.
It’s cool to reuse colors, fonts and dimensions but the holy grail of UI development is to reuse entire UI components, already styled for you and ready to shine in your page.
Because it will help you in the long run. It takes more time to create custom UIView classes for each UI element but it will be much easier to just drop them in pages and they will know how to look.
and that’s powerful
Just think how much UI logic you can reuse for a text field, which is very simple at the beginning but at the end of the project you will probably end up with something like this:
- Text color
- Text font
- Placeholder color
- Placeholder font
- isValid predicate
- isPassword field
- isEmail field
- isNormal field
- Focus animation
- Unfocus animation
And this list can easily grow if we take into account the style for a small error icon with an error message. Why not? And maybe I want an animation for that error message and maybe that error should be tappable. Why? To display additional error info in a tooltip. Which will have its own style. You see where I’m going.
On iOS there are two ways to create custom components:
- using only code
- using storyboard
We are not big fans of storyboards but we still use them. We think that the best approach is to use both methods. Sometimes, some UI logic can be difficult to be defined using only code and in a storyboard can be done faster.
With that in mind, there are a ton of tutorials about XIBs on iOS. After you have the custom UIView class connected to a XIB file, you can start customizing the new UI component.
Expose custom view properties in the storyboard
After the custom view is finished, you want to use it in your page storyboard. Let’s say that you want that exact component style but different.
One way is to create an IBOutlet of your component and change its properties in your page view controller.
Another option is to edit that view property directly in the page storyboard. This can be done by marking your properties @IBInspectable.
and these properties will start to show up here:
User Interface Components in Android
I would say right off the bat that, on Android, is easier to create UI components. The UI is defined in an XML file and each tag is a UI component. This helps a lot because everything is more readable and most important, easier to change.
You can define your own UI components by extending the basic ones. For example, if you want to reuse a custom text field, you can create a new class which will extend the LinearLayout class. In that custom class, you can load a custom XML layout and start customizing it as you wish. But do not forget about overriding BaseSavedState to retain your component’s state across configuration changes.
The new view binding system is a great way to access your XML components from code, in the past you had to use the findViewById method. Now, it’s much easier if you include the whole XML layout in a <layout> tag, a binding class will be generated and after that, you can access your views like this:
That viewBind variable is created like this:
The ViewTextfieldBinding is the generated class which contains all your views and you can simply access it using the dot operator.
Notice that the inflating process of the views is changed, you don’t need to specify the name of the XML layout file anymore.
An attribute is a property for a view. If you want to define new attributes for your custom view you can do it by creating a new entry in the attrs.xml file.
Then in the class file, you can read the values from those attributes.
Now your component is ready to show off its new swag.
Using vectorial images in your app will help you stop resizing that icon to look good on all screen densities. The support is better on Android than on iOS. Android Studio has this tool to add vectorial assets, basically, it will convert your SVG file into an XML format.
Here is an XML file for a menu button icon:
It looks pretty short and readable.
iOS does not support vectorial assets by default but there are some good libraries that will do the job.
Treat your style constants with respect, keep them in a clean and safe place.
Create a good data structure for the app’s themes.
And finally, the most important one is to create UI components as often as you can. It will accelerate the UI development because you will just drop that component in a page, maybe change a few attributes and all the logic is already encapsulated there, waiting to be reused as many times as needed.