I am having trouble understanding how to embed my view into a scroll view with a dynamic height. I have managed to create scroll views before, but only with a static height. How would an iPhone application, such as Facebook or Instagram, for example, manage to create a scroll view for an entire feed of dynamic-height data. To this point I have used storyboards to create the scroll views, but I am assuming that this done in code would be much better.
class connectDetailsViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//set up variables with data
fillData()
}
override func viewWillAppear(animated: Bool) {
let nameTitleString = String(businesses[businessNum]["name"]!)
nameTitle.text = nameTitleString
processTimes() // Adds information for the hours of operation, if applicable
processDetails() //Adds information for the details if possible (a few sentences to a few paragraphs)
}
}
processDetails() and ProcessTimes() will edit the content in a stack view, how can I create a scroll view to encompass both of these stack views at the correct height?
Generally, for providing a feed of content such as a Facebook timeline you would likely use a UITableView as it manages the reuse of cells and the overall contentSize internally using it's delegate methods on the dataset to determine number of rows, estimated row heights and so on. The tableView actually uses a scrollView to acheive this but it handles it internally.
In my opinion this would be your best approach, to do this without using a tableView you would need to replicate alot of the logic that it performs on your behalf.
The main thing to remember with a scrollView is that it can load content that is larger than the scrollView, and then manages scrolling/panning/zooming to allow you to view the larger content.
If you just wanted to know how to do this yourself, you would likely need a parent container in the scrollview and then you would keep adding new views into it, adjusting the scrollViews contentSize height each time you add new content
Related
I have an Xcode project with an NSWindowController whose contentViewController was set to a subclass of NSViewController. I recently removed the NSViewController subclass from the storyboard and replaced the contentViewController with an NSTabViewController subclass.
Now, when I run the application, the NSWindow opens with a size of 500x500 instead of the size of the first tab. What's more, there is no view I can see in the storyboard that has a size of 500x500, and that size isn't being programmatically, either. The window itself is set to a different size, as is the view in the NSTabViewController's first NSViewController.
I'm assuming that there is some sort of constraint I have to set somewhere, but if there is, I don't know where/how to find it. Using Xcode 9.2 and High Sierra.
Programmatically setting the window's size to the correct size in windowDidLoad() works, but if I ever change the size of the view, I'll have to change that, as well, which will get old, quick.
Sorry if this is vague; I genuinely have no clue what kind of screenshot or code snippet would be helpful.
I recently ran into this frustrating problem as well.
There are a couple options to workaround this problem:
As you mentioned, set preferredContentSize in each of your custom view controllers that hold the tab's content to your desired size. This is inflexible but it does work.
// Swift
class FooViewController: ViewController {
override func viewWillAppear() {
super.viewWillAppear()
preferredContentSize = NSSize(width: 400, height: 280)
}
}
I found a hint to a better solution in this SO answer. You can add a subview (stackview, nsview, etc...) to the main view of the view controller that handles the tab's content (phew!) and then add constraints that pin it to each edge and add constraints that set the size.
Here's a screenshot of what it looks like in Interface Builder. I added a Stack View and then added 6 constraints.
Hope this helps.
Joshua's answer with setting the preferredContentSize did the trick, all kudos to him! One remark worth making is that since this is done exclusively for the parent tab view controller it's a good idea to subclass it and move this handling into tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) delegate method, which gets invoked when the tab is selected:
override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
tabViewItem?.viewController?.preferredContentSize = tabViewItem?.view?.frame.size
// Alternatively: tabViewItem?.viewController?.preferredContentSize = tabViewItem?.view?.fittingSize
super.tabView(tabView, didSelect: tabViewItem)
}
This way the preferred content size is always up to date and you can worry not about manually refreshing it, assuming the view provides the correct frame size or fitting size, which is easily achieved with constraints.
This method also get's invoked after the window controller finishes loading and where the 500×500 gets initially set.
Setting the preferred content size in every tabbed view controller itself is not ideal: the same code is duplicated across multiple controllers and adds unnecessary noise if these controllers are reused else where.
I had a similar issue. I added a view controller with a container view as the window content and pointed the container view content to the tab view controller.
I am using the self sizing cell feature and it works well until I want to hide a cell completely. I moved away from heightForRowAtIndexPath for this and I setup the following:
override func viewDidLoad() {
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 0
}
However when I have no text for a tableviewcell to render I get the following message:
Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a tableview cell's content view. We're
considering the collapse unintentional and using standard height
instead.
I really just need a way to hide / show content dynamically. I am using a static tableview for this, so maybe I am approaching this wrong?
I found the solution.
First, for a static table to use self sizing correctly, you can only have one label per table cell. I was trying to put in a lot of content into a cell and only the first label would size the cell. I could be wrong about the rule of one label per cell, and the problem might be constraints / auto layout related. I've watched the WWDC video on this, and the way I had it setup, should have worked with my existing constraints, as they where set to the contentView of the cell.
Secondly, the UI updates needed to be coupled with begin and end rules, and a reload.
tableView.beginUpdates()
//-- You Table UI changes
tableView.reloadData()
tableView.endUpdates()
You can also replace reloadData with reloadRowsAtIndexPaths to be specific to the rows to update, but my instance required all rows to be updated.
I had a similar problem some time ago. I would use dynamic cells, and remove the hidden cells in
numberOfRowsInSection
and in
cellForRowAtIndexPath
You should try to add a property, like "hidden" in your cell content array, and check when you load/reload your data. That works (of course) still fine with Autolayout.
I have a listview that contains a horizontal scrollView inside each row.
Now when getView() is called for each position in the listView, I am creating and adding views (relative layouts) to the horizontal scrollView. This getView() is pretty cramped.
I am trying to improve the performance of my listview.
I do not store each of the views inside my horizontal scrollView for each position in list view as that would amount to storing all the views that go into the whole listView. This i wouldn't be taking advantage of view re-use feature.
But then how do i improve the performance as I am having to create the views inside the horizontal scrollView every time get view is called and that itself is quite heavy.
If i store these internal views (relative layouts) in a map (position, list of views) and then in get view just call that position get all the internal views and feed to my horizontal scroll view?
I am confused about this. could someone please help me in improving the performance here.
Thanks.
Sunny
The main concept of Listview is to reuse the child components.
Basically, what you are doing could be wrong. HorizontalScrollview consumes a lot more memory than ListView, and I'm assuming that the getView() method is doing a lot of work.
My Suggestion:
Listview should be replaced with ScrollView and HorizontalScrollView should be replaced with ViewPager or HorizontalListView.
Note: If you want a perfect answer post a screenshot
I need to create a view based tableview in Popover as specified below:
Tableview should be placed in Popover(Popover height should be same as tableview).
Each row should contain a view.
Each row view will contain 3 labels.
Labels should be auto re sizable based on its text height.
Based on 3 labels height, Cell row height should resize.
Based on all cell rows, tableview height should resize.
Based on tableview height, Popover should resize.
I have done this in a static format, but i need to do it in more dynamic format(in future i should be able to add more rows using same classes and methods).
Main problem i am facing is, i am unable calculate the size of cell view in tableView:heigthOfRow: since i don't know the text of labels in this point of time.
So i just created tableview cells in loadView itself and saved in array, and fetching from array in tableview delegate methods. But i think this is wrong way of doing so.
Note: All data to tableview will be given while loading the view itself. Labels are not editable.
Cocoa system resizes the subviews based on superview ,I think scenario that you are looking for is to resize super view based on subview size.
Following 2 solutions i can suggest right awyay,
1.You can choose to post notification upon size change in each subview and make immediate superview observe that.
2.Use globals for size of each subview in your case 3 labels, and have an API to calculate finalRect in your view of view based table view.
Hope this helps:) have a nice day.
I subclassed UITableViewController and called it FeaturedGamesViewController. Ok, I also have a navigation controller to which I added this FeaturedGamesViewController as the root. All pretty standard stuff that you do in the App Delegate.
Also note that there is no NIB file created for FeaturedGamesViewController. Just subclassing UITableViewController and calling initWithStyle sets the style of the table and sets the dataSource and delegate and size automatically. Data Source and Delegate are obviously set to FeaturedGamesViewController.
- (id)init
{
// Call the superclass's designated initializer
[super initWithStyle:UITableViewStyleGrouped];
}
OK, You see that I have set the table size to "Grouped". In Landscape view on my iPad it has about 20 pixels of space to the top, left and right (Sorry can't post screen shot because I am new here and the system won't let me until I have accumulated a certain number of points)
I DO NOT want that! This is Landscape so I expect it to fill up the all the space between the navigation bar and the tab bar below. What is worse is that I have faked a grid with a Custom UITableViewCell but the space to the left and right make it so that if you click on that space, the entire row is selected thus betraying the sense that this is a grid.
Now I figure I should resize the table view in viewDidLoad or something but I don't know how. I cannot do initWithFrame because of potential memory leaks (and possibly resetting dataSource and delegate and autoresizeMask properties that were already set) so there must be a setter or something to reset the origin of the tableview to just beneath the Navigation bar and filling up the entire screen with size 1024X748. How do you do dynamically reset the size of the table view?
Then I got really frustrated and I decided to do it via a Nib file, that way I can set the the orientation to landscape and set simulated Navigation and Tab bars and fill the rest of the space with the table view. This worked! If you are curious how to create a table view with a Nib after you have subclassed UITableViewController WITHOUT a nib, here is how:
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/CreateConfigureTableView/CreateConfigureTableView.html#//apple_ref/doc/uid/TP40007451-CH6-SW10
Go to the paragraph right before "Creating a Table View Programmatically".
OK, when I did that, my landscape view of the "grid" looks filled up the entire space between the navigation bar at the top and the tab bar at the bottom just like I wanted.
I was fiddling with this some more and I found out that in my "non nib" version (the problematic one), I had set the table style to "grouped". When I changed it to "plain", it worked!!! But here is the thing though: In the nib version, "grouped" or "plain" gives the correct layout with the table occupying the whole space. So what gives?
Sorry for the long question but I guess what I am asking is:
1) How do you programmatically reset the size of the table view without introducing potential memory leaks or affecting other properties already set for you (ex: dataSource, delegate, autoResizeMask - these are set for you just because you subclassed UITableViewController)?
2) How do you do that for any view in general?
3) Why does "plain" style fill the layout as desired whereas "grouped" style gives the weird layout. Note that it this is not a problem in the Nib version.
Thanks for your patience!
Answer for (2), and hence for (1):
A UIView's frame is in a local coordinate system of its superview. A common way to make a view fit its superview is
CGRect bounds = [[self superview] bounds];
[self setFrame:bounds];
You should do this inside layoutSubviews.