How can I keep my window always zoomed? - macos

So I have storyboard with 2 view controller's and 1 window controller. In my subclass of NSWindowController I can put my window zoomed like this:
override func windowDidLoad() {
super.windowDidLoad()
window?.zoom(self) // Or window?.setFrame(NSScreen.mainScreen()!.visibleFrame, display: true)
}
But when I want to change my window's content view controller, that size doesn't remain same size and I have to tell to zoom it again and that is ugly animation. How can I manage to keep my window's zoomed even if I change my window's content view controller?
UPDATE:
I manage to get it work. In my IBAction when my another window controller is zoomed, I did this when I want change content view controller.
secondViewController!.view.frame = contentViewController!.view.frame
contentViewController = secondViewController

Related

NSProgressIndicator on Modal Sheet Doesn't Animate

My main window controller has a toolbar item that triggers the presentation of a modal sheet. The sheet is supposed to display the progress of a lengthy, asynchronous process (e.g., sync local data with a server).
However, I can not get the (indeterminate) progress indicator to animate.
This is the action that triggers the modal sheet:
var syncProgressWindowController: SyncProgressWindowController!
// ...
#IBAction func syncWithServer(_ sender: AnyObject) {
// (Actual HTTP code not implemented)
syncProgressWindowController = SyncProgressWindowController()
syncProgressWindowController.loadWindow()
guard let modalWindow = syncProgressWindowController.window else {
return
}
self.window?.beginSheet(modalWindow, completionHandler: { (response) in
// THIS GETS EXECUTED.
// However, the code below has no effect:
self.syncProgressWindowController.progressIndicator.startAnimation(self)
// self.syncProgressWindowController.progressIndicator is
// NOT nil, despite windowDidLoad() not being called
// (see below)
})
}
The modal sheet window controller (class SyncProgressWindowController above) is defined like this:
#IBOutlet weak var progressIndicator: NSProgressIndicator!
convenience init() {
self.init(windowNibName: "SyncProgressWindow")
}
override func windowDidLoad() {
super.windowDidLoad()
// Breakpoints here don't work, logs don't print to the console.
// Not called? But outlet _is_ set (see above).
}
The xib file (SyncProgressWindow.xib) has:
File's Owner Identity/Class set to "SyncProgressWindowController"
Window has New Referencing Outlet to File's Owner's window
Window has delegate outlet wired to "File's Owner" (just in case - but delegate methods don't seem to get called either).
Window has "Visible at Launch" unchecked (and is therefore displayed modally with no problems).
Progress has New Referencing Outlet wired to File's Owner's progressIndicator.
However:
SyncProgressWindowController's windowDidLoad() does not get called (Execution does not stop at breakpoints there and logs aren't printed).
Despite that, the property/outlet progressIndicator is set somehow, because the app does not crash when I attempt to animate it, with code like this:
self.syncProgressWindowController.progressIndicator.startAnimation(self)
What am I missing?
completionHandler will be fired when you close sheet by endSheet(_:returnCode:) So you start indicator before sheet will be closed.
I'm not good in xib files, but when i disabled row with loadWindow, windowDidLoad was called. I'm not sure it's right way.

NSViewController disable resize when presented as Sheet?

So I am presenting an NSViewController as a sheet of a window that has resize disabled.
The view controller that is presented as a sheet can still be resized.
How do I disable resizing of a NSViewController?
Swift 4:
override func viewDidAppear() {
// any additional code
view.window!.styleMask.remove(.resizable)
}
By the way you can do this without writing code, here is how:
Drag a Window Controller element to the Storyboard from the Object
Library.
Connect the Window Controller to the specific View Controller which you want to disable resize.
On the Window Controller's Attributes uncheck Resize option.
After some more trying I found out this did the trick in viewDidLoad:
self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height);
If you add these methods, the issue will be fixed.
- (void)updateViewConstraints NS_AVAILABLE_MAC(10_10) {
[super updateViewConstraints];
}
- (void)viewWillLayout NS_AVAILABLE_MAC(10_10) {
self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height);
}

Xcode XIB: how to implement a ScrollView or PageControl to swipe sub-views?

I'm building a typical Xcode 6 iOS app.
My goal is:
A screen that has an sub-area that can be swiped to change the content.
For example, the home screen has a logo image, a middle area that I want to be swipeable, and a bottom button.
When the user swipes (or taps) the middle area, the area shows the next (or previous) information, which is a typical UIImage and UILabel caption.
The rest of the screen stays the same, i.e. there is no navigation change.
The code is here. It use the recommendations from the StackOverflow post here.
My question: how can I implement the code below better, while still using an XIB?
My current implementation does work, and uses this approach...
A typical Swift Demo.swift file that is a UIViewController that has:
the page index, min, and max
outlets for the PageControl, UIImageView, and UILabel
actions for the page control change, and the image swipe or tap
A typical Demo.xib file that has:
a typical UIViewController for the entire screen
a UIImageView and UILabel for the changeable image and caption text
a PageControl to indicate what tutorial page the user is viewing
I am seeking better ways to accomplish this; I've read many of Xcode tutorials and so far none seem definitive for Xcode 6, XIBs, and Swift.
Here are some implementations that I've researched that seem promising...
Is there a way to implement a subview area in the XIB?
For example, can Xocde show the XIB with a rectangular area that is intended for the changeable content?
Is there an idiomatic way to write the code for changeable content?
For example, by using a ScrollView, perhaps that contains a UIPageViewController?
Is there a way to make a PageControl XIB object large enough to cover the entire UIImageView and UILabel, so I can skip making the UIImageView respond to gestures.
In my Xcode, the PageControl seems to have an uneditable height that is always 37.
The bounty will be for expert advice.
To make a UIPageViewController swipe-able you should implement the UIPageViewControllerDataSource protocol and provide a view controller for the pageViewController(pageViewController:viewControllerBeforeViewController) -> UIViewController? and the ...viewControllerAfterViewController) methods.
Provide a custom view controller for each page that presents an image and label and takes them as properties so you can provide them from the PageViewController.
My trick it to create a method that instantiates a new view controller in these methods:
// MARK:- UIPageViewControllerDataSource
extension MyPageViewController: UIPageViewControllerDataSource {
func viewControllerWithIndex(var index: Int) -> UIViewController! {
let viewController = storyboard?.instantiateViewControllerWithIdentifier("MyViewController") as! MyViewController // This VC has to be in the storyboard, otherwise just use MyVC()
// Adjust the index to be cyclical, not required
if let count = data?.endIndex {
if count == 1 && index != 0 { return nil }
if index < 0 { index += count }
index %= count
}
viewController.view.tag = index
viewController.record = data?[index]
return viewController
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let index = viewController.view?.tag ?? 0
return viewControllerWithIndex(index + 1)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let index = viewController.view?.tag ?? 0
return viewControllerWithIndex(index - 1)
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return countAndSetupPageControl()
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return viewController?.view.tag ?? 0
}
}
Now for the "sub-area" you will need to implement a ChildViewController. If you're using storyboards you can just drag a Container View and put PageViewController in the embedded view controller, otherwise you need to add the PageViewController.view as a subview and set the frame to the middle.
You can find more info in the apple documentation but basically you MUST call these methods:
addChildViewController(pageViewController)
view.addSubView(pageViewController.view)
pageViewController.view.frame = ... // This is your "sub-area"
pageViewController.didMoveToParentViewController(self)
If you add a height constraint to PageControl you can set it's height to whatever you want.
I don't see a problem with your current implementation. Changing it to use a PageViewController would be quite more work.
If I were you I would add an animation in pageUpdate function so the image would fade in or slide in...
It would only make sense to use a PageViewController if you want to be able to scroll to the next page (as in content moving in the same time your finger is moving onscreen). And you can use a PageViewController or a CollectionView with paging enabled.

preferredContentSize does not shrink

I use UIPopoverController to popover a window. I use preferredContentSize to set the size. Then, I use Push a new view controller with larger size. When the child view pop up, I like to recover the window to original size. But seems not work.
I already put the self.preferredContentSize = myWindowSize; But it seems can not recover.
In iOS6 or before, I set the contentSizeForViewInPopover to CGSizeZero, then set the proper size. But on iOS7, it will make the popup disappear.
This solved it for me:
override func viewDidAppear(animated: Bool)
{
navigationController?.preferredContentSize = preferredContentSize
super.viewDidAppear(animated)
}
The only solution I can think of is to dismiss your popover window and automatically present it again with the new size you want. I'm also having trouble for iOS 8 where the content size won't shrink in height. The width shrinks, but the height can only increase for me.
Have a look at
https://developer.apple.com/library/ios/documentation/uikit/reference/UIPopoverControllerDelegate_protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIPopoverControllerDelegate/popoverController:willRepositionPopoverToRect:inView:
It might help you reposition the rect with the delegate protocol
Solution:
Ok, here's my solution. From the view controller where you present the popover controller, you can set up a delegate called something like "resetContentSize:(CGSize)size". e.g. in your presenting view controller's header file:
#protocol PopoverDelegate <NSObject>
- (void)resetContentSize:(CGSize)size
#end
in the implementation file where you present the popover controller, you can set the delegate
self.popoverController.delegate = self;
From your popover controller, you can add the PopoverDelegate in the header file
#property (nonatomic, weak) id<PopoverDelegate> popoverDelegate;
and call the delegate method in your implementation file:
[popoverDelegate resetContentSize:CGSizeMake(320.0f,205.0f)];
Had the same issue. The solution for me was to set preferredContentSize on the navigationController, instead of self.
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.preferredContentSize = myControllerSize
}

Resize NSPopover after it is shown

I have an NSPopover who's content in an NSView. I create the popover and add the view like this:
NSPopover *thePopover = [[NSPopover alloc] init];
[thePopover setContentViewController:myViewController];
I then show the popover and set the size:
[thePopover setContentSize:NSMakeSize(300.0f, 160.0f)];
At this point the popover is visible and its content view is correct. Is there a way that I can resize the popover at this point without closing and re-showing it?
I was able to achieve this by calling back to the controller that launched the popover via a delegate protocol and resetting the size using the setContentSize:
From the documentation:
The popover's content size is set to match the size of the content view when the content view controller is set.
So, set the frame of the popover's content view.
NSWindow has private property _popover that store a reference to NSPopover.
You can use it to access to NSPopover instance from any NSViewController (and then call setContentSize: on it) like this:
- (NSPopover *)popover
{
NSWindow *window = self.view.window;
NSPopover *popover = [window valueForKey:#"_popover"];
return popover;
}

Resources