Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
312 changes: 162 additions & 150 deletions Demo/Base.lproj/Main.storyboard

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions Demo/CheckboxCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// CheckboxCell.swift
// Demo
//
// Created by Aleksei Cherepanov on 31/10/2018.
// Copyright © 2018 Kevin Hirsch. All rights reserved.
//

import UIKit
import DropDown

class CheckboxCell: DropDownCustomCell {

override func setSelected(_ selected: Bool, animated: Bool) {
let executeSelection: () -> Void = { [weak self] in
guard let `self` = self else { return }
self.accessoryType = selected ? .checkmark : .none
}

if animated {
UIView.animate(withDuration: 0.3, animations: {
executeSelection()
})
} else {
executeSelection()
}
accessibilityTraits = selected ? .selected : .none
}

override func systemLayoutSizeFitting(_ targetSize: CGSize) -> CGSize {
let contentSize = optionLabel.systemLayoutSizeFitting(targetSize)
let accessoryViewWidth:CGFloat = 40
return CGSize(width: contentSize.width + accessoryViewWidth,
height: contentSize.height)
}

override func layoutSubviews() {
super.layoutSubviews()
optionLabel.frame = contentView.bounds
}

}
39 changes: 39 additions & 0 deletions Demo/MySecondCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// MySecondCell.swift
// Demo
//
// Created by Aleksei Cherepanov on 31/10/2018.
// Copyright © 2018 Kevin Hirsch. All rights reserved.
//

import UIKit
import DropDown

class MySecondCell: DropDownCustomCell {
var logoImageView = UIImageView(frame: .zero)

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(logoImageView)
}

override func layoutSubviews() {
super.layoutSubviews()
let size = contentView.bounds.size
let imageSize = logoImageView.image?.size ?? .zero
let padding: CGFloat = 4
logoImageView.frame = CGRect(x: padding,
y: (frame.height - imageSize.height)/2,
width: imageSize.width,
height: imageSize.height)
let offset = padding * 2 + imageSize.width
optionLabel.frame = CGRect(x: offset,
y: 0,
width: size.width - offset,
height: size.height)
}
}
29 changes: 29 additions & 0 deletions Demo/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class ViewController: UIViewController {
switch sender.selectedSegmentIndex {
case 0: setupDefaultDropDown()
case 1: customizeDropDown(self)
case 2: customizeDropDownWithClass(self)
case 3: customizeDropDownSelector(self)
default: break;
}
}
Expand Down Expand Up @@ -134,6 +136,31 @@ class ViewController: UIViewController {
/*** ---------------- ***/
}
}

func customizeDropDownWithClass(_ sender: AnyObject) {
dropDowns.forEach {
/*** FOR CUSTOM CELLS ***/
$0.cellClass = MySecondCell.self

$0.customCellConfiguration = { (index: Index, item: String, cell: DropDownCell) -> Void in
guard let cell = cell as? MySecondCell else { return }

// Setup your custom UI components
cell.logoImageView.image = UIImage(named: "logo_\(index % 10)")
}
/*** ---------------- ***/
}
}

func customizeDropDownSelector(_ sender: AnyObject) {
dropDowns.forEach {
/*** FOR CUSTOM CELLS ***/
$0.cellClass = CheckboxCell.self
$0.customCellConfiguration = nil
}
}



//MARK: - UIViewController

Expand All @@ -143,6 +170,8 @@ class ViewController: UIViewController {
setupDropDowns()
dropDowns.forEach { $0.dismissMode = .onTap }
dropDowns.forEach { $0.direction = .any }

dropDowns.forEach { $0.showArrowIndicator = true }

view.addSubview(textField)
}
Expand Down
8 changes: 8 additions & 0 deletions DropDown.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
0AB5D89A1D0EF15D002D3A17 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D8971D0EF15D002D3A17 /* ViewController.swift */; };
0AB5D8A11D0EF173002D3A17 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0AB5D89F1D0EF173002D3A17 /* Main.storyboard */; };
0AC1C33A1B6B884100A8DC0D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0AC1C3391B6B884100A8DC0D /* Images.xcassets */; };
9442B7DB2189B8EC000720B8 /* CheckboxCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9442B7DA2189B8EC000720B8 /* CheckboxCell.swift */; };
945A86E22188BD39007B8D96 /* MySecondCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 945A86E12188BD39007B8D96 /* MySecondCell.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -81,6 +83,8 @@
0AB5D8971D0EF15D002D3A17 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
0AB5D8A01D0EF173002D3A17 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
0AC1C3391B6B884100A8DC0D /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Demo/Images.xcassets; sourceTree = "<group>"; };
9442B7DA2189B8EC000720B8 /* CheckboxCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxCell.swift; sourceTree = "<group>"; };
945A86E12188BD39007B8D96 /* MySecondCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySecondCell.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -137,6 +141,8 @@
0AB5D8971D0EF15D002D3A17 /* ViewController.swift */,
0A8518D41D6480190089529A /* MyCell.swift */,
0A8518D61D6481E30089529A /* MyCell.xib */,
945A86E12188BD39007B8D96 /* MySecondCell.swift */,
9442B7DA2189B8EC000720B8 /* CheckboxCell.swift */,
0AB5D89F1D0EF173002D3A17 /* Main.storyboard */,
0AB5D8961D0EF15D002D3A17 /* NiceButton.swift */,
0A7644121B676C2300BF1A2D /* Supporting Files */,
Expand Down Expand Up @@ -358,8 +364,10 @@
files = (
0AB5D89A1D0EF15D002D3A17 /* ViewController.swift in Sources */,
0A8518D51D6480190089529A /* MyCell.swift in Sources */,
9442B7DB2189B8EC000720B8 /* CheckboxCell.swift in Sources */,
0AB5D8991D0EF15D002D3A17 /* NiceButton.swift in Sources */,
0AB5D8981D0EF15D002D3A17 /* AppDelegate.swift in Sources */,
945A86E22188BD39007B8D96 /* MySecondCell.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
1 change: 1 addition & 0 deletions DropDown/helpers/DPDConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal struct DPDConstant {
static let BackgroundColor = UIColor(white: 0.94, alpha: 1)
static let SelectionBackgroundColor = UIColor(white: 0.89, alpha: 1)
static let SeparatorColor = UIColor.clear
static let SeparatorInset = UIEdgeInsets.zero
static let CornerRadius: CGFloat = 2
static let RowHeight: CGFloat = 44
static let HeightPadding: CGFloat = 20
Expand Down
63 changes: 52 additions & 11 deletions DropDown/src/DropDown.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,21 @@ public final class DropDown: UIView {
*/
public var arrowIndicationX: CGFloat? {
didSet {
if let arrowIndicationX = arrowIndicationX {
tableViewContainer.addSubview(arrowIndication)
arrowIndication.tintColor = tableViewBackgroundColor
arrowIndication.frame.origin.x = arrowIndicationX
} else {
arrowIndication.removeFromSuperview()
}
guard let x = arrowIndicationX else { return }
arrowIndication.frame.origin.x = x
showArrowIndicator = true
}
}
public var showArrowIndicator: Bool = false {
didSet {
if showArrowIndicator {
tableViewContainer.addSubview(arrowIndication)
arrowIndication.tintColor = tableViewBackgroundColor
} else {
arrowIndication.removeFromSuperview()
}
}
}

//MARK: Constraints
fileprivate var heightConstraint: NSLayoutConstraint!
Expand All @@ -184,7 +190,7 @@ public final class DropDown: UIView {
@objc fileprivate dynamic var tableViewBackgroundColor = DPDConstant.UI.BackgroundColor {
willSet {
tableView.backgroundColor = newValue
if arrowIndicationX != nil { arrowIndication.tintColor = newValue }
if showArrowIndicator { arrowIndication.tintColor = newValue }
}
}

Expand Down Expand Up @@ -216,6 +222,16 @@ public final class DropDown: UIView {
willSet { tableView.separatorColor = newValue }
didSet { reloadAllComponents() }
}

/**
The separator inset

Changing the separator inset automatically reloads the drop down.
*/
@objc public dynamic var separatorInset = DPDConstant.UI.SeparatorInset {
willSet { tableView.separatorInset = newValue }
didSet { reloadAllComponents() }
}

/**
The corner radius of DropDown.
Expand Down Expand Up @@ -362,6 +378,14 @@ public final class DropDown: UIView {
reloadAllComponents()
}
}

public var cellClass: DropDownCustomCell.Type = DropDownCustomCell.self {
didSet {
tableView.register(cellClass, forCellReuseIdentifier: DPDConstant.ReusableIdentifier.DropDownCell)
templateCell = nil
reloadAllComponents()
}
}

//MARK: Content

Expand Down Expand Up @@ -538,6 +562,7 @@ private extension DropDown {

tableView.backgroundColor = tableViewBackgroundColor
tableView.separatorColor = separatorColor
tableView.separatorInset = separatorInset
tableView.layer.cornerRadius = cornerRadius
tableView.layer.masksToBounds = true
}
Expand Down Expand Up @@ -693,20 +718,25 @@ extension DropDown {
direction = .top
}
}
constraintWidthToFittingSizeIfNecessary(layout: &layout)
constraintWidthToBoundsIfNecessary(layout: &layout, in: window)

let visibleHeight = tableHeight - layout.offscreenHeight
let canBeDisplayed = visibleHeight >= minHeight

if showArrowIndicator {
arrowIndication.frame.origin.x = computeArrowIndicator(window: window,
layout: layout)
}

return (layout.x, layout.y, layout.width, layout.offscreenHeight, visibleHeight, canBeDisplayed, direction)
}

fileprivate func computeLayoutBottomDisplay(window: UIWindow) -> ComputeLayoutTuple {
var offscreenHeight: CGFloat = 0

let width = self.width ?? (anchorView?.plainView.bounds.width ?? fittingWidth()) - bottomOffset.x
let width = self.width ?? fittingWidth() - bottomOffset.x

let anchorViewX = anchorView?.plainView.windowFrame?.minX ?? window.frame.midX - (width / 2)
let anchorViewY = anchorView?.plainView.windowFrame?.minY ?? window.frame.midY - (tableHeight / 2)
Expand Down Expand Up @@ -749,16 +779,27 @@ extension DropDown {

return (x, y, width, offscreenHeight)
}

fileprivate func computeArrowIndicator(window: UIWindow, layout: ComputeLayoutTuple) -> CGFloat {
if let x = arrowIndicationX { return x }
guard let anchorViewX = anchorView?.plainView.windowFrame?.minX else {
return layout.width / 2
}
let anchorViewWidth = anchorView?.plainView.bounds.width ?? 0
let leftSpacing = max(anchorViewX - layout.x, 0)
return leftSpacing + anchorViewWidth/2
}

fileprivate func fittingWidth() -> CGFloat {
if templateCell == nil {
templateCell = (cellNib.instantiate(withOwner: nil, options: nil)[0] as! DropDownCell)
templateCell = (tableView.dequeueReusableCell(withIdentifier: DPDConstant.ReusableIdentifier.DropDownCell) as! DropDownCell)
}

var maxWidth: CGFloat = 0

for index in 0..<dataSource.count {
configureCell(templateCell, at: index)
templateCell.setSelected(true, animated: false)
templateCell.bounds.size.height = cellHeight
let width = templateCell.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).width

Expand Down
14 changes: 14 additions & 0 deletions DropDown/src/DropDownCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ open class DropDownCell: UITableViewCell {

}

open class DropDownCustomCell: DropDownCell {
var strongOptionLabel = UILabel(frame: .zero)

required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

override public init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
optionLabel = strongOptionLabel
contentView.addSubview(strongOptionLabel)
}
}

//MARK: - UI

extension DropDownCell {
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ dropDown.customCellConfiguration = { (index: Index, item: String, cell: DropDown

For a complete example, don't hesitate to check the demo app and code.

To avoid NIBs use
```swift
dropDown.cellClass = CellClassName.self
```
Where `CellClassName` is subclass of `DropDownCustomCell`.

### Events

```swift
Expand Down