GithubHelp home page GithubHelp logo

baseview-swift's Introduction

License Build Status codecov.io

BaseViewSwift

The BaseViewSwift framework provides an organizational tool for writing custom views using UIKit. This framework is written using Swift 3.0. It has been developed and used by iOS developers at ustwo.

Dependencies

Installation

CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

$ gem install cocoapods

To integrate BaseViewSwift into your Xcode project using CocoaPods, specify it in your Podfile:

platform :ios, '8.3'

use_frameworks!

pod 'BaseViewSwift', '~> 1.0.0'

Then, run the following command:

$ pod install

Manually

If you prefer not to use either of the aforementioned dependency managers, you can integrate BaseViewSwift into your project manually. Add the BaseView.swift file from the Sources folder to your Xcode project.

Usage

Use the BaseView as a base class for all your custom views. Then override our common setup functions and have them automatically called as part of the initialization.

BaseView itself is a subclass of UIView. It contains three empty setup functions that are called by both init(frame:) and awakeFromNib(). These commonly used setup functions are setup(), setupConstraints(), and setupAccessibility() and are called in that order.

Examples

Here are some example implementations of using the three setup functions in practice. In all of the examples we call super on the setup function, but do this as appropriate in your own code.

We use titleLabel throughout our examples. We assume that this has been added as a UILabel property to the custom view.

setup

Use the setup() function to initialize and subviews, set default values, etc.

override func setup() {
  super.setup()

  backgroundColor = UIColor.red

  titleLabel = UILabel()
  titleLabel.text = "Some text"
  addSubview(titleLabel)
}

setupConstraints

Use the setupConstraints() function to add layout constraints for all the subviews.

override func setupConstraints() {
  super.setupConstraints()

  titleLabel.translatesAutoresizingMaskIntoConstraints = false

  addConstraint(NSLayoutContaint(item: titleLabel, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1.0, constant: 0.0))
  addConstraint(NSLayoutContaint(item: titleLabel, attribute: .Right, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1.0, constant: 0.0))
  addConstraint(NSLayoutContaint(item: titleLabel, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1.0, constant: 0.0))
}

setupAccessibility

Use the setupAccessibility() function to add any accessibilityIdentifier for testing/debugging as well as any accessibilityLabel and accessibilityHint as appropriate for your users.

Note that it is best to use this for static identifiers that will not change at runtime. For dynamically generated identifiers or identifiers that will change over time, we recommend doing this in the view controller or view model as appropriate.

override func setupAccessibility {
  super.setupAccessibility()

  titleLabel.accessibilityIdentifier = "TITLE"
}

Contributing

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms. See the Code of Conduct file.

Maintainers

Contact

[email protected]

License

BaseViewSwift is released under the MIT License. See License.

baseview-swift's People

Contributors

danieladias avatar madhikarma avatar

Stargazers

 avatar

Watchers

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

baseview-swift's Issues

Separate Example From Framework

Separate out the example code from the actual framework.

  • Create an Xcode workspace.
    • One project for the framework code with just the source code from the Sources folder. It should have a Cocoa Touch Framework target.
    • One project for the example code with an iOS App target. It should link to the framework rather than directly accessing the the code in the Sources folder.
  • Ensure 100% test coverage for both projects.
    • Update .travis.yml file to test both projects.

This will improve separation of the code base, making it clearer what is in the framework vs. what is just an example of using the framework.

This will also make it easier to have frameworks targeting tvOS and watchOS in the future as well as example projects.

Both Alamofire and Moya show two different approaches to separating the code. I prefer the Alamofire approach since it is dependency manager independent whereas Moya uses its own CocoaPods package to import the framework into the example. Alamofire also does a good job of targeting other platforms.

Conflicting constraints in the sample

This is due to the default constraints set by the system to match the autoresizing mask.

Log:

BaseView[16125:2837815] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x7fecea4037f0 h=--& v=--& H:[UILabel:0x7fecea70b2e0'Some text'(0)]>",
    "<NSLayoutConstraint:0x7fecea417bd0 H:|-(0)-[UILabel:0x7fecea70b2e0'Some text'](LTR)   (Names: '|':BaseView.View:0x7fecea70c780 )>",
    "<NSLayoutConstraint:0x7fecea417c70 UILabel:0x7fecea70b2e0'Some text'.right == BaseView.View:0x7fecea70c780.right>",
    "<NSLayoutConstraint:0x7fecea404790 'UIView-Encapsulated-Layout-Width' H:[BaseView.View:0x7fecea70c780(375)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fecea417c70 UILabel:0x7fecea70b2e0'Some text'.right == BaseView.View:0x7fecea70c780.right>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Add Pod Lib Lint Test to CI

Summary

Add a pod lib lint test to the CI script to ensure that the pod stays valid on all targets in the podspec.

Details

This could either be done after the Xcodebuild test script using the command above or, if you want to also migrate to Fastlane, you could use the pod_lib_lint command in the Fastfile (see https://github.com/ustwo/formvalidator-swift/blob/master/fastlane/Fastfile as an example).

In either case, a Gemfile with CocoaPods in it should also be added to ensure a pinned version of CocoaPods is used for the linting.

Notes

For more detail on linting a Pod see: https://guides.cocoapods.org/terminal/commands.html#pod_lib_lint

Only call setupConstraints if opted-in to auto layout and during the "measurement pass"

Problem 1

The constraints should be constructed during the measurement pass in the layout process to make sure they are added when the system asks for them.

Problem 2

From a developer point of view; if I decide to not use auto layout in my layout I wouldn't expect to get a call to setupConstraints()

Proposed solution

When setting up local constraints, as it is done within the View.swift in the sample, we make this view dependent on auto layout and it cannot be used anymore without auto layout enabled.
Because of this it is best to make this dependency explicit by implementing requiresConstraintBasedLayout to return true.

By setting the requiresConstraintBasedLayout we tell the system to call back when it is ready to receive the local constraints, this callback is the method: updateConstraints() and it will be called during the measurement pass.
The measurement pass is the first thing that is happening within the layout process. Meaning it will call bottom-up in the view-tree and ask for each view's constraints.
Then it prepares the views so that the next step in the layout can set the actual frame of the views.

In a basic layout it almost always works fine to construct the constraints when the view is initialising. But when it comes to a more complex layout with activation/de-activation of constraints, priorities, e.tc during runtime we need to make sure the changes are happening when the system is asking for them, i. e during the measurement pass.

In BaseView.swift I would suggest the following changes:

private(set) var needsSetupOfConstraints = true

override func updateConstraints() {
    if needsSetupOfConstraints {
        self.setupConstraints()
        needsSetupOfConstraints = false
    }

    super.updateConstraints()
}

Then in View.swift I would add this, telling the system we use local constraints to construct our layout:

override class func requiresConstraintBasedLayout() -> Bool {
    return true
}

So basically we move the setupConstraints() to the updateConstraints() callback to make sure we construct the constraints when the system asks for it.
We also make sure the setupConstraints() is called when we are opted-in to auto layout. Meaning it will not be called if we expect to create a layout with frames and not auto layout.

I have also added a flag to block the setup from happening more than once. Any call to setNeedsUpdateConstraints will trigger the measurements pass and the updateConstraints() will be called.
Also any changes to the system of constraints will automatically trigger updateConstraints(), so we need to make sure we only construct the constraints once.

The changes in View.swift is to tell the system that we are opting-in to auto layout, and will construct the layout with constraints.
This is only needed when creating constraints from code. When using NIBs and Storyboards, the system will return this automatically for us.

References

Apple docs:

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instm/UIView/updateConstraints
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/clm/UIView/requiresConstraintBasedLayout

Pretty good article about the different passes (although a bit old):

https://www.objc.io/issues/3-views/advanced-auto-layout-toolbox/

Please let me know what you think @madhikarma @aamctustwo

Support CocoaPods

To make it easier to bring into a project, add support for CocoaPods.

  • Create .podspec file. See CocoaPods documentation
  • Create CHANGELOG.md file to track changes. This is also imported into the CocoaPods directory.
  • Create LICENSE.md file to specify open-source license for using the package. I recommend the MIT License.
  • Ensure full and valid documentation (types, functions, variables, etc.) of all source files to be included in the package. This is used to generate documentation on CocoaDocs
  • Ensure full and valid test suite of all source files to be included in the package.
    • Connect test suite with Travis-CI for automated continuous integration testing.
    • Connect test suite and Travis to Codecov to automate proper test coverage.
  • Separate out source files to be included in the package into a Sources folder in the root directory. This makes for easier packaging and allows other types of package management in the future more easily (such as Swift Package Manager)

Note: After merging and creating a new release, the .podspec needs to be upload to CocoaPods. Follow the Making a CocoaPod Guide.

Add support for `tvOS`

Add support for tvOS. This depends on #7 Separate Example From Framework.

  • Add tvOS target to the Framework project.
  • Create a tvOS example.
  • Add tests for tvOS.
  • Update all package managers to list tvOS as a supported target.

`BaseView` is not declared as `public`

BaseView and its internal members need to be declared as public. That way they are accessible when they are contained within a Swift module/framework.

Add support for `watchOS`

Add support for watchOS. This depends on #7 Separate Example From Framework.

  • Add watchOS target to the Framework project.
  • Create a watchOS example. This could be included as part of the iOS example project.
  • Add tests for watchOS.
  • Update all package managers to list watchOS as a supported target.

Revise README

Revise the README file to ensure that it is (note that some of this is subjective):

  • Clear
  • Covers examples of common implementation/usage of the framework
  • Include badge for test coverage
  • Score at least a 75 on scoreme.report

Examples of some repositories with quality README files:

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.