UIPageViewController: child view controllers with different height - uiscrollview

I'm using a UIPageViewController to display 3 different view controllers. The UIPageViewController is inside my hierarchy as follow:
ScrollView
ContentView
UIImage
UILabel
UIPageViewController
I would like to add each child view controller of the UIPageViewController in a way that each extends into ScrollView based on its height. The child view controllers can contain different contents: a list of images, pre formatted text, just one image with a title and a description.
I'm having trouble to do that because for some reason I'm not able to calculate the exact height.
My attempt was to change the height constraint on the ContentView every time I detect a swipe is completed.
This is how I add the page view controller:
self.pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
self.addChildViewController(self.pageViewController)
self.pageViewController.view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.pageViewController.view!)
self.pageViewController.didMove(toParentViewController: self)
with the following constraints (using SnapKit):
self.pageViewController.view.snp.makeConstraints() { make in
make.edges.equalToSuperview()
}
and every time I want to change the constraint constant I calculate the height of each view controller, iterating over the subviews and calculate the height amount.
Probably this is the wrong method wondering if someone have some hint.
Edit: https://imagebin.ca/v/3YrSrcYQnvV3

Related

How to expand table view cell and show content beneath it?

I want to have the table view cell expand and show the buttons that I have laid out below the visible view when the cell isn't selected. So far I have managed to expand the cell so that the entire view shows with the buttons, but there is one major problem with this....
The buttons that are supposed to be revealed only when the cell is selected always appear in the table, and the table view looks really weird becuase for each cell there are buttons overlapping the next cell which were supposed to be hidden!
I have tired making a subclass of the cell, but I am stuck because when I override the setSelected method to show the button, all the buttons from all the cells show up, not just the one I clicked, Ill provide my code below.
I there an easier way to show the buttons without using a subclass? And if not how could I use the subclass in a way that wouldn't show all the buttons for all the cells?
Cell Subclass (.m file)
- (void)awakeFromNib {
// Initialization code
editHidden.hidden = YES;
removeHidden.hidden = YES;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
editHidden.hidden = NO;
removeHidden.hidden = NO;
// Configure the view for the selected state
}
Your table view delegate needs to implement tableView:heightForRowAtIndexPath:. Implement this method to return the correct height for your cell given the state that it is in (collapsed or expanded). When it comes time to expand your cell you should update your state and call [tableView beginUpdates]; [tableView endUpdates]; to have it recalculate and relayout the tableview.

Why is my split view behaving like this and how can I fix it?

I created a split view controller that displays two views, like this :
When I compile it, it gives me this result :
Unfortunately, the first view isn't visible and I must drag from the left hand side of the window to see the two views :
First, why is the split view behaving like this ? Why isn't it already at the right size from the beginning ?
If I add this line to the viewDidLoad() function of my SplitViewController :
splitView.adjustSubviews()
Then the two view appears, with equal size, but I don't understand what the adjustSubviews() function does exactly, and I can't control the position of either.
How to fix it programmatically ? How to adjust the size of each view ? Is there a way to adjust it in interface builder ?
Thank you.
EDIT : There is now a bounty of 50 points for this question
Since NSSplitViewController is auto-layout based, you can use auto-layout either if you want a fixed or a dynamic behavior on your views.
For a fixed width what you can do is add a custom view inside your 2 views with a 0 constraint to all the edges and a fixed width. For example:
Then the window will get expanded as follows:
You should be able to set the position of the divider in the split view using (assuming 2 views in the split view):
self.splitView.setPosition(200.0, ofDividerAtIndex: 0)
That would set the first divider at position 200.0
We need to set sizes of NSSplitView's sub-views directly.
Setting ones through splitViewItems seems to have no effect.
// NSSplitViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSView *view1 = self.splitView.subviews[0];
NSRect rect = view1.frame;
rect.size.width = 150;
view1.frame = rect;
NSView *view2 = self.splitView.subviews[1];
rect = view2.frame;
rect.size.width = 300;
view2.frame = rect;
}

Return position of selected row in NSOutlineView

I have an NSOutlineView which when a row in the outline view is double-clicked, an NSPopover is displayed using the following code:
-(void)doubleClick:(id)nid{
NSLog(#"Test double click");
[_popover showRelativeToRect:[nid bounds] ofView:nid preferredEdge:NSMaxXEdge];
}
The code works fine but places the popover in the middle of the vertical height of the outline view. I would like the popup to appear next to the row which is selected (double-clicked) in the outline view. Is there a call I can make to the Outline View to return the position of the selected row? I couldn't seem to find such a method in the documentation. Clearly I would replace [nid bounds] which such a call. Else, any other suggestions of how I could work around this would be appreciated.
Don't forget that NSOutlineView inherits from NSTableView. You're looking for -frameOfCellAtColumn:row:. The column is probably 0. The row you can obtain with -selectedRow.
If your outline view is view-based, you could also pass the row view (-rowViewAtRow:makeIfNecessary:) and NSZeroRect for the rect to -showRelativeToRect:..., so it automatically tracks the row.

NSSplitView resizes the custom NSView contained

I've a vertical NSSplitView, the bottom subview contains a custom view (eg NSView) and a NSTextView.
The NSView contains inside it two NSButtons.
When I resize the splitView, making it smaller, the NSView containing the buttons is resized, too.
I don't want this behavior.
To better explain my problem please view the attached image.
Image 1: the window at application startup, everything is ok
Image 2: I've resized making smaller the split view, only a little part of buttons is visible
Image 3: I've enlarged again the split view but as you can see the NSView remains smaller and buttons are no longer visible (if I resize the splitView to bottom the NSView 'disappears')
This is a vicious problem that's based on the legacy workings of Cocoa views. The best solution I've seen is to constrain the minimum dimension of any portion of the split view. If the subviews never collapse, their metrics don't cross into another dimension and they should re-enlarge just fine.
To do this, set up a delegate for your split view, which will implement - splitView:constrainMaxCoordinate:ofSubviewAt:. The split view will call your delegate method hoping it can leave the max divider position at the height of the split view (passing this in as the second argument), but you can simply subtract some quantity from that value (say, 60) to return it as the minimum height for the bottom view.
- (CGFloat)splitView:(NSSplitView *)aSplitView
constrainMaxCoordinate:(CGFloat)proposedMin
ofSubviewAt:(NSInteger)dividerIndex {
return proposedMin - 60;
}
Of course, you'll probably want to do more checking in this method to make sure you're talking about the right split view, and the right subview, to avoid overreaching effects, but this is the basic idea.
(See also this fabulicious article on the subject.)
Constraining the divider position did not help in my case, as I'm animating the subviews and subviews can be collapsed.
I managed to achieve an acceptable solution by implementing the splitView delegate method -splitviewWillResizeSubviews: (means, you have to connect the delegate property from the split view to your controller in IB or in code) to maintain a minimum width by setting the subview to hidden instead of shrinking it to zero:
- (void)splitViewWillResizeSubviews:(NSNotification *)notification {
NSUInteger divider = [[[notification userInfo] valueForKey:#"NSSplitViewDividerIndex"] intValue];
NSView *subview = nil;
if(divider == SPLITVIEW_DIVIDER_SIDEBAR) {
subview = (NSView*)[self.splitView.subviews objectAtIndex:SPLITVIEW_SIDEBAR_INDEX];
}
if(subview) {
if(subview.frame.size.width < SPLITVIEW_MINIMUM_SIDEBAR_WIDTH) {
CGRect correctedFrame = subview.frame;
correctedFrame.size.width = SPLITVIEW_MINIMUM_SIDEBAR_WIDTH;
subview.frame = correctedFrame;
subview.hidden = YES;
} else {
subview.hidden = NO;
}
}
}

NSTableView content view inset

I am looking to inset the contents of an NSTableView so that there is a gap between the top of the table view and the first cell.
On iOS this is easy with UITableView - achieved by using setContentInset:.
Turn headers back on and substitute the header view with your own subclass. Override its -drawRect: to draw only your background color. Also override -headerRectOfColumn: to prevent any of the column headers from being drawn. I'm not sure if this prevents column dragging or sorting but I'll bet it does.
The question asked how to adjust content insets similar to iOS. The currently selected answer shows how to move the first row down, but that's not quite the same thing. Adjusting the content insets will also move the start of the scrollbar to the inset position, just like iOS. This is useful when placing content underneath a "vibrant" or transparent toolbar.
An NSTableView itself does not have content insets. On macOS content insets are usually part of NSScrollView. To get access to the scroll view of NSTableView's view controller you can use the enclosingScrollview method of NSView, disable automatic adjustment and set the insets like this:
(warning old school Obj-C here)
self.enclosingScrollView.automaticallyAdjustsContentInsets = NO;
self.enclosingScrollView.contentInsets = NSEdgeInsetsMake(50.f,0.f,0.f,0.f);
Calling these from viewDidLoad is usually fine, however some types of table views will override your values with their own.
NSOutlineView set to source-list mode comes with lots of default values overridden to make the view look like the Finder sidebar.
There is no "clean" way to set the content-insets of these views. They stubbornly override your values, I've found that if you subclass NSOutlineView and overload setFrameSize: it will do the trick. So like this (inside the NSOutlineView subclass):
- (void)setFrameSize:(NSSize)newSize {
[super setFrameSize:newSize];
self.enclosingScrollView.automaticallyAdjustsContentInsets = NO;
self.enclosingScrollView.contentInsets = NSEdgeInsetsMake(100.f,0.f,0.f,0.f);
}
This will do the trick, but the initial scroll position will be strange. Calling scrollToBeginningOfDocument: from the initWithCoder: method of the subclass will scroll it to the correct initial position.
It's not very clean but you can achieve that by having the first row higher than the rest. Implement heightOfRow table delegate method:
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
{
if (row == 0) {
return normalRowHeight + topPadding;
} else {
return normalRowHeight;
}
}
The drawback is that you would also need to implement custom highlighting and custom cell drawing to take into account the extra space for the first row.
scrollView.automaticallyAdjustsContentInsets = false
scrollView.contentInsets = NSEdgeInsets(top: 40, left: 0, bottom: 0, right: 0)

Resources