I am facing unique issue with nspopupbutton in one of my mac application.
I am using table view to load the filters, filters having different options, We can select the option from dropdown. As like I shown below.
My Issue is , After adding another filter (table view cell), My first cell dropdown text showing reversely. Please find the screenshot below.
Please share your thoughts , I really trapped in this issue.
Issue only in High Sierra MacOS.
Edit
I am creating custom cell in XIB as like below and binding the NS elements using tag
Code I used to load table view
public override nint GetRowCount (NSTableView tableView)
{
return appliedFilters.Count;
}
public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
{
var vw = (FiltersCell)tableView.MakeView (parentVC.appliedFilters.ElementAt((int)row), this);
// Binddata is method I used to bind data in FIlterCell
vw.BindData (parentVC.appliedFilters [(int)row].ToString(), (int)row,parentVC);
return vw;
}
public override nfloat GetRowHeight (NSTableView tableView, nint row)
{
return 30;
}
macOS HS changes a number of layer based things, and if we are using the "old" NSCells somewhere it wouldn't surprise me they broke something.
You could try setting your parent view to layer based (WantsLayer = true or in IB) and see if that "fixes" things.
Multiple people have hit layer based regressions / issues in HS (not bugs in Xamarin.Mac).
From xamarin team
Related
I have a view based NSTableView and can't figure out how to work around a visual glitch where the currently selected row flickers while scrolling up or down with the arrow keys.
The selected row should appear 'glued' to either the top or bottom of the view, depending on scroll direction. The Finder shows this correct behavior in list view but a regular table view seems to not behave this way out of the box. I'm confused as to why that is and see no obvious way to circumvent it. Can anybody point me to possible causes / solutions?
Edit No. 1
A cell based NSTableView behaves in the desired way by default, so this is presumably a bug specific to the view based implementation. I don't want to use a cell based table for unrelated reasons though.
Edit No. 2
I've tried making the table view's parent view layer backed, as well as intercepting the up / down arrow keystrokes to do my own scrolling, but so far I haven't been able to eliminate the flickering.
Edit No. 3
I've created a small sample project that reproduces the issue.
It looks like the selection changes and the old and new selected rows are redrawn. Next the selected row is animated up/down. Disabling scroll animation fixes the issue. Scroll animation can be disabled by subclassing NSClipView and overriding scroll(to:).
override func scroll(to newOrigin: NSPoint) {
setBoundsOrigin(newOrigin)
}
It might have some side effects.
Edit
Copied from zrzka's solution, with some adjustments. Scroll animation is temporarily disabled when using the up arrow or down arrow key.
class TableView: NSTableView {
override func keyDown(with event: NSEvent) {
if let clipView = enclosingScrollView?.contentView as? ClipView,
(125...126).contains(event.keyCode) && // down arrow and up arrow
event.modifierFlags.intersection([.option, .shift]).isEmpty {
clipView.isScrollAnimationEnabled = false
super.keyDown(with: event)
clipView.isScrollAnimationEnabled = true
}
else {
super.keyDown(with: event)
}
}
}
class ClipView: NSClipView {
var isScrollAnimationEnabled: Bool = true
override func scroll(to newOrigin: NSPoint) {
if isScrollAnimationEnabled {
super.scroll(to: newOrigin)
} else {
setBoundsOrigin(newOrigin)
documentView?.enclosingScrollView?.flashScrollers()
}
}
}
Did you try change the view ?
scrollView.wantsLayer = true
If you used Interface Builder:
Select the scroll view
Open the View Effects Inspector (or press Cmd-Opt-8)
In the table, find the row for your scroll view and check the box.
In iOS, a toolbar can be added to any view. In macOS however, it seems only possible to add a toolbar to a window.
I'm working on an app with a split view controller with a toolbar but the toolbar's items only have a meaning with respect to the right view controller's context.
E.g. let's say I have a text editor of some sort, where the left pane shows all documents (like in the Notes app) and the right pane shows the actual text which can be edited. The formatting buttons only affect the text in the right pane. Thus, it seems very intuitive to place the toolbar within that right pane instead of stretching it over the full width of the window.
Is there some way to achieve this?
(Or is there a good UX reason why this would be a bad practice?)
I've noticed how Apple solved this problem in terms of UX in their Notes app: They still use a full-width toolbar but align the button items that are only related to the right pane with the leading edge of that pane.
So in case, there is no way to place a toolbar in a view controller, how can I align the toolbar items with the leading edge of the right view controller as seen in the screenshot above?
Edit:
According to TimTwoToes' answer and the posts linked by Willeke in the comments, it seems to be possible to use Auto Layout for constraining a toolbar item with the split view's child view. This solution would work if there was a fixed toolbar layout. However, Apple encourages (for a good reason) to let users customize your app's toolbar.
Thus, I cannot add constraints to a fixed item in the toolbar. Instead, a viable solution seems to be to use a leading flexible space and adjust its size accordingly.
Initial Notes
It turns out this is tricky because there are many things that need to be considered:
Auto Layout doesn't seem to work properly with toolbar items. (I've read a few posts mentioning that Apple has classified this as a bug.)
Normally, the user can customize your app's toolbar (add and remove items). We should not deprive the user of that option.
Thus, simply constraining a particular toolbar item with the split view or a layout guide is not an option (because the item might be at a different position than expected or not there at all).
After hours of "hacking", I've finally found a reliable way to achieve the desired behavior that doesn't use any internal / undocumented methods. Here's how it looks:
How To
Instead of a standard NSToolbarFlexibleSpaceItem create an NSToolbarItem with a custom view. This will serve as your flexible, resizing space. You can do that in code or in Interface Builder:
Create outlets/properties for your toolbar and your flexible space (inside the respective NSWindowController):
#IBOutlet weak var toolbar: NSToolbar!
#IBOutlet weak var tabSpace: NSToolbarItem!
Create a method inside the same window controller that adjusts the space width:
private func adjustTabSpaceWidth() {
for item in toolbar.items {
if item == tabSpace {
guard
let origin = item.view?.frame.origin,
let originInWindowCoordinates = item.view?.convert(origin, to: nil),
let leftPane = splitViewController?.splitViewItems.first?.viewController.view
else {
return
}
let leftPaneWidth = leftPane.frame.size.width
let tabWidth = max(leftPaneWidth - originInWindowCoordinates.x, MainWindowController.minTabSpaceWidth)
item.set(width: tabWidth)
}
}
}
Define the set(width:) method in an extension on NSToolbarItem as follows:
private extension NSToolbarItem {
func set(width: CGFloat) {
minSize = .init(width: width, height: minSize.height)
maxSize = .init(width: width, height: maxSize.height)
}
}
Make your window controller conform to NSSplitViewDelegate and assign it to your split view's delegate property.1 Implement the following NSSplitViewDelegate protocol method in your window controller:
override func splitViewDidResizeSubviews(_ notification: Notification) {
adjustTabSpaceWidth()
}
This will yield the desired resizing behavior. (The user will still be able to remove the space completely or reposition it, but he can always add it back to the front.)
1 Note:
If you're using an NSSplitViewController, the system automatically assigns that controller to its split view's delegate property and you cannot change that. As a consequence, you need to subclass NSSplitViewController, override its splitViewDidResizeSubviews() method and notify the window controller from there. Your can achieve that with the following code:
protocol SplitViewControllerDelegate: class {
func splitViewControllerDidResize(_ splitViewController: SplitViewController)
}
class SplitViewController: NSSplitViewController {
weak var delegate: SplitViewControllerDelegate?
override func splitViewDidResizeSubviews(_ notification: Notification) {
delegate?.splitViewControllerDidResize(self)
}
}
Don't forget to assign your window controller as the split view controller's delegate:
override func windowDidLoad() {
super.windowDidLoad()
splitViewController?.delegate = self
}
and to implement the respective delegate method:
extension MainWindowController: SplitViewControllerDelegate {
func splitViewControllerDidResize(_ splitViewController: SplitViewController) {
adjustTabSpaceWidth()
}
}
There is no native way to achieve a "local" toolbar. You would have to create the control yourself, but I believe it would be simpel to make.
Aligning the toolbar items using autolayout is described here. Align with custom toolbar item described by Mischa.
The macOS way is to use the Toolbar solution and make them context sensitive. In this instance the text attribute buttons would enable when the right pane has the focus and disable when it looses the focus.
I am creating a tableView in my ViewDidLoad and setting up a custom Source and Cell. That all works great. However the UITableView is displaying separator lines behind my custom view. I can comment out my code for the custom appearance so that the all that loads on screen is an empty UITableView and it still displays the separator lines even though I have set the separatorStyle = UITableViewCellSeparatorStyle.None
var table = new UITableView(View.Bounds);
table.SeparatorStyle = UITableViewCellSeparatorStyle.None;
table.SeparatorColor = UIColor.Clear;
table.TableFooterView = new UIView(CGRect.Empty);
Add(table);
I have also tried setting there color to clear and a blank footer to no avail.
Any help is greatly appreciated.
I know time to time Xamarin ios could be strange, but finally I was able to make it work.
You have to run this code in the ViewDidLayoutSubviews and it works
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
table.SeparatorStyle = UITableViewCellSeparatorStyle.None;
}
You need the following within your override the UITableViewController's ViewDidLoad method (I take it you have subclassed a ViewController for this purpose):
table.SeparatorStyle = UITableViewCellSeparatorStyle.None;
That should do the trick.
Regards,
John
I've searched a lot on the internet for a solution to this problem but I can't figure it out. I'm trying to create a custom cell in a table view.
I made a CustomCell.swift class to configure the labels I want in my custom cell, created it via storyboard (the first prototype cell in the tableview) and linked it with a identifier to the cellForRowAtIndexPath method
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let cellIdentifier = "huisCell"
var cell: CustomCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? CustomCell
if cell == nil {
cell = CustomCell(style: UITableViewCellStyle.Value1, reuseIdentifier: cellIdentifier)
}
cell!.huisAdresLabel.text = "123"
cell!.huisDetailLabel.text = "456"
return cell
}
My CustomCell.swift code is like this:
class CustomCell: UITableViewCell {
#IBOutlet var huisAdresLabel: UILabel
#IBOutlet var huisDetailLabel: UILabel
}
It's very basic now, but I just want it to work because than I can expand the cell with more attributes and style it better.
Pictures via DropBox because I need 10 reputation to properly document my problem :)
https://www.dropbox.com/sh/5v9jb6cqp80knze/AAD5-yPR8-KoStQddkqKIbcUa
I hope someone can explain what I'm doing wrong.
Edit:
To clear up some things, before my try to make a custom cell, I got it working with the basic cells, with the one label on the left hand side. But when I tried to style the tableview and created a custom cell it won't work.
Also, when testing different solutions I came across the problem that de two labels in CustomCell.swift are nil. Even when I made a custom init and did like a
self.huisAdresLabel = UILabel()
it was still nil. in the code that I showed you it prints the following:
<UILabel: 0xb2aadc0; frame = (0 -21; 42 21); text = '123'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0xb2aa3a0>>
I resolved this issue by overriding the following function, setting the height of the cells manually:
override func tableView(tableView:UITableView!, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat
{
return 44
}
Try using a xib file and adding your custom cell class to the
"Table View Cell" that you create in your xib file.
(Make sure you reconnect it to the outlets in your custom cell class ;)
This link may help.
http://www.weheartswift.com/swifting-around/
I had the same Problem, but after disabling the "Use Auto Layout" under the "File Inspector", it did work!! and the Custom Cells are displayed
Note: I made the Custom Cells in the Builder, not in Code. Using Xcode beta 3
Working w/ Custom table cells in Xcode 6 Beta-4's IB, I found they all rendered on top of each other and my non-Custom cell.
I fixed my problem by...
selecting the Custom cell in IB
selecting the Size inspector (Option-Command-5)
in the Table View Cell section
checking the Custom box
keeping the default-provided 44 Row Height
Quick workaround: disable Use Size Classes, but still don't know is it bug or feature :-) needs more investigation or man reading.
I'm trying to customize the disclosure arrow appearance in my view-based NSOutlineView. I saw that it's recommended to use
- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
delegate method to achieve it. The problem is that this method is not called for some reason. I have 2 custom cell views - one for item and second for header item. May be this method is not called for view-based outline views? May be something became broken in Lion?
Please shed some light.
Solution 1:
Subclass NSOutlineView and override makeViewWithIdentifier:owner:
- (id)makeViewWithIdentifier:(NSString *)identifier owner:(id)owner {
id view = [super makeViewWithIdentifier:identifier owner:owner];
if ([identifier isEqualToString:NSOutlineViewDisclosureButtonKey]) {
// Do your customization
}
return view;
}
For Source Lists use NSOutlineViewShowHideButtonKey.
Solution 2:
Interface Builder
The button is added to the column and the identifier set to NSOutlineViewDisclosureButtonKey.
Official documentation from NSOutlineView.h
/* The following NSOutlineView*Keys are used by the View Based NSOutlineView to create the "disclosure button" used to collapse and expand items. The NSOutlineView creates these buttons by calling [self makeViewWithIdentifier:owner:] passing in the key as the identifier and the delegate as the owner. Custom NSButtons (or subclasses thereof) can be provided for NSOutlineView to use in the following two ways:
1. makeViewWithIdentifier:owner: can be overridden, and if the identifier is (for instance) NSOutlineViewDisclosureButtonKey, a custom NSButton can be configured and returned. Be sure to set the button.identifier to be NSOutlineViewDisclosureButtonKey.
2. At design time, a button can be added to the outlineview which has this identifier, and it will be unarchived and used as needed.
When a custom button is used, it is important to properly set up the target/action to do something (probably expand or collapse the rowForView: that the sender is located in). Or, one can call super to get the default button, and copy its target/action to get the normal default behavior.
NOTE: These keys are backwards compatible to 10.7, however, the symbol is not exported prior to 10.9 and the regular string value must be used (i.e.: #"NSOutlineViewDisclosureButtonKey").
*/
APPKIT_EXTERN NSString *const NSOutlineViewDisclosureButtonKey NS_AVAILABLE_MAC(10_9); // The normal triangle disclosure button
APPKIT_EXTERN NSString *const NSOutlineViewShowHideButtonKey NS_AVAILABLE_MAC(10_9); // The show/hide button used in "Source Lists"
This answer is written with OS X 10.7 in mind, for newer versions of OS X/macOS, refer to WetFish's answer
That method does not get called because it is only relevant for cell based outline views.
In a view based outline view, the disclosure triangle is a regular button in the row view of expandable rows. I don't know where it gets added, but it does, and NSView's didAddSubview: method handles exactly that situation of a view being added somewhere else.
Hence, subclass NSTableRowView, and override didAddSubview:, like this:
-(void)didAddSubview:(NSView *)subview
{
// As noted in the comments, don't forget to call super:
[super didAddSubview:subview];
if ( [subview isKindOfClass:[NSButton class]] ) {
// This is (presumably) the button holding the
// outline triangle button.
// We set our own images here.
[(NSButton *)subview setImage:[NSImage imageNamed:#"disclosure-closed"]];
[(NSButton *)subview setAlternateImage:[NSImage imageNamed:#"disclosure-open"]];
}
}
Of course, your outline view's delegate will have to implement outlineView:rowViewForItem: to return the new row view.
Despite the name, frameOfOutlineCellAtRow: of NSOutlineView still gets called for view based outline views, so for the positioning of your triangle, you might want to subclass the outline view and override that method, too.
For Swift 4.2 macOS 10.14, #WetFish's answer can be implemented as follows:
class SidebarView: NSOutlineView {
override func makeView(withIdentifier identifier: NSUserInterfaceItemIdentifier, owner: Any?) -> NSView? {
let view = super.makeView(withIdentifier: identifier, owner: owner)
if identifier == NSOutlineView.disclosureButtonIdentifier {
if let btnView = view as? NSButton {
btnView.image = NSImage(named: "RightArrow")
btnView.alternateImage = NSImage(named: "DownArrow")
// can set properties of the image like the size
btnView.image?.size = NSSize(width: 15.0, height: 15.0)
btnView.alternateImage?.size = NSSize(width: 15.0, height: 15.0)
}
}
return view
}
}
Looks quite nice!
Swift2 version of #Monolo's answer:
override func didAddSubview(subview: NSView) {
super.didAddSubview(subview)
if let sv = subview as? NSButton {
sv.image = NSImage(named:"icnArwRight")
sv.alternateImage = NSImage(named:"icnArwDown")
}
}