UICollectionView some cells are neither visible items nor dequeued cells - ios

I have an issue in my UICollectionView where some cells seem to be neither part of the indexPathForVisibleItems, nor are they taken from the caching queue with dequeueReusableCell. The result is, that some cells don't receive required updates of data during a scroll and show old behavior.
For simplicity, I reduced the project to the neccessary Controllers and a minimized Storyboard. Basically, I've got a NavigationController as EntryPoint that contains the MainViewController, which itself contains a ContainerView with the CollectionViewController.
The NavigationController uses the default edit button to switch between edit and non-edit mode - this should result in an image displayed on the cells while in edit mode. Therefore I implemented setEditing and changed the images hidden property of all visible cells, and additionally i set the images hidden property while dequeuing - assuming that cells are either visible or they will be dequeued in the future.
This works fine while the CollectionView is scrolled from top to bottom. But when I switch back from Edit-Mode to Non-Edit-Mode while scrolled to the bottom and then scroll back to the top, some cells still display the image (more specific: at least the same row, which is the first non-visible row). Somehow I'd assume that the dequeued cells and the visible cells would be complementary parts of the displayed data, which should result in either the images being hidden/unhidden during the setEditing call (which works for the first 4 rows of cells) or being hidden/unhidden during the dequeuing (which works for the last few rows, except the third row in my example)
Code for the CollectionViewController:
import Foundation
import UIKit
import Photos
class CollectionViewController : UICollectionViewController {
fileprivate let CELL_ID = "PicCell"
fileprivate let IMAGE_VIEW_SIZE = 104
var selectedIndex = -1
var itemCount = 28
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
print(self.isEditing)
collectionView?.allowsMultipleSelection = editing
for indexPath in (collectionView?.indexPathsForSelectedItems)! {
collectionView?.deselectItem(at: indexPath, animated: true)
}
for indexPath in (collectionView?.indexPathsForVisibleItems)! {
let cell = collectionView?.cellForItem(at: indexPath) as? PicCell
if cell != nil {
cell?.editing = editing
}
}
}
override func viewDidLoad() {
self.navigationItem.rightBarButtonItem = self.editButtonItem
collectionView?.reloadData()
}
}
extension CollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemCount;
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print(indexPath.row)
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CELL_ID, for: indexPath) as! PicCell
cell.editing = isEditing
cell.isSelected = false
return cell;
}
}
Code for PicCell
import Foundation
import UIKit
class PicCell : UICollectionViewCell {
#IBOutlet weak var deleteBtn: UIImageView!
var editing:Bool = false{
didSet {
self.deleteBtn.isHidden = !editing
self.deleteBtn.tintColor = UIColor.blue
}
}
override var isSelected: Bool{
didSet {
if isSelected && editing {
self.deleteBtn.tintColor = UIColor.red
} else {
self.deleteBtn.tintColor = UIColor.blue
}
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.editing = false
self.isSelected = false
}
}
Code for MainViewController
import Foundation
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
collectionController?.setEditing(editing, animated: animated)
}
var collectionController : CollectionViewController? = nil
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowCollectionView" {
collectionController = segue.destination as! CollectionViewController
}
}
}
as you can see I'm using constant values for the item count in a single section, which shouldn't be a big deal.
I don't know if it matters, but my cells are 104px in width and height, the image is rendered as template and the UIContainerView is 343 in width and 473 in height, i'm testing on an iPhone 7+ simulator with iOS 10.1
If there's anything missing from the storyboard, i might add some screenshots, but i'm unsure what to post exactly
Thanks in advance for your help and kind regards
Christian
Edit: just to be clear with my question: I'm looking for some advice for either obvious mistakes in my code or a way to access UICollectionViewCells that are neither in the list of visible items nor dequeued during scroll - there might be a way to access those cells i simplay don't know about

You can change this behavior if you turn off prefetching:
collectionView?.isPrefetchingEnabled = false
Or you can keep it on, but then either:
hook into UICollectionViewDelegate methods didEndDisplaying and willDisplay to know as cells appear and disappear independent of cellForItemAt; or
have cells do some KVO on some view controller property or observe some notification that the view controller will initiate in order to know whether to change their state.

Related

How Do I Get Gesture Recognizers to work on base controller when displaying a popup controller? [duplicate]

I have a collection view and when a cell is selected it presents a popover view showing more information about that cell.
I would like to allow the user to click another cell and then have the popover view change to showing that cell's information without having to close the popover. If the user were to click somewhere on the parent view that isn't a cell then the popover should close. But, I would like the user to still be able to scroll the collection view without closing the popover.
How can that be done?
According to Apple :
When a popover is active, interactions with other views are normally disabled until the popover is dismissed. Assigning an array of views to this property allows taps outside of the popover to be handled by the corresponding views.
Then you can use the passthroughViews in the following way :
CollectionViewController
import UIKit
let reuseIdentifier = "Cell"
class CollectionViewController: UICollectionViewController {
var popoverViewController : PopoverViewController?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
//#warning Incomplete method implementation -- Return the number of sections
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
return 15
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CollectionViewCell
cell.labelInfo.text = "Cell \(indexPath.row)"
return cell
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
println("tapped")
if let popover = self.popoverViewController {
var cell = self.collectionView!.cellForItemAtIndexPath(indexPath) as! CollectionViewCell
popover.labelPop.text = cell.labelInfo.text
}
else {
self.popoverViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PopoverViewController") as? PopoverViewController
var cell = self.collectionView!.cellForItemAtIndexPath(indexPath) as! CollectionViewCell
var t = self.popoverViewController!.view
self.popoverViewController!.labelPop.text = cell.labelInfo.text
self.popoverViewController!.modalPresentationStyle = .Popover
var popover = self.popoverViewController!.popoverPresentationController
popover?.passthroughViews = [self.view]
popover?.sourceRect = CGRect(x: 250, y: 500, width: 0, height: 0)
self.popoverViewController!.preferredContentSize = CGSizeMake(250, 419)
popover!.sourceView = self.view
self.presentViewController(self.popoverViewController!, animated: true, completion: nil)
}
}
}
The above code is the CollectionViewController to handle the UICollectionViewController and all its delegates.
CollectionViewCell
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var labelInfo: UILabel!
}
The custom cell with just a UILabel inside.
PopoverViewController
class PopoverViewController: UIViewController {
#IBOutlet var labelPop: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
And finally the PopoverViewController to show in form of .Popover.
There are some observations in answer I would like to point out :
I set a reference to the class PopoverViewController to keep it through the life cycle and pass it data when it remains open yet.
The line var t = self.popoverViewController!.view it's necessary because if not the #IBOutlet inside the PopoverViewController was not init until it's presented, there could be other ways to do it.
I present the popover in the middle of the screen to handle the tap in several cell and test it the scroll too, you can display it in any position you want.
In the views to allow when the popover is opened , I set the self.view, but in this way you need to dismiss it for you own, because it never is dismissed when you make taps in the view, you can put any view you want instead.
Any trouble you have with the solution I can share it the project on Github.
I hope this help you
What you are looking for is the passthroughViews property of the popover.
However, if you open the popover as a result of tapping a cell, I don't see how scrolling the collectionView will make sense. Don't you open the popover with the arrow pointing to your cell? Scrolling the view will make the presenting cell to move away...
You can use property of UIViewController 'modalInPopover' to enable touches outside the popover boundary. Just write the line given below in your view controller which you are presenting using popover controller.
self.modalInPopover = false;
where self is kind of UIViewController.
I have attached a screenshot for the same.
In swift the line will remain same
self.modalInPopover = false

How to subclass TWTRTweetTableViewCell in Swift Twitterkit?

I am trying to find out if it is possible to subclass TWTRTweetTableViewCell from the TwitterKit library. So far I have my custom cell class inherit from TWTRTweetTableViewCell. The xib has a UIView in it which has an outlet to the cell class and the UIView class is set to
TWTRTweetView. Like this-
class UserTweetViewCell: TWTRTweetTableViewCell {
#IBOutlet weak var tweetViewCustom: TWTRTweetView!
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
}
}
The cell's class in property inspector is set to UserTweetViewCell and the UIVIew's class in the cell is set to TWTRTweetView.
In the main view controller I have this
tableView.register(UserTweetViewCell.self, forCellReuseIdentifier: tweetTableReuseIdentifier)
and then
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let tweet = tweetsarr[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: tweetTableReuseIdentifier, for: indexPath) as! UserTweetViewCell
cell.tweetViewCustom.showActionButtons = false
cell.tweetViewCustom.linkTextColor = UIColor(red:0.12, green:0.53, blue:0.90, alpha:1.0)
cell.tweetViewCustom.configure(with: tweet as? TWTRTweet)
cell.tweetViewCustom.theme = .light
cell.tweetViewCustom.delegate = self
return cell
}
However, i get an error at line cell.tweetViewCustom.showActionButtons = false and the error is Unexpectedly found nil while unwrapping an Optional value. What am I missing here?
I finally did it and it's working like a charm. The trick is not to subclass TWTRTweetTableViewCell but instead just subclass a regular UITableViewCell and use a TWTRTweetView inside of it. Which is basically what TWTRTweetTableViewCell does, it has tweetView property which is essentially an IBOutlet of type TWTRTweetView. The custom cell Nib should contain a UIView with TWTRTweetView set as it's class in the identity inspector. Here goes the code-
class CustomTweetCell: UITableViewCell{
#IBOutlet weak var customTweetView: TWTRTweetView!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func configureCell(with tweet: TWTRTweet){
self.customTweetView.showActionButtons = false
self.customTweetView.configure(with: tweet)
}
}
For the cell's height, the following needs to be done for the tableview-
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let tweet = tweets[indexPath.row]
let tweetheight = TWTRTweetTableViewCell.height(for: tweet as! TWTRTweet, style: .compact, width: self.view.frame.width, showingActions: false) + 30 //this 30 should be the height of any additional views that you put in the cell Nib file
return tweetheight
}
NOTE: Its extremely important to have autolayout constraints enabled within the tableview cell with the TWTRTweetView and any other views that you may have and also make sure the Table view cell row height is set to Default or blank in the cell's Size inspector.Failing to do so will mess up the tweet view height and will cause undesirable results.
I wanted to Subclass TWTRTweetTableViewCell so that I could add the likes count, retweets count, reply button etc. so far it hasn't worked. So next I am going to give it a try Subclassing TWTRTweetView and use that in the tableview cell instead. I think I have tried it once with partial success. The challenge is the tweet height
This is how I am calculating the tweet height in Objective-c:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
TWTRTweet * tweet = self.tweets[indexPath.row];
if (self.tweets.count > indexPath.row) {
[self.prototypeCell configureWithTweet:tweet];
}
CGFloat tweetHeight = [CustomTweetTableViewCell heightForTweet:tweet style:TWTRTweetViewStyleCompact width:[tableView bounds].size.width showingActions:YES];
self.tweetHeights[indexPath.row] = [NSNumber numberWithFloat:tweetHeight];
return tweetHeight;
}

The delegate in this app is not being set

I have an app with two view controllers... ViewController and CollectionViewController. I have filter functions in the viewController that perform color filters on an image loaded into this view controller. The CollectionViewController contains a collection view that acts as a horizontal scrolling menu with cells, that when pressed, are supposed to call the filter functions in ViewController. The storyboard has a show segue with ID "FilterSegue"
I got it to work with Notification Centers but I wanted to try to get it to work with protocols and delegates to learn about the methods. I received some suggestions, but have been unable to get the delegate to set despite numerous attempts.
Here is the code:
ViewController:
import UIKit
class ViewController: UIViewController, FilterDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "FilterSegue" {
let destvc = segue.destinationViewController as! CollectionViewController
destvc.filterDelegate = self
}
}
func onRedFilter() {
// some code
}
func onGreenFilter() {
// some code
}
func onBlueFilter() {
// some code
}
func onUnfiltered() {
// some code
}
}
CollectionViewController:
import UIKit
//Protocols for filter functions called by the filter menu collection view custom cells.
protocol FilterDelegate: class {
func onRedFilter()
func onGreenFilter()
func onBlueFilter()
func onUnfiltered()
}
class CollectionViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UICollectionViewDelegate {
let reuseIdentifier = "FilterCell"
var filterDelegate: FilterDelegate? = nil
#IBOutlet var collectionView: UICollectionView!
// Filter labels for custom filter menu cells.
var tableData:[String] = ["Red Filter",
"Green Filter",
"Blue Filter",
"Unfilter",
"New Filter 1",
"New Filter 2"]
// Filter images for custom filter menu cells.
var tableImages: [String] = ["waterfallred.png",
"waterfallgreen.png",
"waterfallblue.png",
"waterfall.png",
"waterfall.png",
"waterfall.png"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Set up collectionView for the filters.
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: 120, height: 80)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView!.registerClass(colvwCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView!.backgroundColor = UIColor.whiteColor()
self.view.addSubview(collectionView!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Set uo required methods for collection view.
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tableData.count
}
// Method for custom collection view cell texts and images.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: colvwCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! colvwCell
cell.backgroundColor = UIColor.grayColor()
cell.lblCell.text = tableData[indexPath.row]
cell.imgCell.image = UIImage(named: tableImages[indexPath.row])
return cell
}
// Method for calling functions upon pressing custom filter menu collection view cells. In this case, the filter functions in the main view controller are called using notifications.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Cell \(indexPath.row) selected")
guard let filterDelegate = filterDelegate else {
print("Filter delegate wasn't set!")
return
}
switch indexPath.row {
case 0:
filterDelegate.onRedFilter()
case 1:
filterDelegate.onGreenFilter()
case 2:
filterDelegate.onBlueFilter()
case 3:
filterDelegate.onUnfiltered()
default:
print("No available filter.")
}
}
}
The guard statement returns "Filter delegate wasn't set!" on any depressed cell on the collection view menu.
instead of this
protocol FilterDelegate: class {
func onRedFilter()
----
}
try this
protocol FilterDelegate {
func onRedFilter()
----
}
I found the answer...
I realized that the segue wasn't being set because it was trying to instantiate another collection view controller with the segue. I removed the prepareForSegue altogether and the segue from the storyboard.
I have a button called onFilter2 on the main view controller that instantiates the collection view in a container on a press (calls a function showFilterMenu to show the filter menu) and then hides it on a second press (hideFilterMenu). The showFilterMenu function is where the colectionviewcontroller is instantiated. So, I set the delegate in this function. It took one line of code.
Here is the function executed upon pressing the filter button with the correct call of the delegate.
// New onFilter button action that shows/hides the filter collection view scroll bar on main view controller.
#IBAction func onFilter2(sender: UIButton) {
if filterButtonOn == true {
if (sender.selected) {
hideFilterMenu()
editButton.selected = false
sender.selected = false
} else {
showFilterMenu()
sender.selected = true
hideSliderMenu()
editButton.selected = false
//Reset color sliders to initial values.
redHorizontalSlider?.setValue(0, animated: false)
greenHorizontalSlider?.setValue(0.0, animated: false)
blueHorizontalSlider?.setValue(0.0, animated: false)
// Set initial values for color indicators.
redSliderIndicator.text = "0.00"
greenSliderIndicator.text = "0.00"
blueSliderIndicator.text = "0.00"
}
}
}
// Shows the filter menu scrolling bar in container view upon request.
func showFilterMenu() {
let newViewController = self.storyboard?.instantiateViewControllerWithIdentifier("FilterCollectionView") as! CollectionViewController
newViewController.view.translatesAutoresizingMaskIntoConstraints = false
self.toggleOnViewController(newViewController)
self.currentViewController = newViewController
// Set the delegate for the filter functions called by the cells in the collectionViewController filter menu.
// Set the delegate for the filter functions called by the cells in the collectionViewController filter menu.
//Here is the line of code that fixes the problem...
newViewController.filterDelegate = self
filterMenuShowOn = true
}
// Hides filter menu scrolling bar from container view upon request.
func hideFilterMenu() {
currentViewController!.view.alpha = 1
UIView.animateWithDuration(0.5, animations: {
self.currentViewController!.view.alpha = 0
},
completion: { finished in
self.currentViewController!.view.removeFromSuperview()
self.currentViewController!.removeFromParentViewController()
})
}
I want to thank Matt for getting me thinking about what was happening.

Getting EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0x0) in swift because of PanGesture

I have made a collection view with 4 cells and when you press the button in cell 1 (I have not made the other cells yet) it will take you to a new ViewController called "FirstCollectionViewController". I have a pangesturderecognizer that shows the slide out menu when you swipe in the collection view.
But when you are in the "FirstCollectionViewController" and you want to come back to the collection view, you can press a button up in the left side corner. But when I press it, I will get an EXC_BAD_INSTRUCTION error at the "self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())" inside the ViewDidLoad in the CollectionViewController. I have tried to put the panGestureRecognizer in the ViewDidAppear but the same happens
How can i fix it?
BTW the segue which should send you back to the collection view is called: "SegueFirstCollectionViewController"
CollectionViewController :
import Foundation
import UIKit
class CollectionViewController: UICollectionViewController {
var Array = [String]()
var ButtonArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
Array = ["1","2","3","4"]
ButtonArray = ["1", "2", "3", "4"]
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
override func viewDidAppear(animated: Bool) {
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Array.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as UICollectionViewCell
let Label = cell.viewWithTag(1) as! UILabel
Label.text = Array[indexPath.row]
let Button = cell.viewWithTag(2) as! UIButton
Button.setTitle(ButtonArray[indexPath.row], forState: UIControlState.Normal)
// Button.addTarget(self,action:#selector(ButtonArray(_:)), forControlEvents:.TouchUpInside)
Button.addTarget(self, action: Selector("ButtonArray:"), forControlEvents:.TouchUpInside)
return cell
}
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("Inside didSelectItemAtIndexPath")
}
func ButtonArray(sender : UIButton) {
print("m")
if sender.currentTitle == "1"{
performSegueWithIdentifier("segueFirst", sender: nil)
}
if sender.currentTitle == "2"{
}
if sender.currentTitle == "3"{
}
if sender.currentTitle == "4"{
}
}
}
FirstCollectionViewController :
class FirstCollectionViewController : UIViewController {
#IBAction func SegueFirstCollectionViewController(sender: UIButton) {
performSegueWithIdentifier("SegueFirstCollectionViewController", sender: nil)
}
}
You are missusing segues. Segues creates new instance of their destination ViewController, meaning that when you start your app you have 1 CollectionViewController, then you click a cell and you have a stack of 1 CollectionViewController AND 1 FirstViewController, then you hit the button, and event it looks like it, you are not coming back to the original CollectionViewController, but you are creating a new one, meaning you now have 1 collectionViewController, one first viewController and one CollectionViewController.
This is why you are reexcecuting viewDidLoad, this is why it fail because the pan gesture recognizer has already been added to another view....
If you want to implement flat (push pop / master -> detail / back button...) you should encapsulate your collectionViewController in a NavigationViewController and use the freely given back button : DON'T HANDLE THE BACK YOURSELF, ESPECIALLY WITH SEGUE
PS for the sake of beeing exact but for an advertized public : a special kind of segue exist to deal with back, they are called unwind segue. However, UINavigationController should always be the preferred option for flat navigation. Unwind segue find their use when dealing with vertical navigation (modal presentation / OK - Cancel)
You don't need get the cell outside
Make set value in
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{
//set value here
if self.currentTitle == "1" ){
//
}
else {
//...
}
}
and reload the specific cell
let indexPath = IndexPath(item: i, section: 0)
self.tableView.reloadRows(at: [indexPath], with: .automatic)
Hope it would make help for you:)

Swift - pop up view, date picker?

I've googled for hours and have tried a handful of tutorials, but haven't been able to get this working:
I have a TableView, and I want to make it so pressing on a cell presents a popup that has a date picker.
I have my custom viewcontroller with the date picker presenting (popping up from the bottom), but it takes up the entire screen. Thoughts? I found one mention of this exact issue while googling but the solution didn't work.
One possibility is to overlay a subview (object of UIView) (with a date picker and a done button) on top of your tableview. Then use .hidden feature of the subview to hide/show the view. The following is an example of the tableviewcontroller. When setting up the storyboard make sure that the subview has the layout constraints so the date picker is positioned properly. I used the "resolve auto layout issues" and it worked good. Unless you do special processing the subview will get positioned at the bottom of the rows. If you have a lot of rows the aubview will get clipped or hidden completely. So it is better to position the subview at the relative to the bottom of the page in your auto layout.
Here is a simple example that worked well for me. In viewDidLoad the subview is hidden. When you click on any row it will show the subview and the date picker. When you press done it will hide the subview again.
import UIKit
class TableViewController: UITableViewController {
#IBAction func doneButton(sender: UIButton) {
// process the date using datePickerOutlet properties
subView.hidden = true // hide the subview and its components
}
#IBOutlet weak var subView: UIView!
#IBOutlet weak var datePickerOutlet: UIDatePicker!
#IBAction func datePicker(sender: UIDatePicker) {
}
override func viewDidLoad() {
super.viewDidLoad()
subView.hidden = true // hide the subview and its components
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("\(indexPath.row)")
subView.hidden = false // show the subview and its components
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = "\(indexPath.row)"
return cell
}
}
Alternatively, put it in a UIAlertView(). At least it will be centered. It's going to be tough to combine a table and a picker on a small screen, like let's say a 4S.
I think this should work:
override var preferredContentSize: CGSize {
get{
// Checks if it is currently presenting
if presentingViewController != nil {
return (datePicker.sizeThatFits(presentingViewController!.view.bounds.size))
}
return super.preferredContentSize
}
set{ super.preferredContentSize = newValue }
}
This code goes under the View controller for the popup.
Basically what it does is to set the width of the popup to the minimum size required for the date picker.
Got it from the iTunes U tutorial by Paul Hegarty

Resources