There is a new property of UIView: layoutMargin in iOS8.
In order to set the margins of a view programmatically:
self.view.layoutMargins = UIEdgeInsetsMake(0, 0, 10, 10);
Is it possible to set margins in Interface Builder?
This can be achieved by setting a User Defined Runtime Attribute in the Identity Inspector:
Although Xcode does have a margins setting, as of 7.3 (7D175), it still doesn't have any effect, so you have to use the user defined attribute:
Related
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 using Xamarin.Form platform to use Map feature in my application. I could able to add an annotation on the map. However, I would like to know is there a way to add disclosure indicator on annotation that enables user to tap to go to DetailViewController.
using Xamarin.Forms.Maps;
Map map;
Title= "MapView";
map = new Map {
IsShowingUser = true,
HeightRequest = 100,
WidthRequest = 960,
VerticalOptions = LayoutOptions.FillAndExpand
};
map.Pins.Add(new Pin {
Position = new Position(29.7,-95.0177232),
Label = "Boardwalk"
});
I want something similar to the following screnshot.
I don't believe this is possible to provide your own views in the current Xamarin.Forms Maps component (v1.2.3x) as it is very locked down.
Update 1:-
Unfortunately not. This is a requested feature for Xamarin.Forms, as of October 24 here.
The only way around at present would be to create your own custom renderer.
I want to change the width of the InfoWindow of google map iOS SDK. Is there anyway to do?
The reason I want to do it is to show the full address in the InfoWindow. Is there any other way to do so instead of customizing the InfoWindow?
Also, can I change the text size in the InfoWindow?
At least, I would like to achieve the effect like this map:
http://www.google.com/about/company/facts/locations/
where hyperlink can be applied as well.
Set the map delegate to the view controller, then implement the google maps delegate method
-(UIView *)mapView:(GMSMapView *) aMapView markerInfoWindow:(GMSMarker*) marker
{
UIView *view = [[UIView alloc]init];
//customize the UIView, for example, in your case, add a UILabel as the subview of the view
return view;
}
The question: Why having a header view prevents scroll view from being resized by auto layout?
I'm trying to embed my custom view in a scroll view, which in turn is enclosed in a split view. I've created the following view hierarchy using Interface Builder in Xcode 4.5 DP 4, but the same problem seems to happen also in Xcode 4.4.
NSWindow
NSView (content view of the window)
NSSplitView
NSView (split view panel)
NSView (split view panel)
NSScrollView
TestView (my custom view)
Now, if TestView provides a NSTableHeaderView (via -headerView) property the split view divider cannot be dragged all the way to bottom (or right) to hide the TestView but stops to the boundary of the initial width or height of the TestView. If the -headerView property returns nil, the divider can be dragged freely.
This can be reproduced every time, just by creating a fresh Cocoa application project, adding the views and running the project. The steps:
Create a new Cocoa Application project
Create TestView class with headerView property which returns a NSTableHeaderView instance.
Edit MainMenu.xib and add a split view
Add custom view and make it TestView
Choose Editor -> Embed in -> Scroll view
Run the project
(No constraints or other Interface Builder menus touched)
TestView.m:
#implementation TestView {
NSTableHeaderView *_header;
}
- (NSTableHeaderView *)headerView
{
if (!_header) {
_header = [[NSTableHeaderView alloc]
initWithFrame:NSMakeRect(0.0, 0.0, 100.0, 17.0)];
}
return _header;
}
#end
Any pointers, what should I do to get the split view divider moving again?
Implement this NSSplitViewProtocol method in a convenient class:
- (BOOL)splitView:(NSSplitView *)splitView shouldAdjustSizeOfSubview:(NSView *)subview {return TRUE;}
Make sure to connect the split view's delegate output the class object.
The split view can now be adjusted to any size.
My solution was to manually remove the autoresizing constraints of the table header:
NSTableHeaderView *headerView = outlineView.headerView;
NSView *headerViewSuperview = headerView.superview;
[headerViewSuperview removeFromSuperview];
headerView.superview.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:headerViewSuperview];
In IB this can be done easily by checking the 'Resize' checkbox on or off.
My problem is I want my main NSWindow to not be resizable, until a button is clicked, and then i want it to be resizable.
I've scoured the Internet but can't find anything? Can a window not be made to be resizable or not programmatically?
Thanks in advance everyone!
Since 10.6, you can change the style mask of a window using -[NSWindow setStyleMask:]. So, you'd do something like this:
In Objective-C
To make it resizable:
window.styleMask |= NSWindowStyleMaskResizable;
To make it non-resizable:
window.styleMask &= ~NSWindowStyleMaskResizable;
In Swift
To make it resizable:
mainWindow.styleMask = mainWindow.styleMask | NSWindowStyleMaskResizable
To make it non-resizable:
mainWindow.styleMask = mainWindow.styleMask & ~NSWindowStyleMaskResizable
The Swift 3 solution to this issue is to use the OptionSet class described at:
https://developer.apple.com/reference/swift/optionset
In short:
To replace the set of flags, you now do something like:
myWindow.styleMask = [ .resizable, .titled, .closable ]
To add a flag, do something like:
myWindow.styleMask.insert( [ .miniaturizable, .fullscreen ] )
To remove a flag, something like:
myWindow.styleMask.remove( [ .resizable ] )
You can't change the style mask of a window after creating it, but you can set the window's minimum and maximum frame size to the same size. Do that after you and your resizable window awake from nib, and then change the maximum and optionally the minimum size back when the user clicks the button.
In Swift 3,
if enabled {
window.styleMask.update(with: .resizable)
} else {
window.styleMask.remove(.resizable)
}
In Xcode 8 / Swift 3, try something like:
// e.g., on a view controller’s viewDidAppear() method:
if let styleMask = view.window?.styleMask
{
view.window!.styleMask = NSWindowStyleMask(rawValue: styleMask.rawValue | NSWindowStyleMask.resizable.rawValue)
}
I am new to macOS development. None of the above solutions worked for me (Swift 4.2). I also didn't learn that much (copy/paste doesn't help with that).
I have created this NSWindow subclass. I use it in Interface builder.
class MainDocumentChooserW: NSWindow {
// NOTE: This method is called when the Window is used via Interface builder.
// Setting the styleMask like this will override the IB settings.
override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) {
super.init(contentRect: contentRect, styleMask: style, backing: backingStoreType, defer: flag)
styleMask = [.miniaturizable, .titled, .closable]
}
}
Tested on macOS: High Sierra 10.13.6 (17G5019).