I have an application derived from the Xcode Cocoa Application template that prints properly on El Capitan, but introduces a centimetre margin all round with a shadow box on Sierra. Changing the margin values (in the shared instance in viewDidLoad of the viewController) to larger positive values makes the margins bigger, but reducing them to zero still leaves the margin and shadow box. The image is a screen-shot of the print preview. I would like to go back to having the PDF take up the whole printed page. Looking at the menu for the File/Print shows it executes firstResponder print:. Should I try to override this function? But if the code below is being executed to no effect, what good will this do?
NSPrintInfo *PageDefaults = [NSPrintInfo sharedPrintInfo];
[PageDefaults setBottomMargin:0];
[PageDefaults setLeftMargin:0];
[PageDefaults setRightMargin:0];
[PageDefaults setTopMargin:1];
If I increase the top and left margins in the code above to 120, the print changes to:
The storyboard shows that I am only using a PDF View control that fills the entire viewController scene
And finally, the viewController on screen shows it looks as it should:
-[PDFView print:] in 10.10 calls printWithInfo:autoRotate:pageScaling:. In 10.12 -[PDFView print:] calls printOperationWithView: and prints the background. Solution: create a subclass of PDFView, override print: and call printWithInfo:autoRotate:pageScaling:.
- (void)print:(id)sender {
[self printWithInfo:[NSPrintInfo sharedPrintInfo] autoRotate:YES pageScaling:kPDFPrintPageScaleToFit];
}
Related
I am using storyboard layouts to set up view layout.
I am supporting both iPhone and iPad layouts.
When the view is created with initWithCoder:, it is initially created with the frame size of the device I was last looking at in Interface Builder.
If I am designing with the iPhone X layout in interface builder and then build and run on an iPad, the view is initially created with iPhone X screen dimensions. Then viewDidLayoutSubviews: is called and it updates the screen dimensions to the correct iPad size.
The subviews are using drawRect inside UIViews to draw the view graphics. I am doing this so I can change graphic colors via code. I change the color variable and then call setNeedsDispay on the view to redraw the view with new colors using CGGraphicsContext commands.
It also allows me to draw any graphic image at any size. And with lots of graphics that means I don't have to include all the different images at 1x, 2x and 3x sizes in my bundle. It's all drawn dynamically.
Some of these images are laid out when the view loads and not in Interface Builder. So I check the screen size and draw the button size and position accordingly.
What happens is, viewDidLoad is called and it draws the graphics based on the initial screen size.
Then viewDidLayoutSubviews is called and I have to update the drawing of the subviews I placed manually repositioning them based on new screen dimensions and then calling the drawRect on them. I feel like this is just unnecessary extra work for the device.
In addition to that, viewDidLayoutSubviews is called for other reasons then just resizing the view on initial load of the viewController. So then each time it's called it will go redraw the subviews, even if they don't need it.
And, if the device I am running on is the same as the device I was using in Interface Builder, it doesn't call the viewDidLayoutSubviews. I can't just let the view layout the subviews there because there is no guarantee it will be called.
My solution so far is creating a variable to track the screen width. I set the variable in viewDidLoad. If it creates the view at iPhone X size, my screenWidthTracker = 1125. When viewDidLayoutSubviews is called I compare the current screen size to screenWidthTracker.
if (self.view.frame != screenWidthTracker) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateView" object:self];
};
if the view has changed size, it sends a message to redraw views. Any views I have placed manually as subviews are registered to listen for #"updateView".
Is there a better way to manage this? Is there a method that gets called ONLY when screen dimensions change and not when its updating the position of other subviews? Should I be utilizing viewDidAppear? I feel as though that is too late in the chain and I don't want the user to see button size updates.
In general, from the description you've given, I'd say that at the very most, you should be doing as much size work as possibly by using autolayout (which you can configure entirely within the storyboard) and then implementing only drawRect and viewDidLayoutSubviews — and the latter only if you need to.
A very common strategy is to use a boolean flag to set up initial conditions in the first call to viewDidLayoutSubviews, and not use it thereafter. Screen size changes after launch (not at launch), such as rotation, are detected by implementing willTransition(to:with:) — and even then you should check to see that the old size and new size are not the same (180 degree rotation) and do nothing if they are.
experts!
Today started to build Apple Watch version for my CelebrateMore! app and can't solve one issue. I have 2 interface controllers - Page-based kind. As soon as I set segue to "next page" from first to second, my first interface controller starts to scroll much more than before.
Screenshot from Storyboard:
Screenshot from simulator during scrolling:
Screenshot from simulator if I remove next page segue and scroll:
Already from scrollbar it can be seen, that there is a lot of empty content if "next page" is used. Does anyone has some idea, how to solve this, how to avoid "the long after content scrolling" issue?
I've struggled with the same issue for several hours until I decided to give it up for a while and focus on filling the labels with actual data at runtime. After I added some code to the main initialisation methods of WKInterfaceController ((void)awakeWithContext:(id)context {} and/or (void)willActivate {} ), this scrolling issue magically disappeared. Try to set the text of one or all the labels in your interface in code in one of the 2 methods mentioned above and see if the scrolling problem gets fixed:
[self.label setText:#"Some text.."];
It worked in my case.
Due to low reputation I cannot upvote: The solution of setting the text of a label worked for me too. I set an empty text first, then load the data and update the label there again once the request is successful. It works and calculates the height of the scroll view correctly.
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
//enable correct scroll height
self.titleLabel.setText("")
self.loadData()
}
I used a different way to work around it.
First put a group in the InterfaceController, and assign fixed height to it, then put other components inside the group.
To make the group occupy full screen, its height for 38mm should be set to about 142 point and for 42mm should be set to about 165 point. You can use the plus sign next to the value to enter these values for the 2 models.
In my app there's a lot of wasted space in landscape on the iPhone 6, and to a lesser extent even on 4" screens. I'm already using iOS 8's UISplitViewController changes to support the two-pane view in landscape on the iPhone 6 Plus, but it'd be useful to see both panes on some smaller devices as well.
Conveniently Apple had a WWDC 2014 session, "Building Adaptive Apps with UIKit" which included details on exactly how to do this. You can download the sample code here, but in short: they put the UISplitViewController inside of a UIViewController subclass. The subclass uses setOverrideTraitCollection:forChildViewController: to force [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular] on the split view when it considers the width wide enough. At the time the sample code worked well, and still does on most devices.
However after attempting to use this code in my own app, I discovered it fails horribly on the iPhone 6 Plus. You can see this yourself if you download the sample code and make two changes:
Add a storyboard, add an empty view controller to that, and set it as the "Launch Screen File". This is necessary to run the app at its native resolution on the 6 Plus.
In AAPLTraitOverrideViewController.m, change line 21 to size.width > 500.0, or anything greater than 414. This is necessary to ensure the split view only shows a single view in portrait on the 6 Plus.
Now you can run the app in the simulator. To see the problem, just do this:
Rotate the device to landscape (command-right arrow)
Rotate it immediately back to portrait (command-left arrow)
You can already see that something's not right. All of the table cells should have an arrow on the right side in portrait, but they don't. They're behaving like they're still in a split view. If you tap one of those rows, it gets worse—the detail view slides up from the bottom, and the navigation bar is gone.
I think there must be a bug in iOS 8 here that's causing the problem. But since this code was shared before the iPhone 6 Plus was announced, it also seems possible that it just needs some adjustments to make it compatible with that device. So far the only solution I've found is to change line 21 to something like if (size.width > 500.0 && size.width < 736.0) but I don't want to use code that could break again the next time Apple introduces a new screen size. Is there a better way to handle this?
Seems like you'll always want to make the horizontal size class regular (UIUserInterfaceSizeClassRegular). To do this, override traitCollectionDidChange:. In this method, if the vertical size class is compact (suggesting it's likely in landscape), override the trait collection so that the horizontal size class is regular.
UITraitCollection *compactHeight = [UITraitCollection traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact];
if ([self.traitCollection containsTraitsInCollection:compactHeight]) {
UITraitCollection *regularWidth = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
self.forcedTraitCollection = [UITraitCollection traitCollectionWithTraitsFromCollections:#[self.traitCollection, regularWidth]];
[self setOverrideTraitCollection:self.forcedTraitCollection forChildViewController:_viewController];
} else {
[self setOverrideTraitCollection:nil forChildViewController:_viewController];
}
However, if you'll want more specific behavior, you'll have to rely on canvas size for app-specific behavior.
I'm experiencing what I think is a very strange issue.
First and foremost, my application moves UI elements around the screen using the following example command:
[view setFrame:NSRectFromCGRect(CGRectMake(0, 0, 0, 0))];
I give my users the possibility to resize the application's NSWindow to one other size with this command:
[self.window setFrame:NSRectFromCGRect(CGRectMake(0, 0, 0, 0)) display:YES animate:YES];
When in 'resized mode', I obviously change every single setFrame command to the appropriate coordinate system.
However, there is an issue: when (if) the user switches back to the original NSWindow size by clicking on the button again, and the application performs the UI movements again, the views that are moved around are not in the location they are supposed to be in.
To clarify: my UI movement code runs fine, over and over, either in normal or resized mode. However, if I switch from one to the other, some items (not all) are shifted.
What could be causing this strange behavior? I'm using the same exact commands (within each screen resolution), and NSLogs confirm the UI elements are in the location that I specified; however, this location is clearly not the one I'm attempting to move to.
Has anyone experienced a similar issue here?
You've probably set some sort of autoresizing mask in Interface Builder that's interfering with your manual placements. Go in to your nib and remove all the springs and struts in the Size inspector. Although if possible you should let autosizing handle the placement, or move to Auto Layout which was introduced in 10.7.
After days and days of trying to figure this out, I've come to the following conclusion: the resizing code simply wasn't working.
I confirmed this by using NSLog after I resized my window. In the end, I changed my resizing code by adding the following line after the traditional setFrame: method:
[self.window setContentSize:NSSizeFromCGSize(CGSizeMake(desiredWidth, desiredHeight))];
I have a custom subclass of UIView and a full screen UIButton with a background image on it as you can see in the image,
on the right side, there is a UIView with a UIImageView on it and some other not important labels and buttons. The important part is, there are two scrollviews on that UIView and at the run time, i add UIButtons ( not small ones, 200 x 100 ) on those scrollviews with vertical scrolling enabled.
The situation is really very complex so i will write as items what happens in different conditions. In my application, there are 10 of these complete screens, on a custom subclass of UIView and i use UIPanGestureRecognizer to scroll from one to other.I have a good reason not to use a scrollview for this. All the windows ( the below image, not UIWindow ) populates their content in a loop, so the running code is exact same for all of them and all of them are being created from the same .xib file. And the last information, the bigger UIButton that covers all the window has an action for touchupinside and the smaller buttons in scrollviews have their own touchupinside selectors.
Some of my windows works perfectly, if i touch anywhere on the screen, the bigger buttons action is called, if i touch and drag, the scroll works and i navigate to the next / previous window, if i touch on smaller buttons in scrollviews, their actions fired and lastly i can perfectly scroll within those buttons.
Some of my windows, when i try to scroll the scrollviews, the bigger windows pangesturerecognizer catchs this event not the small scrollviews, and if i touch the small button in that scroll view, the bigger buttons event is fired ( it pass all through uibutton > uiscrollview > uiimageview > uiview to the uibutton at the button like they dont exist )
if i replace the right container view some other position on the big uibutton, it randomly works well or not, sometimes one of the scrollviews works well and the other not.
it has a consistent behaviour, if on a one position, it works, it allways works, and if it not, it never does.
Again, all the views and subclasses have their userinteraction enabled yes, the opposite is already imposible because the behaviour changes only acording to position of container view on the big uibutton and also changes acording to big uibuttons background image.
I've placed a touchesBegan method for just test purposes, when the touches does not work as expected, the event fired with touch.view is the container of the big button, even if i touch on a small button within the scrollviews.
I've spend two days already for that and no result.What can cause this behaviour ?
EDIT : After krumelur's comment I've changed my focus from configuration of those views to animation i give to the container views, and i've noticed that, the problem is about the animation, i'm adding the the code part that animates all ten windows and behaves like a custom scrollview. All windows have their custom layer class and when i catch pangesture recognizer i move windows on the screen with following code. items in that code is an array which holds layers of all the windows. The animation causes that strange situation but i couldnt figure out yet..
- (void)layoutSublayers {
[super layoutSublayers];
[CATransaction begin];
[CATransaction setDisableActions:YES];
float angleDelta = 2 * M_PI / [items count];
float a = angle;
for (UIBaseLayer *l in items) {
l.position = self.position;
CATransform3D translation = CATransform3DMakeTranslation(cosf(a)*(radius.x), 1.0, (sinf(a)*radius.y) - radius.y*1.0);
float dailyAngle = (M_PI_2 - a);
CATransform3D rotation = CATransform3DMakeRotation(dailyAngle, 0, 1.0, 0);
CATransform3D t = CATransform3DConcat(rotation, translation);
l.transform = t;
a += angleDelta;
}
[CATransaction commit];}
http://img836.imageshack.us/img836/296/screenshot20111215at013.png
bringSubviewToFront did not work reliably on 5.0 (on 5.1 the hack was not needed)
I had to add a bigger transparrent UIButton around the button that behaved as if it was disabled to fix this on iOS prior to 5.1.
At last I've figured out that, even I send back views with CATransform3DMakeTranslation and layer.zPosition settings, touch events goes to views according to their frame position. The views that aren't currently even seen (overlayed by others) still catch touches. Writing just one line of code ([self.superview bringSubviewToFront:self]) for the view that is currently on screen solved the problem.
For testing this and understanding what I'm talking about, I've made simple test application that has only two buttons on it.
The blue and yellow buttons.The blue button overlays the yellow button so you can only see the blue one at the beginning.
On the touch up inside code for the blue button, I just send it back with buttons.layer.zPosition = -1. After this, there is no visible part of the blue button remains on screen, instead I see just the yellow button now, but when I touch the yellow button (or I think I am touching it) still blue buttons touch up inside code runs.
I'm not sure if this is the desired logic but it does not seem reasonable to me.
As I mentioned above, writing [blueButton.superview sendSubviewToBack:blueButton] while setting its layers zPosition to -1 works as expected.