|
| 1 | +<p align="center"> |
| 2 | + <img alt="Swift" src="https://img.shields.io/badge/Swift-5.1-orange.svg"> |
| 3 | + <img alt="CocoaPods" src="http://img.shields.io/cocoapods/v/IBKit.svg"> |
| 4 | + <a href="https://github.com/IBCodeKit/IBKit" target="_blank"> |
| 5 | + <img alt="Platform" src="https://img.shields.io/cocoapods/p/IBKit.svg?style=flat"> |
| 6 | + </a> |
| 7 | + <a href="https://travis-ci.org/IBCodeKit/IBKit" target="_blank"> |
| 8 | + <img alt="Build Status" src="https://travis-ci.org/IBCodeKit/IBKit.svg?branch=master"> |
| 9 | + </a> |
| 10 | +</p> |
| 11 | + |
1 | 12 | # IBKit
|
2 |
| -A declarative style interface builder in Swift |
| 13 | + |
| 14 | +A declarative style interface builder in Swift. |
| 15 | + |
| 16 | +```Swift |
| 17 | +var body: Interface { |
| 18 | + ViewGroup { |
| 19 | + UIImageView() |
| 20 | + .assign(to: \Self.thumbnailImageView, on: self) |
| 21 | + |
| 22 | + UIView { |
| 23 | + UILabel() |
| 24 | + .font(.preferredFont(forTextStyle: .title2)) |
| 25 | + .textColor(.label) |
| 26 | + |
| 27 | + UILabel() |
| 28 | + .font(.preferredFont(forTextStyle: .caption1)) |
| 29 | + .textColor(.secondaryLabel) |
| 30 | + .assign(to: \Self.additionalTextLabel, on: self) |
| 31 | + |
| 32 | + UIImageView() |
| 33 | + .image(UIImage(named: "ic_passenger")?.withRenderingMode(.alwaysTemplate)) |
| 34 | + .tintColor(.secondaryLabel) |
| 35 | + |
| 36 | + UILabel() |
| 37 | + .font(.preferredFont(forTextStyle: .caption1)) |
| 38 | + .textColor(.secondaryLabel) |
| 39 | + } |
| 40 | + .backgroundColor(.systemBackground) |
| 41 | + |
| 42 | + PriceView() |
| 43 | + |
| 44 | + UIView() |
| 45 | + .backgroundColor(.separator) |
| 46 | + } |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +## Why IBKit |
| 51 | + |
| 52 | +Storyboard, XIB is a kind of XML file. However have you ever edited a storyboard or XIB by hand? Probably not. It isn't easy to read or edit. Storyboards and XIBs have many troublesome properties such as `id`, `destination`, etc. Futhermore these contain a fairly large amount of XML. So it is impossible to see what’s changed when someone opens a pull request with its modification. |
| 53 | + |
| 54 | +Worse, Storyboards have a habit of growing larger and larger over time. they might start off small, but then you add another view controller and another, and another, and suddenly you realize that you have ten screens of data in a single file, and any source control changes you make are suddenly quite painful. |
| 55 | + |
| 56 | +Also, Interface Builder doesn’t know much about the Swift code, and vice versa. It makes you have lots of unsafe functionality. For example, we Ctrl-drag from IB into our code to connect something to an action. Then what if we delete that action on the code? The code still compiles well but it will be crashed! |
| 57 | + |
| 58 | +~Finally Interface Builder doesn't work as intended. Named colors that set from xib can not be changed in `viewDidLoad`, `viewWillAppear`, `awakeFromNib` method.~ |
| 59 | + |
| 60 | +Nevertheless, Storyboard, XIB has powerful advantages. It gives you a very good visual representation. Also it is easy of use. When you create a ViewController programmatically, it has a lot of codes and looks too verbose. |
| 61 | + |
| 62 | +Basically, IBKit is a tool for programmtically UI. By using declarative style, you can understand the UI intuitively. Also IBKit has a simple `Preview` class for Xcode Previews. You can be given a realtime visual representation easily. |
| 63 | + |
| 64 | +IBKit is the most straight forward, the easiest way to implement your UI. |
| 65 | + |
| 66 | +## Requirements |
| 67 | + |
| 68 | +* Xcode 11+ |
| 69 | +* Swift 5.1 |
| 70 | +* iOS 10 |
| 71 | + |
| 72 | +## Installation |
| 73 | + |
| 74 | +`IBKit` doesn't contain any external dependencies. |
| 75 | +These are currently support options: |
| 76 | + |
| 77 | +### Cocoapods |
| 78 | +``` |
| 79 | +# Podfile |
| 80 | +user_framework! |
| 81 | +target 'YOUR_TARGET_NAME' do |
| 82 | + pod 'IBKit' |
| 83 | +end |
| 84 | +``` |
| 85 | +Replace `YOUR_TARGET_NAME` and then, in the `Podfile` directory, type: |
| 86 | +``` |
| 87 | +$ pod install |
| 88 | +``` |
| 89 | +> Deployment target 11.0+ is required to install IBKit via Cocoapods. |
| 90 | +
|
| 91 | +### Swift Package Manager |
| 92 | + |
| 93 | +Create a `Package.swift` file. |
| 94 | +```Swift |
| 95 | +// swift-tools-version:5.1 |
| 96 | + |
| 97 | +import PackageDescription |
| 98 | + |
| 99 | +let package = Package( |
| 100 | + name: "NAME", |
| 101 | + dependencies: [ |
| 102 | + .package(url: "https://github.com/IBCodeKit/IBKit.git", from: "SEMVER_TAG") |
| 103 | + ], |
| 104 | + targets: [ |
| 105 | + .target(name: "NAME", dependencies: ["IBKit"]) |
| 106 | + ] |
| 107 | +) |
| 108 | +``` |
| 109 | +Replace `SEMVER_TAG` and then type: |
| 110 | +``` |
| 111 | +$ swift build |
| 112 | +``` |
| 113 | + |
| 114 | +Or |
| 115 | + |
| 116 | +Open XcodeProject > `File` > `Swift Packages` > `Add Package Dependency..` |
| 117 | + |
| 118 | +Then, Type `https://github.com/IBCodeKit/IBKit.git` |
| 119 | + |
| 120 | +## Usage |
| 121 | + |
| 122 | +Conform to `InterfaceBuilder` protocol. |
| 123 | + |
| 124 | +```Swift |
| 125 | +class PriceView: UIView, InterfaceBuilder { |
| 126 | + var body: Interface { |
| 127 | + ... |
| 128 | + } |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +Declare user interfaces in the body |
| 133 | + |
| 134 | +```Swift |
| 135 | +ViewGroup { |
| 136 | + UIImageView() |
| 137 | + .assign(to: \Self.thumbnailImageView, on: self) |
| 138 | + |
| 139 | + UIView { |
| 140 | + UILabel() |
| 141 | + .font(.preferredFont(forTextStyle: .title2)) |
| 142 | + .textColor(.label) |
| 143 | + |
| 144 | + UILabel() |
| 145 | + .font(.preferredFont(forTextStyle: .caption1)) |
| 146 | + .textColor(.secondaryLabel) |
| 147 | + .didAwakeFromBuilder { views in |
| 148 | + views.this.topAnchor.constraint(equalTo: views.superview.topAnchor).isActive = true |
| 149 | + views.this.trailingAnchor.constraint(equalTo: views.superview.trailingAnchor).isActive = true |
| 150 | + } |
| 151 | + } |
| 152 | + .backgroundColor(.systemBackground) |
| 153 | +} |
| 154 | +``` |
| 155 | + |
| 156 | +Instantiate your view(Controller) using a `loadFromIB` method. |
| 157 | + |
| 158 | +```Swift |
| 159 | +let view = PriceView.loadFromIB() |
| 160 | +``` |
| 161 | + |
| 162 | +Or load the body using a `build` method. |
| 163 | + |
| 164 | +```Swift |
| 165 | +override init(frame: CGRect) { |
| 166 | + super.init(frame: frame) |
| 167 | + build() |
| 168 | +} |
| 169 | +``` |
| 170 | + |
| 171 | +### Xcode Preivews |
| 172 | + |
| 173 | +1. To use Xcode Previews, SwiftUI framework must be imported weakly on your project. |
| 174 | + |
| 175 | + Open XcodeProject > `Build Phases` > `Link Binary With Libraries` > Add `SwifTUI` > Change SwiftUI status to `Optional` |
| 176 | + |
| 177 | +2. Conform to `PreviewProvider` protocol. |
| 178 | + |
| 179 | +```Swift |
| 180 | +#if canImport(SwiftUI) && DEBUG |
| 181 | +import SwiftUI |
| 182 | + |
| 183 | +@available(iOS 13.0, *) |
| 184 | +struct PriceView_Preview: PreviewProvider { |
| 185 | + ... |
| 186 | +} |
| 187 | +#endif |
| 188 | +``` |
| 189 | + |
| 190 | +3. Implement `PreviewProvider` protocol with `Preview` class. |
| 191 | + |
| 192 | +```Swift |
| 193 | +static var previews: some View { |
| 194 | + Preview(view: PriceView.loadFromIB()) |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +## Contribution |
| 199 | +Any pull requests and bug reports are welcome! |
| 200 | + |
| 201 | +Feel free to make a pull request. |
0 commit comments