Change UISplitViewController displayMode property when entering multitasking? (iOS 14) - ios

How can I detect when iPad multitasking (i.e. another app brought on screen, side-by-side with my app) becomes active?
I have a UISplitViewController with the following parameters set up as below:
preferredDisplayMode = .twoBesideSecondary
preferredSplitBehavior = .tile
setViewController(SidebarVC(), for: .primary)
setViewController(SuppVC(), for: .supplementary)
setViewController(SecondaryVC(), for: .secondary)
In landscape orientation, there is plenty of room for all 3 columns (.primary, .supplementary, .secondary). In portrait or when multitasking, the screen width is narrower and my 3rd column (.secondary) gets squished to a very narrow width. The horizontal size class remains regular so I can't use that to detect it. (When horizontal size class becomes compact, my UISplitViewController.compact shows and works just fine).
I'm wanting to change the UISplitViewController.displayMode property to .twoDisplaceSecondary when the screen is narrower, but the horizontal size class remains regular (i.e. when in portrait, or when another app is in multitasking). This would allow the 3rd column to be pushed off-screen (displaced) rather than squished.
A perfect example of the exact behavior I'm looking for is the Contacts app on iPad in iOS 14. It uses a triple column layout, seemingly with a displayMode of .tile. When the app is full screen in landscape, all 3 columns are displayed and there is enough room for no squishing to occur. However, if you bring another app into multitasking (thus making the Contacts app width narrower, but horizontal size class remains regular), the displayMode seems to change to .twoDisplaceSecondary, and the 3rd column is pushed off-screen rather than being squished.
I think I can manage orientation changes via viewWillTransitionToSize() method, but I haven't been able to find a delegate or other way to detect when the app is narrower, but still with a .regular horizontal size class, like the Contacts app, is seemingly able to do.
Screenshots to show what I'm referencing:
You can see that in the second screenshot, the 3rd column is displaced rather than squished when there isn't enough space.

I figured out that it's important to set BOTH .preferredDisplayMode and .preferredSplitBehavior when the view transitions. Setting just one or the other will result in lots of weird behavior.
For future readers, what I figured out works for this case is setting preferredDisplayMode = .twoBesideSecondary and preferredSplitBehavior = .tile when the screen is wide enough, then set preferredDisplayMode = .oneBesideSecondary and preferredSplitBehavior = .displace when the screen is narrow. I used a function that checks if the view.frame.size.width < 1194 to determine if the screen is full width or not, as using orientation only would cause problems if the app is side by side with another. I call this in viewDidLoad as well as viewWillTransition(toSize:).

viewWillTransitionToSize has a size param containing the width. Usually you would check for a width less than 1024.

Related

How do I make my app display another screen for landscape left and landscape right, iPhone X iOS

I suppose this is a more basic question. I am stuck with this app I am coding for the iPhone X. I have it set to display a lot of buttons on the Main.storyboard file. Everything looks how I want to look but the problem is I do not know how to make the screen change with landscape rotation left and landscape rotation right. The screens looks the same but I want to style the page differently.
Xcode 9.1
iOS 11
Swift 4.03
If you want a an actually different layout for landscape (and not simply an adaptive one) you should use size classes:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/Size-ClassSpecificLayout.html
This will enable you to make completely different UI for different orientations.
Interface Builder lets you customize many of your layout’s features based on the current size class. The layout then automatically adapts as the size class changes.
...
As the view’s size class changes (for example, when you rotate an iPhone or switch an iPad app between full-screen and Split View), the system automatically adds items to or removes them from the view hierarchy. The system also animates any changes to the view’s layout.
If the only reason you want different layouts in landscape right and landscape left is to avoid the “notch” at the top of the screen and the gesture area at the bottom, then what you really should be doing is laying out your views relative to the “safe area”, which exists for just this reason. If your views are laid out relative to the safe area layout guides, then they’ll automatically adjust to avoid the notch and the gesture area automatically, regardless of orientation.
https://developer.apple.com/documentation/uikit/uiview/positioning_content_relative_to_the_safe_area
You could make two xib files for each specific orientation and then using what user dfd suggested, load the xibs respectively in the following code:
if UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft {
//Load xib for landscapeLeft
} else if UIDevice.current.orientation == UIDeviceOrientation.landscapeRight
//Load xib for landscapeRight
{
This would allow you to style the orientations differently.
Good Luck,
Arnav
Source:
https://stackoverflow.com/a/38629833/8503080

UIScrollView scroll enabling

I'm currently working on a Swift app that requires a few UIScrollViews, however I'm running into some problems making it work for all device sizes.
In this situation, I would have a content that would all appear on an iPhone 6, but not necessarily on an iPhone 4 or 5.
I've got a scroll view set up with some content and I would like to make it scrollable only if you don't see the full content.
Is there a way to perhaps detect if a certain element is visible?
the property scrollView.isScrollEnabled = true by default, and if the content is bigger than screen the user will be able to scroll.

How do I get a button to position on the bottom of a view controller in Xcode 7.2?

I used to be able to do this:
UIButton *bigBottomBtn=[[UIButton alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height-60, self.view.frame.size.width, 60)];
I also used to be able to just drag a button onto a storyboard and add a constraint that would hold it to the bottom of the parent.
What is going on with Xcode, Autolayout and Apple for that matter....is my Xcode not working properly? Have I missed a major memo? is Apple just going downhill fast?
Your button-creating code used to work (and still does) if self.view's frame was correct at the time you created the button. Note that the view doesn't necessarily come out of the xib or storyboard with the correct frame; the xib/storyboard contains the view at some design size which might not match the current device. This wasn't as much of a problem when all iPhones had 3.5 inch screens, but became a pretty common problem with the advent of the iPhone 5's 4 inch screen.
The view isn't guaranteed to have its correct frame until its superview's layoutSubviews returns, so if for example you're creating bigBottomBtn in viewDidLoad, that's too early. Many questions on stackoverflow cover this problem. You either need to set the autoresizingMask of the button, or implement layoutSubviews or viewDidLayoutSubviews to update the button's frame, or turn off translatesAutoresizingMaskIntoConstraints and install constraints. Note too that your view can change size if you support rotation, slide over or split view multitasking, or if your view can be the detail view of a UISplitViewController, so it's a bad idea to try to guess the correct frame of the button based on the device's screen size at the moment the button is created.
Note that storyboards now by default use a design size of 600x600, which isn't the size of any device. This is probably because if Apple chose some device's size (say, the iPhone 5's 320x568) as the default, and you happened to use a device of that size as your primary (or only) test device, you could easily forget to think about what your app will look like at other sizes. However, you can explicitly set the design size to some device's size if you want:
I usually use “iPhone 3.5-inch” if I don't specifically need something bigger, because it lets me get the most scenes on the screen simultaneously (and produces the smallest screen shots for stackoverflow).
As for “I also used to be able to just drag a button onto a storyboard and add a constraint that would hold it to the bottom of the parent”, I have good news: you still can. Example:
However, you do need to be careful if you have filled your root view with a table view as appears to be the case in your screen shots. You need to drag the button to the document outline in that case, because if you drop it on the table view, Xcode will assume you want it to be the table view header:
Trying to pin a table view header to the bottom of the screen would be folly.
As for the Editor > Align menu, I have found that the items can be mysteriously inactive, which is frustrating.
Note, though, that only the “Horizontally in Container” and “Vertically in Container” will work (when they work at all) with a single view selected. To use the other items in the menu, you need to have at least two views selected, because the other items align the selected views with each other by setting their frames:
If you only have one view selected, Xcode doesn't know what other view you might want to align it to.
Those menu items are perhaps useful in the springs'n'struts model, but they don't add constraints, and under autolayout you probably want constraints to enforce the alignment at run time.
As far as I know, those menu items have never added constraints, but I'm not going to reinstall Xcode 6 to verify that, because there's a convenient popover that will add constraints corresponding to all of those menu items:
In xcode you always need to add buttons according to its visibility. As you said you need to show button on top of tableView and it should be aligned to bottom. For that You just need to arrange the order of items. as shown in the image below.Provide the layout for the button.

Problems Setting up constraints in IOS8 with Swift and Xcode 6

I have tried for several hours to design and place the different elements accordingly. But it seems like no matter what I do, the elements get messed up with sizes and location when moving to different device (screen sizes).
How would you place constraints in the screen below to ensure proper scaling and position when moving to a different device?
http://postimg.org/image/hl4incjzh/
I only work in portait mode.
The views at the left is a UIImage view and a UIWebview which is hidden, and will show dynamically based on external content.
Label and the textview below is also dynamically populated on ViewDidLoad.
Any ideas, suggestions?
You can click on a view element and use the add-constraints-menu:
There you can set different size-options like the margin, if the view should resize etc.

Artifacts after orientation change

My app consists of 2 screen (main and settings) both are filled with scrollview completely. Each has one view controller and is supposed to support landscape and portrait orientations. I'm using Autosizing to achieve that and it works fairly well (all elements are where they should be, size is also correct) but I'm getting strange artifacts when the screen is rotated e.g. picker frame remains the same when the actual "drum" area inside is resized properly, text field is partially overlapped by background, landscape graph remains in place and is overlapped by portrait graph.
All are just basic elements without modifications, there are no images or anything special (graph is exception, I'm reloading it in didRotateFromInterfaceOrientation:). Any ideas why would that happen, why aren't the basic elements redrawn properly?
Figured it out myself in the end. Had to implement workaround as iOS apparently can't do it itself.
1) Artefacts after automatic view resize. Solution here was to hide the view before changing orientation, reload the graph and show it again after orientation was changed. Doesn't look perfect but much better than before.
2) Other problems were caused by the pickerview. Autoresize apparently doesn't work at all. The solution here was create UIView instead of the picker which resizes without problem and again recreate the pickerview every time the orientation changes.

Resources