UITextField inside UIColletionViewCell becomeFirstResponder - swift

I know this question might have a lot of answers on SO. But after trying every solution found on the interweb (+ some of my custom inventions ..) I still can't do what I want to achieve.
Here is the story :
I have a UICollectionViewCell with a Subclass of a UITextField embeded in it.
Here is my Subclass :
class CustomTextField: UITextField {
private let padding = UIEdgeInsets(top: 6.0, left: 0.0, bottom: 0.0, right: 2.0)
private lazy var lineView: UIView = {
let lineView = UIView()
lineView.translatesAutoresizingMaskIntoConstraints = false
lineView.isUserInteractionEnabled = false
lineView.frame.size.height = 2
lineView.backgroundColor = UIColor.tiara
return lineView
}()
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: padding)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func toggleColors() {
if isFirstResponder {
lineView.backgroundColor = .black
} else {
lineView.backgroundColor = UIColor.tiara
}
}
}
private extension CustomTextField {
func commonInit() {
addSubview(lineView)
constraintLineView()
textColor = UIColor.tiara
}
func constraintLineView() {
lineView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
lineView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
lineView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
lineView.heightAnchor.constraint(equalToConstant: 2.0).isActive = true
}
}
And here is the code I use in my UICollectionViewCell :
#discardableResult
func setFirstResponder() -> Bool {
return customTextField.becomeFirstResponder()
}
func endEditing() {
customTextField.resignFirstResponder()
}
The result of customTextField.becomeFirstResponder is always false.
It's called from my UIViewController :
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard indexPath.row != 0 else { return }
dispatchService.stop()
let topIndexPath = IndexPath(item: 0, section: 0)
let topCell: Cell = collectionView.dequeueReusableCell(for: topIndexPath)
topCell.endEditing()
service.data.rearrange(from: indexPath.row, to: 0)
update()
collectionView.performBatchUpdates({
collectionView.moveItem(at: indexPath, to: topIndexPath)
collectionView.scrollToItem(at: topIndexPath, at: .top, animated: true)
}) { (_) in
let secondCell: Cell = collectionView.dequeueReusableCell(for: topIndexPath)
secondCell.setFirstResponder()
self.dispatchService.reset()
}
}
I really don't know where to start, this is the last solution I came with and it stills stays without any keyboard displayed.
I am working on a real device, iPhone X iOS 12.1.

I may not have the quiet right answer but, I think the problem comes from the way you are getting your cell inside collectionView(didSelectItemAt:).
You are using the dequeueReusableCell instead of using cellForItem(at:) for getting your cells. So you are creating a reusable cell and not getting the one you are interested in.

Related

SWIFT UIPanGestureRecognizer failed only first time

My test app have 4 ScrollView.
UIScrollView which can scroll up/down.
UIPageViewController inside 1, which can scroll left/right.
UITableView inside one of viewController 2, can scroll up/down.
UIScrollView inside UITableViewCell on tableView 3, can scroll left/right.
https://i.stack.imgur.com/BexX3.jpg
First swipe gesture by UIScrollView 4 causes PageViewController 2 scroll instead. Second and more swipes work correct.
PanGestureRecognizer of UIScrollView 4 has a state "failed" on the first swipe.
<UIScrollViewPanGestureRecognizer: 0x7f826881fd50; state = Failed; delaysTouchesEnded = NO; view = <Tests.MyCellScroll 0x7f826802f600>; target= <(action=handlePan:, target=<Tests.MyCellScroll 0x7f826802f600>)>>
Any ideas what’s the problem or how could it be debugged?
Example
class ViewController: UIViewController {
private let vcs: [UIViewController] = [
MyVC(),
UIViewController()
]
private lazy var scroll: UIScrollView = {
let scroll = UIScrollView()
scroll.backgroundColor = .systemPink
return scroll
}()
private lazy var pageVC: UIPageViewController = {
let page = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
page.view.backgroundColor = .white
page.delegate = self
page.dataSource = self
return page
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.scroll)
self.addChild(self.pageVC)
self.scroll.addSubview(self.pageVC.view)
self.pageVC.didMove(toParent: self)
self.pageVC.setViewControllers([self.vcs[0]], direction: .forward, animated: true, completion: nil)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.scroll.frame = self.view.frame
self.scroll.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 50)
self.pageVC.view.frame = CGRect(x: self.scroll.bounds.minX, y: self.scroll.bounds.minY + 250, width: self.scroll.bounds.width, height: self.scroll.contentSize.height - 250)
}
}
extension ViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
self.vcs.firstIndex(of: viewController) == 1 ? self.vcs[0] : nil
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
self.vcs.firstIndex(of: viewController) == 0 ? self.vcs[1] : nil
}
}
final class MyVC: UIViewController {
private lazy var table: UITableView = {
let table = UITableView()
table.register(MyCell.self, forCellReuseIdentifier: String(describing: MyCell.self))
table.delegate = self
table.dataSource = self
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.table)
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.table.frame = self.view.frame
}
}
extension MyVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 5 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { MyCell() }
}
final class MyCell: UITableViewCell {
private lazy var subview: UIView = {
let view = UIView()
view.backgroundColor = UIColor.systemBlue.withAlphaComponent(0.7)
return view
}()
private lazy var scrollView: MyCellScroll = {
let view = MyCellScroll()
return view
}()
init(){
super.init(style: .default, reuseIdentifier: nil)
self.contentView.backgroundColor = UIColor.systemOrange.withAlphaComponent(0.7)
self.contentView.addSubview(self.scrollView)
self.scrollView.addSubview(self.subview)
}
override func layoutSubviews() {
super.layoutSubviews()
let frame = self.contentView.frame
self.scrollView.frame = frame
self.scrollView.contentSize = CGSize(width: 500, height: frame.height)
self.subview.frame = CGRect(x: 16, y: 8, width: 500 - 32, height: frame.height - 16)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}
final class MyCellScroll: UIScrollView, UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
print(gestureRecognizer)
return false // 5
}
}

Trying to create new NSTextView every time the attributed string exceeds a certain height?

I'm trying to add a new NSTextView to the last index in my collection view every time the attributed string exceeds a certain bounds. The code works perfectly until the 5th item then it starts its starts creating an item every time the enter button is pressed. I'm thinking its a bug but im not sure. if any one can show me a better way to do it or improve the current code I have I would appreciate it. Below is my code:
Here is the CollectionViewItem
class DocumentItem: NSCollectionViewItem {
var itemView: DocumentTextView?
override func viewDidLoad() {
super.viewDidLoad()
self.itemView?.wantsLayer = true
// Do view setup here.
}
override func loadView() {
self.itemView = DocumentTextView(frame: NSZeroRect)
self.view = self.itemView!
}
func getView() -> DocumentTextView {
return self.itemView!
}
}
Here is the collectionView datasource
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return DocList.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "DocumentItem"), for: indexPath)
return item
}
Here is the NSTextView subclass
class DocumentTextView: NSTextView {
var theContainer = NSTextContainer()
var theStorage = NSTextStorage()
var theManager = NSLayoutManager()
var table = NSTextTable()
var pdfPage: PDFPage?
override init(frame frameRect: NSRect) {
super.init(frame: NSRect(origin: frameRect.origin, size: NSSize(width: 800, height: 1131 )), textContainer: theContainer)
theStorage.addLayoutManager(theManager)
theManager.addTextContainer(theContainer)
self.textContainerInset = CGSize(width: 50, height: 50)
self.textContainer?.widthTracksTextView = true
self.textContainer?.heightTracksTextView = true
self.textContainer?.lineBreakMode = .byWordWrapping
self.maxSize = NSSize(width: 800, height: 1131)
self.backgroundColor = NSColor.fromHexString("ffffff")!
self.isRichText = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is the function bringing the bug
func textDidChange(_ notification: Notification) {
var textView = notification.object as? DocumentTextView
let numberOfItems = theDocumentOutlineView.numberOfItems(inSection: 0)
let theLastTextView = theDocumentOutlineView.item(at: numberOfItems - 1) as! DocumentItem
if textView == theLastTextView.itemView {
print(textView?.attributedString().size())
if (textView?.attributedString().size().height)! >= 1106.0 {
self.DocList.append(2)
var set = Set<IndexPath>()
set.insert(NSIndexPath(forItem: self.DocList.count - 1 , inSection: 0) as IndexPath)
theDocumentOutlineView.insertItems(at: set)
theDocumentOutlineView.scrollToItems(at: set, scrollPosition: NSCollectionView.ScrollPosition.top)
var newFirstResponder = theDocumentOutlineView.item(at: self.DocList.count - 1) as! DocumentItem
newFirstResponder.itemView?.delegate = self
self.view.window?.makeFirstResponder(newFirstResponder.itemView)
}
}
}
Here's my test project, maybe it helps. The delegate of the text view is its view controller, the NSCollectionViewItem. The view controller of the collection view also receives NSText.didChangeNotification notifications to check the length of the text. heightTracksTextView of the text container is false.
ViewController:
class ViewController: NSViewController, NSCollectionViewDataSource, NSCollectionViewDelegate {
#IBOutlet weak var collectionView: NSCollectionView!
var docList: [DocumentObject] = [DocumentObject(index: 0, string: NSAttributedString(string: "New 0"))]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(DocumentItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier("DocumentItem"))
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange),
name: NSText.didChangeNotification, object: nil)
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return docList.count
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
if let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier("DocumentItem"),
for: indexPath) as? DocumentItem {
item.representedObject = docList[indexPath.item]
return item
}
return NSCollectionViewItem()
}
#objc func textDidChange(_ notification: Notification) {
if let textView = notification.object as? NSTextView {
if let theLastItem = self.collectionView.item(at: self.docList.count - 1) as? DocumentItem,
textView === theLastItem.itemView {
//if textView.attributedString().size().height >= 1106.0 {
print("\(textView.attributedString().size().height) \(textView.layoutManager!.usedRect(for: textView.textContainer!).size.height)")
if let textContainer = textView.textContainer,
let heigth = textView.layoutManager?.usedRect(for: textContainer).size.height,
heigth >= 1106.0 {
DispatchQueue.main.async {
if let window = self.view.window,
window.makeFirstResponder(nil) { // end editing of previous item
self.docList.append(DocumentObject(index: self.docList.count - 1, string: NSAttributedString(string: "New \(self.docList.count)")))
let set: Set = [IndexPath(item: self.docList.count - 1, section: 0)]
self.collectionView.insertItems(at: set)
self.collectionView.scrollToItems(at: set, scrollPosition: NSCollectionView.ScrollPosition.top)
if let newItem = self.collectionView.item(at: self.docList.count - 1) as? DocumentItem {
window.makeFirstResponder(newItem.itemView)
}
}
}
}
}
}
}
}
DocumentItem:
class DocumentItem: NSCollectionViewItem, NSTextViewDelegate {
var itemView: DocumentTextView?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
override func loadView() {
self.itemView = DocumentTextView(frame: NSZeroRect)
self.view = self.itemView!
self.itemView?.delegate = self
}
override var representedObject: Any? {
didSet {
if let item = representedObject as? DocumentObject {
itemView?.textStorage?.setAttributedString(item.string)
}
}
}
func textDidEndEditing(_ notification: Notification) {
if let item = representedObject as? DocumentObject {
item.string = itemView?.textStorage?.copy() as! NSAttributedString
}
}
}
DocumentObject:
class DocumentObject {
var index: Int
var string: NSAttributedString
init(index: Int, string: NSAttributedString) {
self.index = index
self.string = string
}
}

Unable to resize cells with custom UICollectionViewFlowLayout

I'm currently trying to implement custom divider views in between each cell of my collection view. I found this answer online that adds the custom view in the inter-line spacing (link).
private let separatorDecorationView = "separator"
final class CustomFlowLayout: UICollectionViewFlowLayout {
override init() {
super.init()
register(SeparatorView.self,
forDecorationViewOfKind: separatorDecorationView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes = super.layoutAttributesForElements(in: rect) ?? []
let lineWidth = self.minimumLineSpacing
var decorationAttributes: [UICollectionViewLayoutAttributes] = []
// skip first cell
for layoutAttribute in layoutAttributes where layoutAttribute.indexPath.item > 0 {
let separatorAttribute = UICollectionViewLayoutAttributes(forDecorationViewOfKind: separatorDecorationView,
with: layoutAttribute.indexPath)
let cellFrame = layoutAttribute.frame
separatorAttribute.frame = CGRect(x: cellFrame.origin.x,
y: cellFrame.origin.y - lineWidth,
width: cellFrame.size.width,
height: lineWidth)
separatorAttribute.zIndex = Int.max
decorationAttributes.append(separatorAttribute)
}
return layoutAttributes + decorationAttributes
}
}
private final class SeparatorView: UICollectionReusableView {
private let imageView: UIImageView = {
let iv = UIImageView(image: UIImage(named: "cell-divider"))
iv.contentMode = .scaleAspectFit
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(imageView)
imageView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
imageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
self.frame = layoutAttributes.frame
}
}
This solution actually works, and I'm able to see the dividers. The problem arises, however, when the user clicks on one of the cells. The behavior I want is for the cell to expand to show more details when the cell is clicked. The way I'm implementing this is by keeping track of which indexPaths are selected, and returning a larger size if they are selected in sizeForItemAt. In didSelectItemAt, I reload the collection view. This approach works when I'm using the normal UICollectionViewFlowLayout, but when I try using my custom flow layout (above), I get the following crash:
no UICollectionViewLayoutAttributes instance for -layoutAttributesForDecorationViewOfKind: separator at path <NSIndexPath: 0xf75c5b66b8a0a8ab> {length = 2, path = 0 - 6}
I tried looking up solutions and found these two stack overflows here and here but none of the answers I tried seemed to work.
I tried:
Invalidating the layout when I reload the collection view.
Implementing a cache that I return from when I override layoutAttributesForItem in my custom layout.
Any help would be greatly appreciated at this point!
It seems that I had to overwrite two methods in my custom layout:
override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAttributes = UICollectionViewLayoutAttributes(forDecorationViewOfKind: elementKind,
with: indexPath)
return layoutAttributes;
}
override func initialLayoutAttributesForAppearingDecorationElement(ofKind elementKind: String, at decorationIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = layoutAttributesForDecorationView(ofKind: elementKind,
at: decorationIndexPath)
return attributes
}
I also needed to keep the layout invalidation call when I reloaded the data.
collectionView.reloadData()
let context = collectionViewLayout.invalidationContext(forBoundsChange: bounds)
context.contentOffsetAdjustment = CGPoint.zero
collectionView.collectionViewLayout.invalidateLayout(with: context)
layoutSubviews()

TextField rejected resignFirstResponder when being removed from hierarchy

I'm trying to make a custom textfield/sendbutton view that overrides inputAccessoryView, but I'm having 2 problems I can't seem to solve:
When the custom view becomes the first responder, I get this warning twice:
CustomKeyboardProject[5958:3107074] API error:
<_UIKBCompatInputView: 0x119e2bd70; frame = (0 0; 0 0);
layer = <CALayer: 0x283df9e00>> returned 0 width,
assuming UIViewNoIntrinsicMetric
Then when I try to resign the custom view as the first responder, Xcode throws this warning:
CustomKeyboardProject[5958:3107074] -[UIWindow
endDisablingInterfaceAutorotationAnimated:] called on <UITextEffectsWindow:
0x11a055400; frame = (0 0; 375 667); opaque = NO; autoresize = W+H; layer =
<UIWindowLayer: 0x283df78a0>> without matching
-beginDisablingInterfaceAutorotation. Ignoring.
Has anyone been able to silence these warnings?
Heres my code:
protocol CustomTextFieldDelegate: class {
func sendMessage()
}
class CustomTextField: UITextField {
override var canBecomeFirstResponder: Bool {
return true
}
override var canResignFirstResponder: Bool {
return true
}
}
class CustomKeyboardView: UIView {
let textField: CustomTextField = {
let textField = CustomTextField()
textField.backgroundColor = .green
textField.textColor = .white
return textField
}()
let attachButton: UIButton = {
let button = UIButton()
button.backgroundColor = .red
button.setTitle("Attach", for: .normal)
return button
}()
let sendButton: UIButton = {
let button = UIButton()
button.backgroundColor = .blue
button.setTitle("Send", for: .normal)
button.addTarget(self, action: #selector(sendMessage), for: .touchUpInside)
return button
}()
#objc func sendMessage() {
delegate?.sendMessage()
}
weak var delegate: CustomTextFieldDelegate?
init() {
super.init(frame: .zero)
isUserInteractionEnabled = true
setViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setViews() {
let subviews = [attachButton, sendButton, textField]
subviews.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
addSubview($0)
}
autoresizingMask = .flexibleHeight
subviews.forEach {
NSLayoutConstraint.activate([
$0.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
layoutSubviews()
[attachButton, sendButton].forEach {
NSLayoutConstraint.activate([
$0.widthAnchor.constraint(equalTo: $0.heightAnchor),
$0.heightAnchor.constraint(equalTo: textField.heightAnchor)
])
}
NSLayoutConstraint.activate([
textField.topAnchor.constraint(equalTo: topAnchor),
attachButton.leftAnchor.constraint(equalTo: leftAnchor),
textField.leadingAnchor.constraint(equalTo: attachButton.trailingAnchor),
sendButton.leadingAnchor.constraint(equalTo: textField.trailingAnchor),
sendButton.trailingAnchor.constraint(equalTo: trailingAnchor),
])
}
override var intrinsicContentSize: CGSize {
return .zero
}
}
class CustomTableView: UITableView {
let keyboardView = CustomKeyboardView()
override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style)
keyboardDismissMode = .interactive
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var canBecomeFirstResponder: Bool {
return true
}
override var canResignFirstResponder: Bool {
return true
}
override var inputAccessoryView: UIView? {
return keyboardView
}
}
private let reuseId = "MessageCellId"
class ExampleViewController: UITableViewController {
private let customTableView = CustomTableView()
var messages = [String]()
override func loadView() {
tableView = customTableView
view = tableView
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.becomeFirstResponder()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: reuseId)
customTableView.keyboardView.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.async {
self.customTableView.keyboardView.textField.becomeFirstResponder()
}
}
// tableView delegate methods
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseId)
cell?.textLabel?.text = messages[indexPath.row]
return cell!
}
override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
customTableView.keyboardView.textField.resignFirstResponder()
}
}
extension ExampleViewController: CustomTextFieldDelegate {
func sendMessage() {
// double check something is in textField
let textField = customTableView.keyboardView.textField
guard let body = textField.text, body != "" else { return }
messages.append(body)
tableView.reloadData()
view.endEditing(true)
customTableView.keyboardView.textField.text = ""
}
}

UITableViewCell with Custom View

i am desperately trying to add a custom View to a UITableViewCell. I have a UITableViewController in Storyboard wich is linked to a TestTableViewController Class and the prototype cell has the identifier "Cell2".
So this is the code in UITableViewController:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return dataArray.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 190
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell2", forIndexPath: indexPath)
let data = dataArray[indexPath.row]
let ticket = YellowTackleTicketComplete()
ticket.frame = CGRect(x: 20, y: 20, width: 335, height: 150)
cell.addSubview(ticket)
// cell.textLabel!.text = tackl!.valueForKey("title") as? String
cell.backgroundColor = UIColor.clearColor()
print(cell.subviews)
return cell
}
The dataArray is just for testing purposes and has one item in it. So what i get is a Table View with one empty cell. In the Xcode UI Debugger i can see the custom view. But its not shown in the simulator or on a device.
If anyone can help, thank you so much!
This is the code of the view:
class YellowTackleTicketComplete: UIView {
var containerView: UIView!
var ticket: TacklTicket!
var dropDownMenu: YellowDrawer!
var dropDownBackground: YellowDrawerBackground!
var ticketShadowLine: UIView!
var lowAlphaView: UIView!
var outbackButton: UIButton!
var arrowView: UIView!
var bounceHeight: CGFloat?
var dropdownHeight: CGFloat?
var topBorder: CGFloat?
var bottomBorder: CGFloat?
//let arrowLayer = CALayer()
//let layerDelegate = LayerDelegate()
var dropDownElements = [String]()
var dropped = false
var animating = false
var delegate: YellowTackleTicketCompleteDelegate?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addObserver(self, forKeyPath: "highlighted", options: NSKeyValueObservingOptions.New, context: nil)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.addObserver(self, forKeyPath: "highlighted", options: NSKeyValueObservingOptions.New, context: nil)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
self.setNeedsDisplay()
if keyPath == "frame" {
// Set up DropdownMenu
self.dropDownBackground.frame.size.height = self.dropDownMenu.frame.maxY
}
}
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
if(!self.clipsToBounds && !self.hidden && self.alpha > 0.0){
let subviews = self.subviews.reverse()
for member in subviews {
let subPoint = member.convertPoint(point, fromView: self)
if let result:UIView = member.hitTest(subPoint, withEvent:event) {
return result;
}
}
}
return nil
}
func ticketTapped() {
//if !animating {
if !dropped {
showDropdown()
NSLog("#")
// NSLog("Scrolling to \((dropDownMenu.indexPathForSelectedRow?.row)!)(the index of the tableview selection)")
// dropDownMenu.scrollToRowAtIndexPath((dropDownMenu.indexPathForSelectedRow)!, atScrollPosition: .Middle, animated: false)
} else {
hideDropdown()
}
// }
}
func showDropdown() {
dropDownMenu.hidden = false
self.ticket.drawShadowBeneathTicket()
print("showing")
if !animating {
animating = true
UIView.animateWithDuration(
1.0,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.5,
options: [],
animations: {
self.dropDownMenu.frame.origin.y = self.bottomBorder!
}, completion: { _ in
//self.delegate?.menuOpen?()
self.animating = false
print(self.dropDownBackground.frame.size.height)
print(self.dropDownMenu.frame.maxY)
}
)
}
self.setNeedsDisplay()
self.dropDownMenu!.setNeedsDisplay()
dropped = true
}
func hideDropdown() {
if !animating {
animating = true
UIView.animateWithDuration(
0.7,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.5,
options: [],
animations: {
self.dropDownMenu.frame.origin.y = self.topBorder! + self.bounceHeight!
}, completion: nil
)
}
UIView.animateWithDuration(
0.5,
delay: 0,
options: UIViewAnimationOptions.TransitionNone,
animations: {
self.dropDownMenu.frame.origin.y = self.topBorder! - self.dropdownHeight!
}, completion: { _ in
//self.delegate?.menuClosed?()
self.animating = false
self.dropDownMenu.hidden = true
self.ticket.setNeedsDisplay()
self.setNeedsDisplay()
}
)
dropped = false
}
func initSubViews() {
self.topBorder = self.frame.height*6/150
self.bottomBorder = self.frame.height - self.topBorder!
self.bounceHeight = 250
self.dropdownHeight = 350
containerView = UIView()
containerView.frame = CGRect(x: 0, y: topBorder!, width: self.bounds.width, height: dropdownHeight!+bounceHeight!)
containerView.clipsToBounds = true
ticket = TacklTicket()
ticket.frame = self.bounds
ticket.addTarget(self, action: #selector(YellowTackleTicketComplete.ticketTapped), forControlEvents: .TouchDown)
dropDownMenu = YellowDrawer()
dropDownMenu.frame = CGRect(x: 0, y: topBorder! - dropdownHeight!, width: containerView.bounds.width, height: dropdownHeight!)
dropDownMenu.hidden = true
dropDownMenu.backgroundColor = UIColor.clearColor()
dropDownMenu.addObserver(self, forKeyPath: "frame", options: .New, context: nil)
dropDownBackground = YellowDrawerBackground()
dropDownBackground.frame = CGRectMake(0,0,self.bounds.width,self.dropDownMenu.frame.maxY)
self.addSubview(containerView)
containerView.addSubview(dropDownBackground)
containerView.addSubview(dropDownMenu)
self.addSubview(ticket)
}
}
You should use a designated initializer for your UIView subview. That is either init(frame:CGRect) or in add it in Interface Builder. You are also not passing let data = dataArray[indexPath.row] to your cell, not sure if you're aware of that. You also never call initSubViews() which seems to be a big part of your custom view.
instead of
cell.addSubview(ticket)
try
cell.contentView.addSubview(ticket)
and the best place to customize cell's content is its init function.
Couple of things you should try, set translatesAutoresizingMaskIntoConstraints to false on the view you created programmatically since you are inserting it into an auto layout controller.
View that should also try just setting,
dropDownMenu, dropDownBackground, containerView and ticket
or
In your cellForRowAtIndexPath also try setting the property
ticket.translatesAutoresizingMaskIntoConstraints = false