UIKit cell animation with UIContentConfiguration - ios

I face into weird animation behaviour when I try to implement UIContentConfiguration for my UITableView.
I've implemented simple downscale animation on touch for my cell, but it doesn't work as expected. UILabel upscale first, and after that downscale.
I realized that animation behaviour related with updating configuration when touch began.
I've found that app has to call setNeedsUpdateConfiguration in animation block, but all my attempts have failed.
Code samples below:
ViewController.swift
class ViewController: UIViewController {
var array: [String] = ["Test 1", "Test 2", "Test 3"]
private lazy var tableView: UITableView = {
let table = UITableView()
table.register(MyCustomTableViewCell.self, forCellReuseIdentifier: "MyCustomTableViewCell")
table.dataSource = self
table.delegate = self
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tableView)
tableView.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "MyCustomTableViewCell", for: indexPath) as? MyCustomTableViewCell {
cell.configure(configuration: AnimatableContentViewConfiguration(text: array[indexPath.row]))
return cell
}
return UITableViewCell()
}
}
MyCustomCell.swift
class MyCustomTableViewCell: UITableViewCell {
func configure(configuration: AnimatableContentViewConfiguration) {
contentConfiguration = configuration
}
}
AnimatableContentViewConfiguration.swift
struct AnimatableContentViewConfiguration: UIContentConfiguration {
fileprivate var titleText: String = ""
init(text: String) {
titleText = text
}
func makeContentView() -> UIView & UIContentView {
AnimatableContentView(configuration: self)
}
func updated(for state: UIConfigurationState) -> AnimatableContentViewConfiguration {
self
}
}
AnimatableContentView.swift
final class AnimatableContentView: UIView, UIContentView {
private lazy var titleLabel: UILabel = {
UILabel(frame: .init(x: 5, y: 5, width: 100, height: 20))
}()
var configuration: UIContentConfiguration {
didSet {
if let configuration = configuration as? AnimatableContentViewConfiguration {
configure(configuration)
}
}
}
required init(configuration: UIContentConfiguration) {
self.configuration = configuration
super.init(frame: .zero)
addSubview(titleLabel)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(_ configuration: AnimatableContentViewConfiguration) {
titleLabel.text = configuration.titleText
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
highlight(true)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
highlight(false)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
highlight(false)
}
func highlight(_ touched: Bool) {
highlight(touched, completion: nil)
}
func highlight(_ touched: Bool, completion: ((Bool) -> Void)?) {
let scaleSize = 0.97
let transform = CGAffineTransform(scaleX: scaleSize, y: scaleSize)
UIView.animate(withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0.8,
options: [.allowUserInteraction, .curveEaseIn],
animations: {
self.transform = touched ? transform : .identity
}, completion: completion)
}
}
I'll appreciate for any help with my issue.

Related

dynamic collectionView in TableView

I have a dynamic collectionView in TableView and there are three errors:
the cells are not displayed correctly when the application starts
when I do inserItem the cells flicker as if they are rebooting
and most importantly when collectionView.insertItems the first four cells are hidden, the video
thanks
MainViewController
class ViewController: UIViewController, UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setting()
}
func setting(){
getData()
tableView.dataSource = self
tableView.delegate = self
tableView.estimatedRowHeight = UITableView.automaticDimension
tableView.rowHeight = UITableView.automaticDimension
}
//load data
func pagination(_ completion: (()->())?){
SmartNetworkSevrice.getGoods(with: url) { [unowned self] (data) in
guard data.modals.count > 0 else {
self.tableView.tableFooterView = nil
return
}
self.goods.append(contentsOf: data.modals)
self.offSet += data.modals.count
DispatchQueue.main.async {
let indexPath = IndexPath(row: 0, section: 0)
self.tableView.tableFooterView = nil
if self.goods.count == data.modals.count || self.isRefresh {
self.tableView.reloadRows(at: [indexPath], with: .none)
} else {
if let cell = self.tableView.cellForRow(at: indexPath) as? TVCellGoods {
UIView.performWithoutAnimation {
self.tableView.beginUpdates()
cell.insertGoods(data.modals)
cell.layoutIfNeeded()
cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
self.tableView.endUpdates()
}
}
}
completion?()
}
}
// define bottom of tableView
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard scrollView == self.tableView else { return }
if (!isMoreDataLoading) {
// Вычислить позицию длины экрана до нижней части результатов
let scrollViewContentHeight = scrollView.contentSize.height
let scrollOffsetThreshold = scrollViewContentHeight - scrollView.bounds.size.height
if(scrollView.contentOffset.y > scrollOffsetThreshold && scrollView.isDragging) {
isMoreDataLoading = true
self.tableView.isScrollEnabled = false;
self.tableView.isScrollEnabled = true;
pagination(nil)
}
}
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "goods", for: indexPath) as? TVCellGoods else { return UITableViewCell() }
guard bestGoods.count != 0, goods.count != 0 else { return UITableViewCell() }
cell.delegate = self
cell.configure(bestGoods, goods, categories)
// автообновление высоты
self.tableView.beginUpdates()
cell.collectionViewHeight.constant = cell.collectionView.collectionViewLayout.collectionViewContentSize.height
self.tableView.endUpdates()
return cell
}
}
extension ViewController: UITableViewDelegate, CallDelegate {
func callMethod() {}
func callMethod(push vc:UIViewController) {
self.navigationController?.pushViewController(vc, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
TableViewCell with collectionView
class TVCellGoods: UITableViewCell {
#IBOutlet weak var collectionView:UICollectionView!
#IBOutlet weak var collectionViewHeight:NSLayoutConstraint!
weak var delegate:CallDelegate?
var bestGoods = [Goods]() // лучшие товары
var goods = [Goods]() // все товары
var categories = [Menu]()
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.tag = 2
collectionView.isScrollEnabled = false
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configure(_ best:[Goods],_ goods:[Goods], _ category:[Menu]) {
self.bestGoods = best
self.goods = goods
self.categories = category
self.collectionView.reloadData()
}
func insertGoods(_ data:[Goods]) {
self.goods.append(contentsOf: data)
let count = self.bestGoods.count + self.categories.count + self.goods.count
let indexPaths = ((count - data.count) ..< count)
.map { IndexPath(row: $0, section: 0) }
self.collectionView.performBatchUpdates({
self.collectionView.insertItems(at: indexPaths)
}, completion: nil)
}
}
and CollectionViewCell
class CVCellGoods: UICollectionViewCell {
#IBOutlet weak var bgView: UIView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var price: UILabel!
#IBOutlet weak var delivery: UIImageView!
#IBOutlet weak var premium: UIImageView!
override func prepareForReuse() {
super.prepareForReuse()
delivery.image = nil
premium.image = nil
title.text = nil
price.text = nil
imageView.image = nil
imageView.sd_cancelCurrentImageLoad()
}
override func awakeFromNib() {
super.awakeFromNib()
imageView.backgroundColor = UIColor(red: 215/255, green: 215/255, blue: 215/255, alpha: 1)
self.contentView.layer.cornerRadius = 5
self.contentView.layer.masksToBounds = true
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize(width: 0, height: 0.5)
self.layer.shadowRadius = 1
self.layer.shadowOpacity = 0.3
self.layer.masksToBounds = false
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}
EDIT: this gives a small bug at startup
TVCellGoods.swift
override func layoutSubviews() {
super.layoutSubviews()
self.collectionView.collectionViewLayout.invalidateLayout()
}
I just figured out how to solve your problem.
You gonna have to invalidate your collection view layout, as you are using an collectionView inside a tableView just add the following method on TVCellGoods.swift
override func layoutSubviews() {
super.layoutSubviews()
self.collectionView.collectionViewLayout.invalidateLayout()
}
I tested it, and worked!!
see video
It's not a good idea to add a scroll view inside another scroll view!
Do you have an example project in order to help you?
the cells are not displayed correctly when the application starts
You calculate collectionView itemSize in ViewDidLoad,
but in ViewDidLoad TableView & TableviewCell width not equal ViewController.
override func viewDidLoad() {
super.viewDidLoad()
print(view.frame.size.width) //320
print(tableView.frame.size.width) //414
}
You can try reloadData in ViewDidAppear.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(view.frame.size.width) //320
print(tableView.frame.size.width) //320
tableView.reloadData()
}
when collectionView.insertItems the first four cells are hidden
Subclass UICollectionViewFlowLayout
class MyCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
TVCellGoods.swift
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.tag = 2
collectionView.isScrollEnabled = false
collectionView.collectionViewLayout = MyCollectionViewFlowLayout()
}

Navigation Bar Dropdown Button?

I would like a dropdown menu to drop once the user taps on the right button inside the Navigation Bar. My go to achieve this is as follow:
(1) Created a custom UIView class, which basically is going to contains the tableView to be displayed (dropped) when the user taps the Navigation Bar right button:
import UIKit
class DropdownUIView: UIView, UITableViewDelegate, UITableViewDataSource {
var dropdownTableSections = ["Ascending Order","Descending Order"]
var ascendingAndDescendingOrderSetionItmes = ["Section Designaion","Depth, h","Width, b","Area of Section, A"]
var dropdownTableView = UITableView()
override init(frame: CGRect) {
super.init(frame: frame)
dropdownTableView.delegate = self
dropdownTableView.dataSource = self
self.addSubview(dropdownTableView)
dropdownTableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
dropdownTableView.leftAnchor.constraint(equalTo: self.leftAnchor),
dropdownTableView.rightAnchor.constraint(equalTo: self.rightAnchor),
dropdownTableView.topAnchor.constraint(equalTo: self.topAnchor),
dropdownTableView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func numberOfSections(in tableView: UITableView) -> Int {
return dropdownTableSections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ascendingAndDescendingOrderSetionItmes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = ascendingAndDescendingOrderSetionItmes[indexPath.row]
return cell
}
}
(2) Created a custom UIButton Class, whereby I added to it the above dropdown view as a SubView. This is going to be the button to be added to the custom NavigationBar class as a UIView:
import UIKit
class DropdownUIButton: UIButton {
var dropdownView = DropdownUIView()
var dropdownViewHeight = NSLayoutConstraint()
var isOpen = false
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.darkGray
dropdownView = DropdownUIView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
dropdownView.translatesAutoresizingMaskIntoConstraints = false
self.setTitle("Sort by:", for: .normal)
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropdownView)
self.superview?.bringSubviewToFront(dropdownView)
dropdownView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
dropdownView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropdownView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
dropdownViewHeight = dropdownView.heightAnchor.constraint(equalToConstant: 0)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([self.dropdownViewHeight])
self.dropdownViewHeight.constant = 300
NSLayoutConstraint.activate([self.dropdownViewHeight])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseIn, animations: {
self.dropdownView.layoutIfNeeded()
self.dropdownView.center.y += self.dropdownView.frame.height / 2
}, completion: nil)
} else {
isOpen = false
NSLayoutConstraint.deactivate([self.dropdownViewHeight])
self.dropdownViewHeight.constant = 0
NSLayoutConstraint.activate([self.dropdownViewHeight])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseIn, animations: {
self.dropdownView.center.y -= self.dropdownView.frame.height / 2
self.dropdownView.layoutIfNeeded()
}, completion: nil)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
(3) Created a custom Navigation Bar class and added to it the custom UIButton created above as a UIView to be displayed as a rightBarButtonItem:
import UIKit
import ChameleonFramework
class CustomUINavigationBar: UINavigationBar {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience init(isNavBarTranslucent: Bool, navBarBackgroundColourHexCode: String, navBarBackgroundColourAlphaValue: CGFloat, navBarStyle: UIBarStyle, preferLargeTitles: Bool, navBarDelegate: UINavigationBarDelegate, navBarItemsHexColourCode: String) {
self.init()
addNavBarRightButton()
setupNavigationBarEssentials(isNavBarTranslucent: isNavBarTranslucent, navBarBackgroundColourHexCode: navBarBackgroundColourHexCode, navBarBackgroundColourAlphaValue: navBarBackgroundColourAlphaValue, navBarStyle: navBarStyle, preferLargeTitles: preferLargeTitles, navBarDelegate: navBarDelegate, navBarItemsHexColourCode: navBarItemsHexColourCode)
}
let customNavigationBarItem = UINavigationItem()
func addNavBarRightButton() {
let button = DropdownUIButton()
let navigationBarRightButtonView: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.addSubview(button)
return view
}()
NSLayoutConstraint.activate([
button.topAnchor.constraint(equalTo: navigationBarRightButtonView.topAnchor),
button.rightAnchor.constraint(equalTo: navigationBarRightButtonView.rightAnchor),
button.leftAnchor.constraint(equalTo: navigationBarRightButtonView.leftAnchor),
button.bottomAnchor.constraint(equalTo: navigationBarRightButtonView.bottomAnchor)
])
let navigationBarRightViewitem = UIBarButtonItem(customView: navigationBarRightButtonView)
customNavigationBarItem.rightBarButtonItem = navigationBarRightViewitem
}
func setupNavigationBarEssentials(isNavBarTranslucent: Bool, navBarBackgroundColourHexCode: String, navBarBackgroundColourAlphaValue: CGFloat, navBarStyle: UIBarStyle, preferLargeTitles: Bool, navBarDelegate: UINavigationBarDelegate, navBarItemsHexColourCode: String) {
items = [customNavigationBarItem]
isTranslucent = isNavBarTranslucent
barTintColor = UIColor(hexString: navBarBackgroundColourHexCode, withAlpha: navBarBackgroundColourAlphaValue)
barStyle = navBarStyle
prefersLargeTitles = preferLargeTitles
delegate = navBarDelegate
tintColor = UIColor(hexString: navBarItemsHexColourCode)
translatesAutoresizingMaskIntoConstraints = false
}
}
(4) Added the above custom NavigationBar to the ViewController class I am interested in as below:
import UIKit
class tableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UINavigationBarDelegate {
lazy var navigationBar = CustomUINavigationBar(isNavBarTranslucent: false, navBarBackgroundColourHexCode: "#FFFFFF", navBarBackgroundColourAlphaValue: 1.0, navBarStyle: .black, preferLargeTitles: false, navBarDelegate: self, navBarItemsHexColourCode: "#FF4F40")
lazy var tableView = CustomTableView(tableViewBackgroundColourHexCode: "#0D0D0D", tableViewDelegate: self, tableViewDataSource: self, tableViewCustomCellClassToBeRegistered: IsectionsCustomTableViewCell.self, tableViewCustomCellReuseIdentifierToBeRegistered: "customCell")
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(navigationBar)
view.addSubview(tableView)
}
override func viewDidLayoutSubviews() {
setupConstraints()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell") as! IsectionsCustomTableViewCell
cell.sectionDesignationLabel.text = "Number 1"
return cell
}
func position(for bar: UIBarPositioning) -> UIBarPosition {
return UIBarPosition.topAttached
}
func setupConstraints() {
NSLayoutConstraint.activate([
navigationBar.leftAnchor.constraint(equalTo: view.leftAnchor),
navigationBar.rightAnchor.constraint(equalTo: view.rightAnchor),
navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.topAnchor.constraint(equalTo: navigationBar.bottomAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
tableView.rightAnchor.constraint(equalTo: view.rightAnchor)
])
}
}
The button gets added to the NavigationBar on its right hand side as I can see the NavigationBar and the Button. However, however, when I click on the button, nothing happens, the tableview does not get dropped down and up as I expected. Any idea where did I go wrong, and are there any CocoaPods that you can recommend to achieve something similar?
You can simply use this library to achieve drop d https://github.com/AssistoLab/DropDown

Can't not check button title from programmatically DropDown button

I am new in Swift and my english is not very well
The question is i build a dropdown view from this tutorial
youtube: https://youtu.be/22zu-OTS-3M
github: https://github.com/Archetapp/Drop-Down-Menu
He programmatically create a fantastic dropdown view without storyboard
the code run very good on my Xcode
but when I went to check the title with the button
I never get it.
this my code
class ViewController: UIViewController, GMSMapViewDelegate
override func viewDidLoad()
super.viewDidLoad()
let typeBTN = dropDownBtn()
self.view.addSubview(typeBTN)
typeBTN.setTitle("animal", for: .normal)
typeBTN.dropView.dropDownOptions = ["dog", "cat", "cow", "boy"]
// ....
protocol dropDownProtocol {
func dropDownPressed(string: String)
}
class dropDownBtn: UIButton, dropDownProtocol {
func dropDownPressed(string: String) {
self.setTitle(string, for: .normal)
self.dismissDropDown()
}
var dropView = dropDownView()
var height = NSLayoutConstraint()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
dropView = dropDownView.init(frame: CGRect.init(x: 0, y: 0, width: 0, height: 0))
dropView.delegate = self
dropView.translatesAutoresizingMaskIntoConstraints = false
}
override func didMoveToSuperview() {
self.superview?.addSubview(dropView)
self.superview?.bringSubviewToFront(dropView)
dropView.topAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
dropView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
dropView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
height = dropView.heightAnchor.constraint(equalToConstant: 0)
}
var isOpen = false
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([self.height])
if self.dropView.tableView.contentSize.height > 150 {
self.height.constant = 170
} else {
self.height.constant = self.dropView.tableView.contentSize.height
}
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseOut, animations: {
self.dropView.layoutIfNeeded()
self.dropView.center.y += self.dropView.frame.height / 2
}, completion: nil)
}else{ dismissDropDown() }
}
func dismissDropDown() {
isOpen = false
NSLayoutConstraint.deactivate([self.height])
self.height.constant = 0
NSLayoutConstraint.activate([self.height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
}, completion: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class dropDownView: UIView, UITableViewDelegate, UITableViewDataSource {
var dropDownOptions = [String]()
var tableView = UITableView()
var delegate: dropDownProtocol!
override init(frame: CGRect) {
super.init(frame: frame)
tableView.backgroundColor = UIColor.darkGray
tableView.delegate = self
tableView.dataSource = self
self.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dropDownOptions.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = dropDownOptions[indexPath.row]
cell.backgroundColor = UIColor.darkGray
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate.dropDownPressed(string: dropDownOptions[indexPath.row])
print(dropDownOptions[indexPath.row])
I have tried a lot to check when button title change. like this
print(typeBTN.currentTitle!)
print(typeBTN.titleLabel?.text! )
print(typeBTN.title(for: .normal))
//I get nothing when i pressed dropdown menu options?
because I want to do this
if typeBTN.currentTitle == "dog" {
//show some date at my mapview
}
but when I click the "dog" of dropdown menu
it never work
can somebody help me?
You can use the Delegation pattern to notify the title changes to your view controller.
Add the protocol:
protocol DropDownButtonDelegate: AnyObject {
func titleDidChange(_ newTitle: String)
}
Add in your DropDownButton class a delegate property:
weak var delegate: DropDownButtonDelegate?
Then in your dropDownPressed function:
func dropDownPressed(string: String) {
self.setTitle(string, for: .normal)
self.dismissDropDown()
delegate?.titleDidChange(string)
}
And finally in your view controller implement the DropDownButtonDelegate:
class ViewController: UIViewController, GMSMapViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let typeBTN = dropDownBtn()
self.view.addSubview(typeBTN)
typeBTN.delegate = self
typeBTN.setTitle("animal", for: .normal)
typeBTN.dropView.dropDownOptions = ["dog", "cat", "cow", "boy"]
}
}
extension ViewController: DropDownButtonDelegate {
func titleDidChange(_ newTitle: String) {
print(newTitle)
}
}

viewmodel in app delegate ion mmvm in swift

I am doing in mvvm .
my code as below:-
private var url = URL(string: "http://www.example.com")!
typealias JSONDictionary = [String:Any]
class Webservice{
static func fetchHostels(completion: #escaping ([JSONDictionary]) -> ()) {
URLSession.shared.dataTask(with: url) { data, _, _ in
if let data = data {
let json = try! JSONSerialization.jsonObject(with: data, options: [])
let dictionaries = json as! [JSONDictionary]
completion(dictionaries)
}
}.resume()
}
}
My hostelmodel:-
class hostelmodel: NSObject {
var name: String?
var city: String?
init(_ dictionary: [String: Any]) {
if let name = dictionary["name"] as? String, let location = dictionary["location"] as? [String: Any], let city = location["city"] as? String {
self.name = name
self.city = city
}
}
}
my hostelviewmodel:-
class hostelviewmodel: NSObject {
private var hostels: [hostelmodel] = []
func fetchData(completion: (() -> Void)?) {
Webservice.fetchHostels { [weak self] dictionaries in
self?.hostels = dictionaries.flatMap(hostelmodel.init)
completion?()
}
}
func numberOfSections() -> Int {
//your number of section
return 1
}
func numberOfRows() -> Int {
//your number of rows
return hostels.count
}
func hostel(atIndex index: Int) -> hostelmodel {
return hostels[index]
}
}
my hostellist:-
#IBOutlet private weak var tableView: UITableView!
private var viewModel: hostelviewmodel
init(hostelViewModel: hostelviewmodel) {
self.viewModel = hostelViewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
private func fetchData() {
viewModel.fetchData { [weak self] in
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return viewModel.numberOfSections()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.numberOfRows()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)as! QM_RestaurtantCell
cell.setRestaurtantData(restaurtant: QM_RestaurtantModel)
return cell
}
}
}
my hosteltablecell:-
#IBOutlet weak var name: UILabel!
#IBOutlet weak var city: UILabel!
func setRestaurtantData(restaurtant:hostelmodel)
{
self.name.text = restaurtant.name
self.city.text = restaurtant.city
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I am doing without storyboard, so here how to set in the appdelegate. What should I set as root view controller in app delegate class. And also how the name and city value display in the tableview cell for row index in swift.How to deo
For Migrating to App Delegate to your HostelList View Controller :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Transferring Control to First Controller
window = UIWindow(frame: UIScreen.main.bounds)
let nav1 = UINavigationController()
let vc = HostelList()
nav1.viewControllers = [vc]
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}
To set your values in tableview, Use this function :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
}
Ok. Try this refactored code pls:
AppDelegate didFinish:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let vm = HostelViewModel()
let vc = HostelViewController(hostelViewModel: vm)
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}
HostelViewController:
class HostelViewController: UIViewController {
private let tableView = UITableView(frame: .zero, style: .plain)
private var viewModel: HostelViewModel
init(hostelViewModel: HostelViewModel) {
self.viewModel = hostelViewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
createUI()
tableView.dataSource = viewModel
fetchData()
}
func createUI() {
tableView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(tableView)
tableView.register(HostelCell.self, forCellReuseIdentifier: cellIdentifier)
tableView.estimatedRowHeight = 60
tableView.rowHeight = UITableViewAutomaticDimension
let tableViewConstraints: [NSLayoutConstraint] = [
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
]
NSLayoutConstraint.activate(tableViewConstraints)
}
private func fetchData() {
viewModel.fetchData { [weak self] in
DispatchQueue.main.async {
self?.tableView.reloadData()
}
}
}
}
HostelViewModel:
class HostelViewModel: NSObject, UITableViewDataSource {
private var hostels: [HostelModel] = []
func fetchData(completion: (() -> Void)?) {
Webservice.fetchHostels { [weak self] dictionaries in
self?.hostels = dictionaries.flatMap(HostelModel.init)
completion?()
}
}
func hostel(atIndex index: Int) -> HostelModel {
return hostels[index]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return hostels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let hostel = hostels[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! HostelCell
cell.setRestaurtantData(restaurtant: hostel)
return cell
}
}
HostelCell file:
let cellIdentifier = "HostelTableCell"
class HostelCell: UITableViewCell {
private var name: UILabel = UILabel()
private var city: UILabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
createUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setRestaurtantData(restaurtant: HostelModel)
{
self.name.text = restaurtant.name
self.city.text = restaurtant.city
}
func createUI() {
[city, name].forEach {
contentView.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = true
}
city.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8).isActive = true
city.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
name.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8).isActive = true
name.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
HostelModel:
class HostelModel {
var name: String?
var city: String?
init(_ dictionary: [String: Any]) {
if let name = dictionary["name"] as? String, let location = dictionary["location"] as? [String: Any], let city = location["city"] as? String {
self.name = name
self.city = city
}
}
}

Swift custom UIView IBOutlet nil , awakeFromNib() never called

I am trying to custom a UIView but when I call it, the outlets of custom view always nil, and awakeFromNib() never called, too. Here my CustomView:
class DropdownMenuView: UIView {
//MARK: Outlets
#IBOutlet var view: DropdownMenuView!
#IBOutlet weak var fadeView: UIView!
#IBOutlet weak var tableView: UITableView!
var selection:Selection = .Default
var iconType:IconType = .Default
var menuTitles:[String] = []
var cellHeight:CGFloat = 44
var textColorNomal:UIColor = UIColor.blackColor()
var textColorHighlight:UIColor = UIColor.redColor()
var isShown: Bool!
//MARK: Lifecycle
override func awakeFromNib() {
print("awakeFromNib")
super.awakeFromNib()
initSubviews()
configView()
}
override func layoutSubviews() {
super.layoutSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
func initSubviews() {
if let nibsView = NSBundle.mainBundle().loadNibNamed("DropdownMenuView", owner: self, options: nil) as? [UIView] {
let nibRoot = nibsView[0]
self.addSubview(nibRoot)
nibRoot.frame = self.bounds
}
}
required init(uSelection: Selection = Selection.Default, uIconType:IconType = IconType.Default, uMenuTitles:[String]) {
self.selection = uSelection
self.iconType = uIconType
self.menuTitles = uMenuTitles
super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
}
private func configView() {
switch (selection) {
case Selection.Multiple:
tableView.allowsMultipleSelection = true
default:
tableView.allowsMultipleSelection = false
}
tableView.registerNib(UINib(nibName: "DropdownMenuCell", bundle: nil), forCellReuseIdentifier: "DropdownMenuCell")
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
hideMenu()
}
internal func show() {
if self.isShown == false {
self.showMenu()
}
}
internal func hide() {
if self.isShown == true {
self.hideMenu()
}
}
func showMenu() {
self.isShown = true
if let app = UIApplication.sharedApplication().delegate as? AppDelegate, let window = app.window {
window.bringSubviewToFront(self)
}
// Animation
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.alpha = 1
})
}
func hideMenu() {
self.isShown = false
// Animation
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.alpha = 0
})
}
}
//MARK: UITableViewDelegate methods
extension DropdownMenuView: UITableViewDelegate {
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat(menuTitles.count) * cellHeight
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return cellHeight
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("DropdownMenuCell") as! DropdownMenuCell
cell.lblTitle.text = menuTitles[indexPath.row]
cell.iconType = iconType
return cell
}
}
//MARK: UITableViewDataSource methods
extension DropdownMenuView: UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return menuTitles.count
}
}
I tried to remove outlets and connect again, but I wasn't lucky. It isn't working anymore. You can check my full demo project here:
https://www.dropbox.com/s/5udbl2kn9vnwjar/ModuleDemo.zip?dl=0
You click on "Label" on StartVC to call custom UIView.
I hope what I ask is possible. Any helps always are appropriate!

Resources