Skip to content

Commit

Permalink
Merge pull request #25 from jeantimex/use-anchors
Browse files Browse the repository at this point in the history
Use anchors and update document.
  • Loading branch information
jeantimex authored Aug 1, 2017
2 parents e3123d1 + 38c9e18 commit 5faa955
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 138 deletions.
65 changes: 26 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,46 +104,33 @@ class CollapsibleTableViewHeader: UITableViewHeaderFooterView {
}
```

Since we are not using any storyboard or XIB, how to do auto layout programmatically? The answer is `NSLayoutConstraint`'s `constraintsWithVisualFormat` function.
Since we are not using any storyboard or XIB, how to do auto layout programmatically? The answer is `constraint anchors`.

```swift
override init(reuseIdentifier: String?) {
...
// arrowLabel must have fixed width and height
arrowLabel.widthAnchor.constraintEqualToConstant(12).active = true

titleLabel.translatesAutoresizingMaskIntoConstraints = false
arrowLabel.translatesAutoresizingMaskIntoConstraints = false
}

override func layoutSubviews() {
super.layoutSubviews()
...
let views = [
"titleLabel" : titleLabel,
"arrowLabel" : arrowLabel,
]

contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-20-[titleLabel]-[arrowLabel]-20-|",
options: [],
metrics: nil,
views: views
))

contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-[titleLabel]-|",
options: [],
metrics: nil,
views: views
))

contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-[arrowLabel]-|",
options: [],
metrics: nil,
views: views
))
...
// Content View
contentView.backgroundColor = UIColor(hex: 0x2E3944)

let marginGuide = contentView.layoutMarginsGuide

// Arrow label
contentView.addSubview(arrowLabel)
arrowLabel.textColor = UIColor.white
arrowLabel.translatesAutoresizingMaskIntoConstraints = false
arrowLabel.widthAnchor.constraint(equalToConstant: 12).isActive = true
arrowLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
arrowLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
arrowLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true

// Title label
contentView.addSubview(titleLabel)
titleLabel.textColor = UIColor.white
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
}
```

Expand Down Expand Up @@ -190,7 +177,7 @@ Setup the normal row cell is pretty straightforward:

```swift
override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell? ?? UITableViewCell(style: .Default, reuseIdentifier: "cell")
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as UITableViewCell? ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")
cell.textLabel?.text = sections[indexPath.section].items[indexPath.row]
return cell
}
Expand All @@ -204,7 +191,7 @@ The idea is really simple, if a section's `collapsed` property is `true`, we set

```swift
override func tableView(_ tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return sections[(indexPath as NSIndexPath).section].collapsed ? 0 : UITableViewAutomaticDimension
return sections[indexPath.section].collapsed ? 0 : UITableViewAutomaticDimension
}
```

Expand Down
4 changes: 4 additions & 0 deletions ios-swift-collapsible-table-section.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
0A12C8C21D9C3AAF00D0BEE3 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12C8C11D9C3AAF00D0BEE3 /* Extensions.swift */; };
0A6AFF931F31311800FA070E /* ExampleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6AFF921F31311800FA070E /* ExampleData.swift */; };
0A8465851F1DBC6E002CD874 /* CollapsibleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8465841F1DBC6E002CD874 /* CollapsibleTableViewCell.swift */; };
0A908DEF1CFCAA9200470F33 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A908DEE1CFCAA9200470F33 /* AppDelegate.swift */; };
0A908DF61CFCAA9200470F33 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0A908DF51CFCAA9200470F33 /* Assets.xcassets */; };
Expand All @@ -18,6 +19,7 @@

/* Begin PBXFileReference section */
0A12C8C11D9C3AAF00D0BEE3 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
0A6AFF921F31311800FA070E /* ExampleData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleData.swift; sourceTree = "<group>"; };
0A8465841F1DBC6E002CD874 /* CollapsibleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollapsibleTableViewCell.swift; sourceTree = "<group>"; };
0A908DEB1CFCAA9200470F33 /* ios-swift-collapsible-table-section.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ios-swift-collapsible-table-section.app"; sourceTree = BUILT_PRODUCTS_DIR; };
0A908DEE1CFCAA9200470F33 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -66,6 +68,7 @@
0A908E021CFCB8D600470F33 /* CollapsibleTableViewHeader.swift */,
0A12C8C11D9C3AAF00D0BEE3 /* Extensions.swift */,
0A8465841F1DBC6E002CD874 /* CollapsibleTableViewCell.swift */,
0A6AFF921F31311800FA070E /* ExampleData.swift */,
);
path = "ios-swift-collapsible-table-section";
sourceTree = "<group>";
Expand Down Expand Up @@ -143,6 +146,7 @@
files = (
0A908E011CFCAC7500470F33 /* CollapsibleTableViewController.swift in Sources */,
0A908E031CFCB8D600470F33 /* CollapsibleTableViewHeader.swift in Sources */,
0A6AFF931F31311800FA070E /* ExampleData.swift in Sources */,
0A8465851F1DBC6E002CD874 /* CollapsibleTableViewCell.swift in Sources */,
0A908DEF1CFCAA9200470F33 /* AppDelegate.swift in Sources */,
0A12C8C21D9C3AAF00D0BEE3 /* Extensions.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,12 @@

import UIKit

//
// MARK: - Section Data Structure
//
struct Item {
var name: String
var detail: String

init(name: String, detail: String) {
self.name = name
self.detail = detail
}
}

struct Section {
var name: String
var items: [Item]
var collapsed: Bool

init(name: String, items: [Item], collapsed: Bool = false) {
self.name = name
self.items = items
self.collapsed = collapsed
}
}

//
// MARK: - View Controller
//
class CollapsibleTableViewController: UITableViewController {

var sections = [Section]()
var sections = sectionsData

override func viewDidLoad() {
super.viewDidLoad()
Expand All @@ -48,33 +23,6 @@ class CollapsibleTableViewController: UITableViewController {
tableView.rowHeight = UITableViewAutomaticDimension

self.title = "Apple Products"

// Initialize the sections array
// Here we have three sections: Mac, iPad, iPhone
sections = [
Section(name: "Mac", items: [
Item(name: "MacBook", detail: "Apple's ultraportable laptop, trading portability for speed and connectivity."),
Item(name: "MacBook Air", detail: "While the screen could be sharper, the updated 11-inch MacBook Air is a very light ultraportable that offers great performance and battery life for the price."),
Item(name: "MacBook Pro", detail: "Retina Display The brightest, most colorful Mac notebook display ever. The display in the MacBook Pro is the best ever in a Mac notebook."),
Item(name: "iMac", detail: "iMac combines enhanced performance with our best ever Retina display for the ultimate desktop experience in two sizes."),
Item(name: "Mac Pro", detail: "Mac Pro is equipped with pro-level graphics, storage, expansion, processing power, and memory. It's built for creativity on an epic scale."),
Item(name: "Mac mini", detail: "Mac mini is an affordable powerhouse that packs the entire Mac experience into a 7.7-inch-square frame."),
Item(name: "OS X El Capitan", detail: "The twelfth major release of OS X (now named macOS)."),
Item(name: "Accessories", detail: "")
]),
Section(name: "iPad", items: [
Item(name: "iPad Pro", detail: "iPad Pro delivers epic power, in 12.9-inch and a new 10.5-inch size."),
Item(name: "iPad Air 2", detail: "The second-generation iPad Air tablet computer designed, developed, and marketed by Apple Inc."),
Item(name: "iPad mini 4", detail: "iPad mini 4 puts uncompromising performance and potential in your hand."),
Item(name: "Accessories", detail: "")
]),
Section(name: "iPhone", items: [
Item(name: "iPhone 6s", detail: "The iPhone 6S has a similar design to the 6 but updated hardware, including a strengthened chassis and upgraded system-on-chip, a 12-megapixel camera, improved fingerprint recognition sensor, and LTE Advanced support."),
Item(name: "iPhone 6", detail: "The iPhone 6 and iPhone 6 Plus are smartphones designed and marketed by Apple Inc."),
Item(name: "iPhone SE", detail: "The iPhone SE was received positively by critics, who noted its familiar form factor and design, improved hardware over previous 4-inch iPhone models, as well as its overall performance and battery life."),
Item(name: "Accessories", detail: "")
])
]
}

}
Expand All @@ -97,7 +45,7 @@ extension CollapsibleTableViewController {
let cell: CollapsibleTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell") as? CollapsibleTableViewCell ??
CollapsibleTableViewCell(style: .default, reuseIdentifier: "cell")

let item: Item = sections[(indexPath as NSIndexPath).section].items[(indexPath as NSIndexPath).row]
let item: Item = sections[indexPath.section].items[indexPath.row]

cell.nameLabel.text = item.name
cell.detailLabel.text = item.detail
Expand All @@ -106,7 +54,7 @@ extension CollapsibleTableViewController {
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return sections[(indexPath as NSIndexPath).section].collapsed ? 0 : UITableViewAutomaticDimension
return sections[indexPath.section].collapsed ? 0 : UITableViewAutomaticDimension
}

// Header
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,28 @@ class CollapsibleTableViewHeader: UITableViewHeaderFooterView {
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)

//
// Constraint the size of arrow label for auto layout
//
arrowLabel.widthAnchor.constraint(equalToConstant: 12).isActive = true
// Content View
contentView.backgroundColor = UIColor(hex: 0x2E3944)

titleLabel.translatesAutoresizingMaskIntoConstraints = false
let marginGuide = contentView.layoutMarginsGuide

// Arrow label
contentView.addSubview(arrowLabel)
arrowLabel.textColor = UIColor.white
arrowLabel.translatesAutoresizingMaskIntoConstraints = false
arrowLabel.widthAnchor.constraint(equalToConstant: 12).isActive = true
arrowLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
arrowLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
arrowLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true

// Title label
contentView.addSubview(titleLabel)
contentView.addSubview(arrowLabel)
titleLabel.textColor = UIColor.white
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true

//
// Call tapHeader when tapping on this header
Expand All @@ -44,44 +56,6 @@ class CollapsibleTableViewHeader: UITableViewHeaderFooterView {
fatalError("init(coder:) has not been implemented")
}

override func layoutSubviews() {
super.layoutSubviews()

contentView.backgroundColor = UIColor(hex: 0x2E3944)

titleLabel.textColor = UIColor.white
arrowLabel.textColor = UIColor.white

//
// Autolayout the lables
//
let views = [
"titleLabel" : titleLabel,
"arrowLabel" : arrowLabel,
]

contentView.addConstraints(NSLayoutConstraint.constraints(
withVisualFormat: "H:|-20-[titleLabel]-[arrowLabel]-20-|",
options: [],
metrics: nil,
views: views
))

contentView.addConstraints(NSLayoutConstraint.constraints(
withVisualFormat: "V:|-[titleLabel]-|",
options: [],
metrics: nil,
views: views
))

contentView.addConstraints(NSLayoutConstraint.constraints(
withVisualFormat: "V:|-[arrowLabel]-|",
options: [],
metrics: nil,
views: views
))
}

//
// Trigger toggle section when tapping on the header
//
Expand Down
59 changes: 59 additions & 0 deletions ios-swift-collapsible-table-section/ExampleData.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// ExampleData.swift
// ios-swift-collapsible-table-section
//
// Created by Yong Su on 8/1/17.
// Copyright © 2017 Yong Su. All rights reserved.
//

import Foundation

//
// MARK: - Section Data Structure
//
public struct Item {
var name: String
var detail: String

public init(name: String, detail: String) {
self.name = name
self.detail = detail
}
}

public struct Section {
var name: String
var items: [Item]
var collapsed: Bool

public init(name: String, items: [Item], collapsed: Bool = false) {
self.name = name
self.items = items
self.collapsed = collapsed
}
}

public var sectionsData: [Section] = [
Section(name: "Mac", items: [
Item(name: "MacBook", detail: "Apple's ultraportable laptop, trading portability for speed and connectivity."),
Item(name: "MacBook Air", detail: "While the screen could be sharper, the updated 11-inch MacBook Air is a very light ultraportable that offers great performance and battery life for the price."),
Item(name: "MacBook Pro", detail: "Retina Display The brightest, most colorful Mac notebook display ever. The display in the MacBook Pro is the best ever in a Mac notebook."),
Item(name: "iMac", detail: "iMac combines enhanced performance with our best ever Retina display for the ultimate desktop experience in two sizes."),
Item(name: "Mac Pro", detail: "Mac Pro is equipped with pro-level graphics, storage, expansion, processing power, and memory. It's built for creativity on an epic scale."),
Item(name: "Mac mini", detail: "Mac mini is an affordable powerhouse that packs the entire Mac experience into a 7.7-inch-square frame."),
Item(name: "OS X El Capitan", detail: "The twelfth major release of OS X (now named macOS)."),
Item(name: "Accessories", detail: "")
]),
Section(name: "iPad", items: [
Item(name: "iPad Pro", detail: "iPad Pro delivers epic power, in 12.9-inch and a new 10.5-inch size."),
Item(name: "iPad Air 2", detail: "The second-generation iPad Air tablet computer designed, developed, and marketed by Apple Inc."),
Item(name: "iPad mini 4", detail: "iPad mini 4 puts uncompromising performance and potential in your hand."),
Item(name: "Accessories", detail: "")
]),
Section(name: "iPhone", items: [
Item(name: "iPhone 6s", detail: "The iPhone 6S has a similar design to the 6 but updated hardware, including a strengthened chassis and upgraded system-on-chip, a 12-megapixel camera, improved fingerprint recognition sensor, and LTE Advanced support."),
Item(name: "iPhone 6", detail: "The iPhone 6 and iPhone 6 Plus are smartphones designed and marketed by Apple Inc."),
Item(name: "iPhone SE", detail: "The iPhone SE was received positively by critics, who noted its familiar form factor and design, improved hardware over previous 4-inch iPhone models, as well as its overall performance and battery life."),
Item(name: "Accessories", detail: "")
])
]

0 comments on commit 5faa955

Please sign in to comment.