iPad screen auto resize in swift 5 - ios

I'm not using storyboard for my app. All design I've done programatically.
Now I'm badly stuck in screen resizing. See my example code for navigation bar:-
let screenSize: CGRect = UIScreen.main.bounds
screenWidth = screenSize.width
screenHeight = screenSize.height
NameHeight = screenHeight * 0.09
NameWidth = screenWidth
navBar = UINavigationBar(frame: CGRect(x: 0, y: 30, width: NameWidth, height: NameHeight))
self.view.addSubview(navBar)
navBar.setItems([navItem], animated: false)
This navigation bar is perfectly working for both landscape and portrait.
Problem is happening:-
1) if I suddenly move my iPad from landscape to portrait or vice versa
then screen is not properly fit.
2) but if I click any button or anything then screen is properly fit.
That means when screen is refreshing then view is good.
My total app code done in this manner.
Are there any solution for my issue?
Does I need to add anything in AppDelegate which will help to reload the screnn??? or any idea please....
Help me please to overcome this issue.....

I'd have to see the code to know what the exact issue is, but you can try:
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
// Reload Data here
self.view.setNeedsDisplay()

The below piece of code saved me:-
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
self.viewDidLoad()
}else if UIDevice.current.orientation.isFlat {
self.viewDidLoad()
} else {
self.viewDidLoad()
}
}
May be I did the wrong thing. But it saved me ...

Related

UITabBar text is not properly align with icons in landscape mode

With a UITabBar, I'm having a problems with text alignment when I'm in landscape mode. What's the best way to center the text up and down so its aligned with the center of the image?
I do understand that the text will be on the right in landscape mode, I'm looking to center it with the icon.
My images are custom images but I have made 1x, 2x, and 3x versions at 25x25, 50x50, and 75x75.
I have the TabBar's bottom constraint set to Superview.Bottom.
I can fix it, if I adjust every tab bar item when I'm only in landscape mode with the following code, but this seems wrong. Also not sure if this would hold up on all devices.
surveyTabBar.items![1].titlePositionAdjustment = UIOffset.init(horizontal: 0, vertical: -10)
The best solution I found was to adjust with the UIOffset, but I only adjust in landscape mode.
#IBOutlet var myTabBar: UITabBar!
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
adjustTabBarTitles()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
self.adjustTabBarTitles()
}
private func adjustTabBarTitles() {
if UIDevice.current.orientation.isLandscape {
for item in self.myTabBar.items! as [UITabBarItem] {
item.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: -8)
}
} else {
for item in self.myTabBar.items! as [UITabBarItem] {
item.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: 0)
}
}
}

UIScrollView frame is changed by changing Simulator device

I have simple ViewController which displays images using UIScrollView which has constraints attaching it to the (top, leading, trailing) of the superView, and a UIPageControl, it works fine on iPhoneX simulator
When I run it on iPad Pro 9.7" simulator, the output is
After changing the View as attribute in the storyboard from iPhoneX to iPadPro 9.7" it worked well
This is the logic I use to calculate scrollviewContentSize & slidesSize
override internal func viewDidLoad() {
super.viewDidLoad()
tutorialScrollView.delegate = self
viewModel = TutorialViewModel()
configurePages()
setupSlideScrollView(slides: slides)
configurePageControl()
}
private func configurePages() {
if let viewModel = viewModel {
createSlides(tutotialPages: viewModel.getTutorialPages())
}
}
private func createSlides(tutotialPages: [TutorialPage]) {
for page in tutotialPages {
if let slide = Bundle.main.loadNibNamed(BUNDLE_ID, owner: self, options: nil)?.first as? TutorialSlideView {
slide.configure(title: page.title, detail: page.details, image: page.image)
slides.append(slide)
}
}
}
private func setupSlideScrollView(slides: [TutorialSlideView]) {
tutorialScrollView.contentSize = CGSize(width: view.frame.width * (CGFloat(slides.count)), height: tutorialScrollView.frame.height)
tutorialScrollView.isPagingEnabled = true
for i in 0 ..< slides.count {
slides[i].frame = CGRect(x: view.frame.width * CGFloat(i), y: 0, width: view.frame.width, height: tutorialScrollView.frame.height)
tutorialScrollView.addSubview(slides[i])
}
}
Can anyone find the problem?
Did you try to print view's frame in setupSlideScrollView method to ensure it is correct? There is no guarantee that it will be correct in the viewDidLoad method if you use AutoLayout. Sometimes it will be, sometimes not. I assume in this particular case, it happened to be correct on iPhone X, but incorrect on iPad.
If that's the problem, you should set contentSize and slides' frames in viewDidLayoutSubviews. Adding slides as subviews should stay in viewDidLoad/setupSlideScrollView because viewDidLayoutSubviews usually gets called multiple times.

How to get the screen height after orientation change?

I'm getting some strange results when querying UIScreen.main.bounds.height after an orientation change. Maybe this isn't the correct way to do it.
I have an observer that listens for the NSNotification.Name.UIDeviceOrientationDidChange event:
NotificationCenter.default.addObserver(self, selector: #selector(self.orientationChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
This calls a function that sets a constraint to 75% of the new screen height. This works fine on iPhone but iPad returns the wrong screen height.
If the iPad is in landscape orientation UIScreen.main.bounds.height will return a value equal to the height in portrait orientation and vice versa.
func orientationChange() {
// This will print the correct value on iPhone and iPad.
if UIDevice.current.orientation.isLandscape {
print("Landscape")
} else {
print("Portrait")
}
let screenSize = UIScreen.main.bounds
let screenHeight = screenSize.height
self.searchViewHeightConstraint.constant = screenHeight * 0.75
// Correct value on iPhone. Incorrect on iPad.
print("screenHeight: '\(screenHeight)'")
UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
self.searchView.superview?.layoutIfNeeded()
}, completion: nil)
}
I've also come across the viewWilltransition method of monitoring orientation change but this behaves in the exact opposite way to the method above. ie. the height is correct on iPad but incorrect on iPhone:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
let screenSize = UIScreen.main.bounds
let screenHeight = screenSize.height
self.searchViewHeightConstraint.constant = screenHeight * 0.75
// Correct value on iPad. Incorrect on iPhone.
print("screenHeight: '\(screenHeight)'")
}
What is the reason for this inconsistent behaviour between iPhone and iPad and is using NotificationCenter the correct approach to monitoring orientation change?
What you should be using is
viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator). This will give you the size and is WAY more reliable than using a notification.
Also, if you wanted to do animations, inside of the UIViewControllerTransitionCoordinator you can leverage the method animate(alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)? = nil) -> Bool
You need to use :
dispatch_async(dispatch_get_main_queue()) {
println("h:\(view.bounds.size.height)")
println("w:\(view.frame.size.width)")
}
// All you need to write code under dispatch_async
This code will be executed after the rotation was completed (in the main queue) and the new sizes are available.
This works for me in Swift 2.3 for both iPad and iPhone.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
print(size.height)
if UIDevice.currentDevice().orientation.isLandscape {
print("Landscape")
print(size.height)
}
else {
print("Portrait")
print(size.height)
}
}

UICollectionView - redesign in Landscape mode

I am working on a UICollectionView to show list of items in Portrait mode as shown in below screen shot and implemented using UICollectionViewController.
Now I want to re-design same thing in Landscape mode as shown in below screen shot.
How can I get such a design using UICollectionView and UICollectionViewFlowLayout in Landscape mode by re-using same code base of portrait? Is it possible? can you please advice me?
Thanks in advance.
You can also do by using stackview. Create a stackview with two views then change the axis when orientation change. Set proper constraints for your requirement.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
handleOrientationForLandscape(isLandscape: UIDevice.current.orientation.isLandscape)
}
func handleOrientationForLandscape(isLandscape: Bool) {
if isLandscape
{
containerStackview.axis = .horizontal
topViewWidthConstraint.constant = 700
}else
{
containerStackview.axis = .vertical
topViewWidthConstraint.constant = UIScreen.main.bounds.width
}
}
please check https://github.com/karthikprabhuA/stackOverflowAnswer

Showing a popover partially off screen

I am trying to recreate the effect that YouTube mobile app on iOS has, when a video is playing in full-screen, landscape mode. Just the top part of a window (next/recommended videos) is visible, and swiping up shows them overlapped with video (that keeps running in background). Swiping down hides them again.
So I added the following code within my video controller that shows the video in landscape mode:
private func showPopover() {
let popoverController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "popoverController")
popoverController.modalPresentationStyle = .popover
popoverController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
popoverController.popoverPresentationController?.delegate = self
let desiredWidth:CGFloat = 200
let desiredHeight:CGFloat = 300
// Initially it's at low right corner
let origin = CGPoint(x: (self.view.superview?.frame.width)! - desiredWidth - 10, y: (self.view.superview?.frame.height)! - 10)
let size = CGSize(width: desiredWidth, height: desiredHeight)
popoverController.popoverPresentationController?.sourceRect = CGRect(origin: origin, size: size)
popoverController.popoverPresentationController?.sourceView = popoverController.view
self.present(popoverController, animated: true, completion: nil)
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
The popover shows up fine, but is always completely on screen. I need it to be only partially visible (just the top 10 pixels, if possible). But something is preventing the view controller from being positioned that way.
How do I move the popover so that only top 10 pixels are visible? It should look something like in the image below (note: this was generating by editing image, not programmatically):
Replace this:
popoverController.popoverPresentationController?.sourceView = popoverController.view
with this:
popoverController.popoverPresentationController?.sourceView = self.view
UPDATE
Ok I understand now. My answer would be it is not possible to only show some part of the popover.
My suggestion here is using UITableViewController as your popover and making its height just enough to show the first row, which is the text in this case.
Then, in optional func scrollViewDidScroll(_ scrollView: UIScrollView) delegate method, implement logics to detect swiping up to show and swiping down to hide.
Found a way, and it's ridiculously easy (with hindsight, of course!). Changed the last line of showPopover function to:
self.present(popoverController, animated: true) {
popoverController.popoverPresentationController?.presentedView?.center = CGPoint(x: (self.view.superview?.frame.width)! - desiredWidth - 10, y: (self.view.superview?.frame.height)! + 100)
}
I will get a specific number instead of using 100 but the view moves to this position without any issues.

Resources