Giter Club home page Giter Club logo

stylish's Introduction

Stylish

Stylesheets For Storyboards

Stylish is a library that lets you create stylesheets in code or in JSON for controlling virtually any property of your UIViews, UILabels, UIButtons, etc. as well as the properties of any custom views you create yourself.

The real magic happens in storyboards, however, where you can assign one or more styles to a view, label, button, etc. right inside the Interface Builder inspector, and immediately see the live preview of the applied styles right inside the storyboard. No need to compile or run the app in the simulator or on device, since Stylish uses @IBDesignable to apply styles at design time as well.

For the first time, this provides a single solution for creating and applying styles and themes that are rendered both in the running app and in the storyboard. So what you see at design time will finally match what users will see in the app.

  • Get the full benefits of a real styling system: update a style in one place, and every view using that style updates as well. Only now, they update both at runtime and in the storyboard.
  • Change the global stylesheet at any time, and the app instantly updates to reflect the new theme.
  • Stylish is completely customizable and extensible, but it comes with a full implementation of stylesheet parsing from JSON. Your app can even load updated stylesheets over the web and cache them as the new default theme, allowing you to tweak the the appearance or any other property of your objects after the app is deployed, or based on user, time of year, etc.
  • Stylish makes it easy to rapidly test our and iterate on designs right from Xcode: you can apply or remove multiple styles in real time, right from the storyboard and see immediately how they will look on device. Or, update your JSON stylesheet in one window while your storyboard auto-updates with the changes in another window.

Installation

Since Swift has not yet reached ABI or module format stability, Apple does not support or advise distributing Swift libraries compiled outside of your project. Instead, the recommended approach is:

  1. Include the Stylish repo (this repo) as a git submodule in your project (or just check out the version of the entire Stylish repo you want to use and add it to your project as a subdirectory)
  2. Drag Stylish.xcodeproj into your own Xcode project or workspace (see the StylishExample project in this repo as a reference)
  3. In your own app target(s) on the "General" tab, scroll down to "Embedded Binaries", click the plus button and choose Stylish.framework. Again, you can refer to the StylishExample project to see what this looks like.

These steps will ensure that the Stylish framework is compiled as part of your own app's build phases as needed and that the Stylish.framework will be using the same version of Swift and the Swift compiler as the rest of your project.

  1. IMPORTANT! Because Stylish is a separate module from your app, some special considerations are needed to get IBDesignables and the live rendering of styles in Storyboards playing nicely. Specifically, it requires two extensions that override prepareForInterfaceBuilder in two types. See MainViewController.swift in the StylishExample app for reference implementations of these extensions.

Example Project

To see Stylish in action, download the the folder “StylishExample” from this repo and open it in the latest version of Xcode. Open “Main.Storyboard” in Interface Builder and watch after a moment as the unstyled labels, buttons, and views are suddenly rendered fully styled right in Xcode.

Now, go to MainViewController.swift and in the UIView extension that overrides prepareForInterfaceBuilder(), change the line Stylish.stylesheet = Graphite() to Stylish.stylesheet = Aqua(). Then return to Main.storyboard and watch as the appearance of the scene completely changes without writing any code or even compiling the app.

Go back to MainViewController.swift and change the same line from Stylish.stylesheet = Aqua() to Stylish.stylesheet = JSON(). Now look again at Main.storyboard and once again, the whole scene will transform to reflect the new Stylesheet loaded from JSON.

At this point, you can open stylesheet.json in Xcode and start changing some of the color, font, or other values in the stylesheet, then return to Main.storyboard to watch the changes you made appear live in Interface Builder.

Or, you can create a new storyboard from scratch, add views, labels, buttons, and images and then add the already-defined styles from the sample project to those views using the inspector and watch them immediately take effect. To set styles in the storyboard, select the view you want to style, and go to the attributes inspector tab in the right panel of Xcode. This is the tab where you normally set things like color, alpha, font, etc. At the top you will see a new field you can fill out called “styles”.

These fields only appear for view classes that conform to the “Styleable” protocol and which are “@IBDesignable”. Unfortunately, it’s not possible to add “@IBDesignable” via extension, so for plain UIKit components, you have to set their custom class to the Stylish version of them: StyleableUIView, StyleableUILabel, StyleableUITextField, StyleableUITextView, StyelableUIButton, and StyleableUIImageView.

  • After you have placed some Styleable components in your blank storyboard, try adding some of these styles to buttons or plain views: PrimaryBackgroundColor, SecondaryBackgroundColor, or Rounded

  • For buttons, add the style: DefaultButton

  • For labels, try some of these HeaderText, BodyText, ThemeTitle, or HighlightedText

In Stylish, styles are not inherited, but they are additive, so you can assign multiple styles to a view by separating their names with a comma, e.g. PrimaryBackgroundColor, Rounded will first apply the style “PrimaryBackgroundColor” to the view, and then it will apply the style “Rounded”. If “Rounded” defines values for certain properties that are different than what “PrimaryBackgroundColor” defined for those same properties, the values from “Rounded” will overwrite the previous values, since it is listed after “PrimaryBackgroundColor” in the list of styles. This approach gives you very fine control over exactly how you want to combine and reuse styles for any given view.

To see an example of how to make one of your own custom views Styleable and live previewable in the storyboard with Stylish, take a look at the example inside “ProgressBar.swift”

Lastly, you can try creating some of your own defined styles by opening “Aqua.swift” or “Graphite.swift” and following the instructions and comments in either of those two files.

Creating a Style

A simple Style looks like this:

struct RoundedGray : Style {
    let propertyStylers = [
        cornerRadius.set(value:  20.0),
        backgroundColor.set(value: .gray)
    ]
} 

It gets added to a Stylesheet along with a style name like this:

class MyStylesheet : Stylesheet {
    let styles: [String: Style] = [
        "RoundedGray": RoundedGray(),
        "AnotherStyleName": AnotherStyle()
    ]
    }

Alternatively, the same Stylesheet and Style can be created in JSON like this:

{
  "styles": [
    {
      "styleName": "RoundedGray",
      "styleProperties": {
          "cornerRadius": 20.0,
          "backgroundColor": "#CCCCCC"
      }
    }

To now apply this style to a view in a Storyboard, make sure the view is set to a custom class that implements the Styelable protocol (e.g. StyleableUIView), select it on the canvas, go to the Attributes Inspector in the right-hand panel of Xcode and add the string RoundedGray to the field at the top of the panel labeled "styles”, and make sure you set Stylish.stylesheet = MyStylesheet() in the UIView extension method that overrides prepareForInterfaceBuilder() as described earlier and shows in the example project. When you press Return / Enter, the view will update immediately on the canvas.

Terminology

Style: A collection of property stylers, or values to will be applied to specific properties of that target object. Same concept as in CSS.

Stylesheet: A collection of named Styles that tie together into a theme. For example, a Stylesheet called “Default” may define Styles called “Header”, “Body”, “Highlighted”, etc. And in this Stylesheet, the “Body” style may define a value of 16 pts for the fontSize property of any targeted view. There might be another Stylesheet called “Large Type” that also defines Styles with the names “Header”, “Body”, and “Highlighted”. But in the “Large Type” Stylesheet, the “Body” style has a value of 28 pts for the fontSize property. In the app, a label with the style “Body” will be set to a font size of 16 pts when the “Default” Stylesheet is active, and 28 pts when the “Large Type” Stylesheet is active. So views are associated with fixed Style names, and Stylesheets define different sets of values for the same collection of named Styles.

Styleable: The protocol that classes must conform to to participate in the Stylish process. Stylish provides versions of common UIKit components that have already implemented Styleable as well as IBDesignable (see below). These are: StyleableUIView, StyleableUILabel, StyleableUITextField, StyleableUITextView, StyleableUIButton, and StyleableUIImageView

@IBDesignable: An attribute that can be added to any UIView subclass which indicates to Interface Builder / Xcode that it should be rendered live on the storyboard canvas.

Extending Stylish

Stylish has been designed to allow developers to extend it with additional Styleable components, including custom components. The ProgressBar component in the StylishExample application included in this repo is an example of how to do so. The process, in a nutshell, consists of:

  1. Creating new PropertyStyler types for each property you want to be able to style. These types consist of a propertyKey that indicates how the property is identified in JSON stylesheets, and a static apply function that describes how to set a value of the right type on a target object of the right type.

  2. Implementing the Styleable protocol on your custom type, which is usually just a matter of adding this property definition:

    `{ 
    @IBInspectable public var styles: String = "" {
           didSet {
               Stylish.applyStyleNames(styles, to: self)
           }
       }
    
  3. And that’s usually all it takes. While Styleable doesn’t include prebuilt Styleable types for SpriteKit or SceneKit, it’s generalized enough to be easily extended to style those kinds of objects as well.

Help

If you have any questions or need help customizing or using Stylish in your project, please open an issue in this repo, or feel free to contact me via

Twitter: @_danielhall
Email: [email protected]

Happy Styling!

stylish's People

Contributors

daniel-hall avatar carlosfernandez avatar lucaslt89 avatar

Stargazers

 avatar  avatar Nadezhda Zenkova avatar Alexander Lester avatar Chris Kirkos avatar Brandon Erbschloe avatar Ramazan Demir avatar Stephen Rakonza avatar Nabil Chatbi avatar Burak Düşün avatar mxmind avatar Show avatar C. Bess avatar Roman Podymov avatar Mohammed Rokon Uddin avatar Nikita Vasilev avatar Jerry Shan avatar Jonatan Liljedahl avatar Anastasiya  avatar Maged Mohammed avatar Phùng Văn Hoà avatar Emilio Pelaez Romero avatar John Lima avatar Ao Zhang - 张 傲 avatar Rob Eberhardt avatar Luis Manuel Ramirez Vargas  avatar  avatar Benny Chew avatar Jeffrey Taylor avatar Shabib Hossain avatar Kyle Howells avatar Pavel Osipov avatar Igors Nemenonoks avatar  avatar Jeru Koiker avatar Dmitriy Shulzhenko avatar SUNG9 (Sung Hyun) avatar  avatar Gabriele Trabucco avatar Hien SiGa avatar Alex Corre avatar Scott Hodson avatar Swain avatar larryonoff avatar Ahmed Farrakha avatar Curtis Wetherly avatar Pim avatar Tiago Barreto avatar Miao avatar  avatar  avatar Flav' avatar sedat avatar Will Bamford avatar Azoft iOS Team avatar Taylor Daughtry avatar  avatar Yusuf U. avatar Dariusz Skrzypoń avatar Nuno Vieira avatar  avatar Rick Chen avatar Ahmed Rafi Ullah avatar Clark Dowding avatar  avatar Shunan avatar  avatar Rob Feldmann avatar Muhammad Umer avatar Yury Dymov avatar Leopold Roitel avatar Bob Edmonston avatar Stanislav Pankevich avatar Fabrice Delhoste avatar koyachi avatar David Ganster avatar Greg Lee avatar Robert Audi avatar  avatar Clay Ellis avatar Janek Mann avatar Vadim Tikhonov avatar Igors Vaitkus avatar  avatar Mohamed Ghonemi avatar Junaid Younus avatar Kyle avatar  avatar Tatiana Magdalena avatar  avatar Karsten Gresch avatar Brandon Brisbon avatar Moayad Al Kouz avatar Daniel Asher avatar Jazbo avatar  avatar Plamen Andreev avatar Ramon Honório avatar Jackie Qi avatar Nick Belyaev avatar

Watchers

 avatar David Ganster avatar Fabrice Delhoste avatar  avatar  avatar  avatar  avatar Randy Hector Bartumeu Huergo avatar Yeti avatar

stylish's Issues

Cocoapods integration

Hi,

thanks for this great framework!
Will you make it available on cocoapods? It would certainly make integration into new projects easier, as well as being much less painful than using submodules.

Setting UIButton.title font

I have a style for UIButtons that requires the use of a custom font. I was thinking I would set UIButton.title to a certain font ... but that doesn't seem to be possible. Do you have an example of how to set the font of a UIButton title?

Swift 3

There are Swift 3 compiler warnings and errors with Stylish.swift

Extending PropertyType

Let's assume I want to handle myself UITextField - which is sadly not covered in stylish (BTW why?)

I've created UITextFieldPropertySet, StyleableUITextField etc.
However UITextField contains additional properties like UITextAutocapitalizationType. How can I extend JSONStyleProperty to handle this kind of property? Am I able to handle custom properties somehow?

I can also prepare PR with 'StyleableUITextField' to extend Stylish :)

Storyboards can't find styles all of a sudden

This happened all of a sudden, everything was working fine before. All of the components in the storyboard are now red (which I'm used to seeing when it can't find the style). The project doesn't appear to have any errors and I've tried trimming down the styles file to rule out any errors in it, but nothing works. Is there any way to debug why this may be happening?

I should note that when I run the application it shows all the styles just fine. If I start a fresh project or open the Stylish example project, the storyboards display styles just fine.

Applying Styles Programmatically

What would be the best way to apply styles from a stylesheet programmatically, for views that need to be generated at runtime?

Apply styles to non conformed "Styleable" components

Problem:

I needed the ability to apply styles to controls that I was unable to specify StyleableUI*** on. I've been using the Eureka form framework for dynamic forms.

Soluton:

I'll start off saying that this is a hack but it did the job :)

Created a custom styler that wrapped Stylish.AnyPropertyStyler:

*since most controls inherit from UIView, we can get away with something like this.

public struct ThemeStyler {
    private let propertyValueApplicator: (UIView) -> ()
    let propertyStyle: AnyPropertyStyler
    
    init(styler: AnyPropertyStyler, propertyValueApplicator: @escaping (UIView) -> ()) {
        self.propertyStyle = styler
        self.propertyValueApplicator = propertyValueApplicator
    }
    
    func apply(to target: UIView) {
        propertyValueApplicator(target)
    }
    
    func apply(to target: UILabel) {
        propertyValueApplicator(target)
    }
    
    func apply(to target: UITextView) {
        propertyValueApplicator(target)
    }
    
    func apply(to target: UITextField) {
        propertyValueApplicator(target)
    }
}

I then extended Stylish.PropertyStyler":

  • Notice this only serves the purpose of creating a propertyValueApplicator that conforms to UIView instead of Stylish.Styleable
public extension PropertyStyler {
    static func setStyle(value: PropertyType?) -> ThemeStyler {
        let styler = set(value: value)
        return ThemeStyler(styler: styler, propertyValueApplicator: { if let target = $0 as? TargetType { Self.apply(value: value, to: target) } })
    }
}

Since all stylers are contained within a style I had to create my own like this:

protocol ThemeStyle: Stylish.Style {
    var stylers: [ThemeStyler] { get }
}

extension ThemeStyle {
	// compatibility with Stylish (default)
    var propertyStylers: [AnyPropertyStyler] {
        get {
            return stylers.map { $0.propertyStyle }
        }
    }
}

Then in my custom theme I added:

class Theme : Stylesheet {
   ...
   ...
   
   struct MyCustomStyle : ThemeStyle {
        var stylers: [ThemeStyler] = [
        	backgroundColor.setStyle(value: .red)
        ]
    }
    
    static func applyStyleNames(_ styles: [String], to target: UIView?) {
        guard let targetView = target else {
            return
        }
        var styleClasses: [ThemeStyle] = []
        for style in styles {
            if let styleClass = Stylish.stylesheet?.styles[style] {
            	// since styles can be populated from json 
                // check for our custom class
                if let themeStyle = styleClass as? ThemeStyle {
                    styleClasses.append(themeStyle)
                }
            }
        }
        styleClasses.forEach{ $0.stylers.forEach { $0.apply(to: targetView) } }
    }
}

I can now use this like so:

Theme.applyStyleNames(["MyCustomStyle"], self.someUIView)

Thoughts for next version?

Runtime JSON style

2 scenarios

  1. In myapp settings screen, I have a table view which lists 3 different theme names. If I select one theme, tableview dismisses and I would like to see the new theme applied (I have JSON stylesheet), is this possible using stylish ? and howto ?

  2. I would like to download JSON stylesheet from server when app starts and see this new theme applied.

Please advise.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.