GithubHelp home page GithubHelp logo

maherksantina / msautoview Goto Github PK

View Code? Open in Web Editor NEW
16.0 4.0 4.0 210 KB

An easy way to create reusable views

Home Page: https://medium.com/@maher.santina90/how-to-easily-create-reusable-xibs-in-xcode-7424b13f6322

License: MIT License

Swift 89.22% C 0.55% Ruby 10.23%
reusable xib xcode ios nib dynamic view storyboard

msautoview's Introduction

MSAutoView

To create an iOS app, you have to deal with ALOT of views. So, managing different views will become painful as the project grows. Sometimes, a single view will be used in multiple places with small variations. You feel that if there's a way to manage every view in one place would save you alot of trouble. Well, this is what MSAutoView does exactly.

Table of Contents

  1. Installation
  2. Prerequisites
  3. Usage
  4. Structure
  5. Customization
  6. Deployment
  7. Authors
  8. License

Installation

MSAutoView is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'MSAutoView'

Prerequisites

  • XCode 9

Structure

This is an example of how your view files will be structured when using MSAutoView: Image

Each view will have its own xib file and swift file. The xib file will contain the view hierarchy, and the swift file will hold the logic for this view.

MSAutoView is a subclass of UIView. When creating a class that inherits from MSAutoView, it automatically finds the corresponding xib and adds it as a subview. It also creates top, bottom, left and right constraints for the subview to hold it in place.

Note: The view in the xib should not have constraint ambiguity or it would not show properly

Usage

Minimal Configuration

Storyboard

  1. Create a xib file and add the reusable view to it (Example):

    Image

  2. Create a swift file and add a class that inherits from MSAutoView

import UIKit
import MSAutoView

class ListingView: MSAutoView {

}

Note: For the minimal configuration to work, the class's name should be the same as the xib's name

  1. In the storyboard, add a normal view to your view controller and set its class to the one created (ListingView)

  2. Run the project, the view should contain the content of the xib

    Image

Customization

Updating View Programmatically

Ofcourse, the reusable view will be useless if you can't pass data to it programmatically. To do that, follow the steps below:

  1. Set the xib File's Owner class to the class created previously

  2. Add outlets to the class:

class ListingView: MSAutoView {
    
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var detailsLabel: UILabel!
    @IBOutlet weak var priceLabel: UILabel!
    
}
  1. In the xib file, connect the outlets to their respective views

  2. In the view controller, create an outlet for the view

@IBOutlet weak var listingView: ListingView!
  1. Connect the view controller's outlet to the view in the storyboard

  2. Change the text values of the labels in the view controller:

listingView.titleLabel.text = "This is a default title"
listingView.detailsLabel.text =  "This is a default details"
listingView.priceLabel.text = "300"

Adding variables to update the view

It's not a good convention to directly update the text in the labels. So, you can create variables to hold the values, and update the view when you change the values. To do that, follow the steps below:

  1. In the ListingView class file, add the following variables:
class ListingView: MSAutoView {
    
    //Outlets
    
    var title: String?
    var details: String?
    var price: String?
}
  1. Override updateView() function to set the label texts:
class ListingView: MSAutoView {
    
    //Outlets
    
    //Variables
    
    override func updateView() {
        super.updateView()
        titleLabel.text = title
        detailsLabel.text = details
        priceLabel.text = price
    }
}
  1. Set the values anywhere in the view controller and update the view:
listingView.title = "This is a default title"
listingView.details =  "This is a default details"
listingView.price = "300"
listingView.updateView()

Using Inspectable variables

You can use inspectable variables to hold the values for the labels:

class ListingView: MSAutoView {
    
    //Outlets
    
    @IBInspectable var title: String?
    @IBInspectable var details: String?
    @IBInspectable var price: Double = 0
    
    //Functions
}

After creating the inspectable variables, you can set them either in the xib or the view controller

Creating a table view cell from any view

If you want to save the hassle of creating a new cell class for each view that is used in a UITableView, you can use the generic class MSTableViewCell<T> supplied by the repository. You can use it as follows:

  1. Create a table view
  2. Register the cell programmatically:
tableView.register(MSTableViewCell<ListingView>.self, forCellWithReuseIdentifier: "Cell")

Alternatively, MSAutoView adds an extension to UIView which has variables that return a table view cell from any view. So, you can register the cell as such:

tableView.register(ListingView.tableViewCell.self, forCellWithReuseIdentifier: "Cell")
  1. In the tableView(_:cellForRowAt:), dequeue the cell with the reuse identifier like this:
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! MSTableViewCell<ListingView>
  1. You can access the encapsulated view using the vairable mainView on the cell:
cell.mainView.doSomething()

This class is an open class so you can subclass it as you wish to add more features.

Creating a collection view cell from any view

Creating a collection view cell from any view acts similar as creating a table view cell. But, you would use the extension variable collectionViewCell instead of the tableViewCell

Getting the table view cell or collection view cell of any view (If exists)

The MSAutoView now has two properties which can be set to be able to retrieve the table view cell/collection view cell later on from the view

public weak var tableViewCell: UITableViewCell?
public weak var collectionViewCell: UICollectionViewCell?

It works similar to the superView variable, just set the cell when setting up the view, and in the delegate functions you can retrieve the corresponding cell by calling either view.tableViewCell or view.collectionViewCell

Creating a scroll view from any view

Creating a scroll view is the same as creating a table view/collection view. Assuming that you have a tall view of class TallView which has a label called anyLabel, you can do the following in your view controller

let tallView = TallView()
tallView.anyLabel.text = "This is a dummy text"
view.addSubviewWithConstraints(tallView.scrollView)

Using a default value for all instances of the view

You can do this in 2 ways:

  1. Set the value in code:
var title: String? = "Default Title"
  1. If it's an inspectable variable, set the value in the xib file

Setting defaut value for view in a specific view controller

If your class variables are inspectables, you can change the default values in the storyboard

Adding padding to the main xib view programmatically

As a recap, the class will embed the xib's view in the main view by adding top, left, bottom and right constraints. To add padding to the constraints, you can set their constant value other than 0. You can do that in the initView() function:

override func initView() {
    super.initView { (top, left, bottom, right) in
        top.constant = 10
        left.constant = 10
        bottom.constant = -10
        right.constant = -10
    }
    updateView()
}

Note that the bottom and right constants should be negative to work as intended

Alternatively, you can add paddings later on:

    fileprivate var leftLayoutConstraint: NSLayoutConstraint?
    fileprivate var rightLayoutConstraint: NSLayoutConstraint?
    
    override func initView() {
        super.initView {[weak self] (top, left, bottom, right) in
            self?.leftLayoutConstraint = left
            self?.rightLayoutConstraint = right
        }
    }

    // update the margin whenever you want.
    func updateMargin(_ left: CGFloat, _ right: CGFloat) {
        self.leftLayoutConstraint?.constant = left
        self.rightLayoutConstraint?.constant = right
    }

Using a xib with name different than class name

If you wish to name your xib something other than the class name, you can do the following:

  1. In the class file, override the initView() function:
class ListingView: MSAutoView {

    //Outlets
    
    //Variables
    
    override func initView() {
        self.xibName = "ListingView2"
        super.initView()
    }

}

Using a xib with bundle different than the class bundle

class ListingView: MSAutoView {

    //Outlets
    
    //Variables
    
    override func initView() {
        self.xibBundle = Bundle(identifier: "Identifier")
        super.initView()
    }

}

Using protocol instead of subclassing

If you inherit from another view and subclassing is not an option, there's a protocol that can be used to easily embed views:

public protocol MSXibEmbedding: AnyObject {
    var xibBundle: Bundle? { set get  }
    var xibName: String? { set get }
    
    func loadXibMainView(constraintsConfiguration: ConstraintsConfiguration?)
    func loadXibItems(xibItemsConfiguration: XibItemsConfiguration?)
}

There's extension functions for the protocol functions so you don't have to worry about the actual implementation. Just make your view conform to the MSXibEmbedding protocol and call loadXibMainView() when you initialize your view

Adding a view inside another view with constraints programmatically

You can easily aggregate views by placing them inside each other. You can do that using the UIView extension function below:

public func addSubviewWithConstraints(_ subview: UIView, constraintsConfiguration: ConstraintsConfiguration? = nil)

Example:

parentView.addSubviewWithConstraints(childView)

Using xibs with multiple items

You might want to use a xib which has multiple top level views. Maybe there are also objects other than views. To do that, override the initView() as follows:

override func initView() {
    loadXibItems { (items) in
        //Use xib items here, maybe save them in variables ...
    }
}

Note: If you call loadXibItems more than once, the previously loaded xib items will be discarded and the new items will be available. So you might get a nil if you try to access a discarded item.

Authors

  • Maher Santina - Initial work

License

This project is licensed under the MIT License - see the LICENSE.md file for details

msautoview's People

Contributors

balazsszamody avatar leoyang-sentia avatar maherksantina avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

msautoview's Issues

Add ability to cascade elements programmatically

It would be nice to have operators to cascade elements vertically and horizontally.
For example, if I have 2 views View1 and View2, and I want to place them below each other in a reusable view, I can do something like this:

let view = VerticalCascading(View1() + View2())

or

let view = VerticalCascading(View1() + .spacer(size: 20) + View2())

Swift Compiler Error

I get 2 errors in MSCollectionViewCell, MSTableViewCell and MSScrollView. They are both the same for each class.

I get the error 'Self.CollectionViewCellContainedViewType' requires that '<<error type>>' inherit from 'MSAutoView' on line 42 in MSCollectionViewCell
and the error Type 'MSAutoView' does not conform to protocol 'CollectionViewCellContainable' on line 49 in the same file.

I started using this package yesterday with no problems, they are now showing up today after not changing any code. The only think I have done is update my xcode version to 12.0 and MacOS version to 10.15.6

Thank you!

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.