Using iOS14.1, Swift5.3,
I try to rotate my iPad from portrait to landscape. However, the view is black on one side in landscape mode (see video).
What do I need to do in order to rotate an iPad ?
open override var shouldAutorotate: Bool {
return true
}
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { (context) in
guard let windowInterfaceOrientation = self.windowInterfaceOrientation else { return }
if windowInterfaceOrientation.isLandscape {
// activate landscape changes
print("landscape now")
print(UIScreen.main.bounds.height)
print(UIScreen.main.bounds.width)
self.view.frame = CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
self.view.setNeedsLayout()
self.view.layoutSubviews()
} else {
// activate portrait changes
print("portrait now")
print(UIScreen.main.bounds.height)
print(UIScreen.main.bounds.width)
self.view.frame = CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
self.view.setNeedsLayout()
self.view.layoutSubviews()
}
})
}
private var windowInterfaceOrientation: UIInterfaceOrientation? {
if #available(iOS 13.0, *) {
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
} else {
return UIApplication.shared.statusBarOrientation
}
}
Related
I am trying to make an adaptive layout using a UICollectionView
On an iPhone I would like my view to stack, on an iPad I would like it to arrange 3 in a row.
This is working, however when I rotate my iPad and rotate it back again, my subviews do not return the position they were in originally at in that rotation.
You can see the behaviour if you pay attention to the Recognition item in the gif
This is my view controller
class AdaptiveLayoutCollectionView: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
registerCells()
configureLayout(with: view.bounds.size)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
configureLayout(with: size)
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
configureLayout(with: view.bounds.size)
}
final func setLayout(as layout: CollectionViewLayoutOption) {
self.layout = layout
configureLayout(with: view.bounds.size)
}
func registerCells() { /* register cells in super view */ }
private func configureLayout(with size: CGSize) {
guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
flowLayout.minimumInteritemSpacing = 0
flowLayout.minimumLineSpacing = 0
flowLayout.sectionInset = UIEdgeInsets(top: 8.0, left: 0, bottom: 8.0, right: 0)
if traitCollection.horizontalSizeClass == .regular {
//let minItemWidth: CGFloat = 300
let minItemWidth: CGFloat = size.width / 3
let numberOfCell = size.width / minItemWidth
let width = floor((numberOfCell / floor(numberOfCell)) * minItemWidth)
flowLayout.itemSize = CGSize(width: width, height: 80)
} else {
flowLayout.itemSize = CGSize(width: size.width, height: 80)
}
collectionView.reloadData()
}
}
I have a pop over and I am trying to change the width when the orientation changes. I get the width that I want when the popUp shows in the current orientation, but when I switch from .portrait -> .landscape I don't get the width that I want.It keeps the .portrait once for example.
I read the documentation about the popoverPresentationController(_:willRepositionPopoverTo:in:)
but I can't figure out how it works.
actually it doesn't even called when I change the orientation. The rest of the UIPopoverPresentationControllerDelegate that I use they work correctly.
Is this the correct one for changing the size of the PopUp , or should I use something else ?
Any help? Thanks a lot
func popoverPresentationController(_ popoverPresentationController: UIPopoverPresentationController, willRepositionPopoverTo rect: UnsafeMutablePointer<CGRect>, in view: AutoreleasingUnsafeMutablePointer<UIView>) {
if popoverPresentationController.presentingViewController is PopUpTextPickerViewController{
let viewFrame = popoverPresentationController.presentingViewController.view.frame
let newRect = CGRect(x: viewFrame.origin.x, y: viewFrame.origin.y, width: self.view.bounds.width, height: 100)
let newView = UIView(frame: newRect)
rect.pointee = newRect
view.pointee = newView
}
print("popoverPresentation:willRepositionPopOverTo")
}
//Shows the PopUpTextPickerViewController on the screen
#IBAction func fontButtonPressed(_ sender: UIBarButtonItem) {
subscribeToNotifications(notification: .popUpTextPickerViewController)
let fontController = storyboard?.instantiateViewController(withIdentifier: "popUpTextPickerViewController") as! PopUpTextPickerViewController
fontController.fontName = self.fontName
fontController.fontSize = self.fontSize
fontController.modalPresentationStyle = UIModalPresentationStyle.popover
fontController.popoverPresentationController?.delegate = self
fontController.popoverPresentationController?.barButtonItem = fontButton
fontController.popoverPresentationController?.backgroundColor = .clear
fontController.popoverPresentationController?.sourceView = self.view
fontController.preferredContentSize = CGSize(width: self.view.bounds.width, height: 100)
present(fontController, animated: true, completion: nil)
}
Try implementing below code in your parent view controller. UIKit calls this method before changing the size of a presented view controller’s view -
override func viewWillTransition(to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if let myVC = self.presentedViewController as? MyViewController {
myVC.preferredContentSize = CGSize(width: {yorWidth}, height: {yourHeight})
}
}
I want to modify the height of popover in landscape mode, but it only work in portrait mode.
I want it's height equal to screenSize.height * 0.7, but it doesn't work with my code below.
Here is my code:
if let orientation = UIDevice.current.value(forKey: "orientation") as? Int {
let diamondViewController = DiamondViewController()
diamondViewController.mode = .buyDiamondPopup
diamondViewController.resetBackgroundColor = {
self.view.backgroundColor = .clear
}
let screenSize = UIScreen.main.bounds
if orientation == 3 { // LandscapeRight
diamondViewController.preferredContentSize = CGSize(width: screenSize.width * 0.6, height:
screenSize.height * 0.7)
} else {
diamondViewController.preferredContentSize = CGSize(width: screenSize.width - 60, height:
min(screenSize.height - 180, CGFloat(5 * 70 + 110) ))
}
diamondViewController.modalPresentationStyle = .popover
if let popover = diamondViewController.popoverPresentationController {
popover.permittedArrowDirections = .init(rawValue: 0)
popover.sourceView = self.view
popover.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
popover.delegate = self
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
self.present(diamondViewController, animated: true, completion: nil)
}
}
...
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
Instead of this
diamondViewController.preferredContentSize = CGSize(width: screenSize.width * 0.6, height: screenSize.height * 0.7)
Try this below
diamondViewController.view = CGRect(x: diamondViewController.view.frame.origin.x ,y: diamondViewController.view.frame.origin.y ,width: screenSize.width * 0.6, height: screenSize.height * 0.7)
If it doesn't help then let me know.
Have you tried using the "Vary for traits" tool? It allows you to apply different constraints depending on the devices orientation. It can be found near the bottom right corner in storyboard next to the different constraint options.
I think you have to set landscape's height in a dispatched thread. Here is my suggestion.
if let orientation = UIDevice.current.value(forKey: "orientation") as? Int {
let diamondViewController = DiamondViewController()
diamondViewController.mode = .buyDiamondPopup
diamondViewController.resetBackgroundColor = {
self.view.backgroundColor = .clear
}
// HERE IS THE CHANGE: CALL FUNC TO GET SIZE
let size = diamondViewController.myPreferedContentSize(landscape: landscape)
diamondViewController.preferredContentSize = size
...
}
}
The func myPreferedContentSize is defined in 'diamondViewController' class as following:
func myPreferedConentSize(landcape: Bool) -> CGSize
{
let screenSize = UIScreen.main.bounds
let retSize: CGSize
if landscape { // LandscapeRight
retSize = CGSize(width: screenSize.width * 0.6, height:
screenSize.height * 0.7)
} else {
retSize = CGSize(width: screenSize.width - 60, height:
min(screenSize.height - 180, CGFloat(5 * 70 + 110) ))
}
return retSize
}
In your diamondViewController class, override this func and add codes:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
let landscape = size.width > size.height
DispatchQueue.main.async {
[weak self] in
guard let this = self else { return }
this.preferredContentSize = this.myPreferedConentSize(
landscape: landscape)
}
}
It is important to call func myPreferedConentSize in DispatchQueue.main.async thread. In this async main thread, it will ask your view controller to set preferred content size in either portrait or landscape orientation correctly.
I used UINavigationBar and when I change to landscape mode the bar look like this .
in portrait mode
Try this way and this code for navigation bar not navigation controller . may be you missing to set Constraint or conflict each other
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator){
super.viewWillTransition(to: size, with: coordinator)
var frame: CGRect = (self.navigationController?.navigationBar.frame)!
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
let orient = UIApplication.shared.statusBarOrientation
switch orient {
case .portrait:
frame.size.height = 44
frame.size.width = self.view.frame.width
default:
frame.size.height = 32
frame.size.width = self.view.frame.width
}
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
print("rotation completed")
self.navigationController?.navigationBar.frame = frame
})
}
I want put several UIViewController together:
leftViewController.view.frame = CGRect(x: -200, y: 0.0, width: size.width/drawerSize, height: size.height)
// Center Drawer
centerViewController.view.frame = CGRect(x: leftViewController.view.frame.width, y: 0.0, width: centerWidth, height: size.height)
// Right Drawer
rightViewController.view.frame = CGRect(x: centerViewController.view.frame.origin.x + centerViewController.view.frame.size.width, y: 0.0, width: size.width/drawerSize, height: size.height)
In the first line I use
leftViewController.view.frame = CGRect(x: -200, y....)
They will show correctly but I can not click buttons on leftViewController.
If
leftViewController.view.frame = CGRect(x: 0.0, y...)
then could click button but this layout is not I want.
The full code is posted by #Kevin Scardina Slide Sidebar Menu IOS 8 Swift
It now function as the picture below, And I'm trying to modify it like a slide menu bar which could hid left and right menu.
/*
To use simply instantiate NVMDrawerController as your root view in your AppDelegate, or in the
StoryBoard.
Once NVMDrawerController is instantiated, set the drawerSize of the NVMDrawerController,
and its leftViewControllerIdentifier, centerViewControllerIdentifier, and
rightViewControllerIdentifier to the Storyboard Identifier of the UIViewController
you want in the different locations.
*/
class NVMDrawerController: UIViewController {
// This is where you set the drawer size (i.e. for 1/3rd use 3.0, for 1/5 use 5.0)
var drawerSize:CGFloat = 4.0
var leftViewControllerIdentifier:String = "LeftController"
var centerViewControllerIdentifier:String = "CenterController"
var rightViewControllerIdentifier:String = "RightController"
private var _leftViewController:UIViewController?
var leftViewController:UIViewController {
get{
if let vc = _leftViewController {
return vc;
}
return UIViewController();
}
}
private var _centerViewController:UIViewController?
var centerViewController:UIViewController {
get{
if let vc = _centerViewController {
return vc;
}
return UIViewController();
}
}
private var _rightViewController:UIViewController?
var rightViewController:UIViewController {
get{
if let vc = _rightViewController {
return vc;
}
return UIViewController();
}
}
static let NVMDrawerOpenLeft = 0
static let NVMDrawerOpenRight = 1
var openSide:Int {
get{
return _openSide;
}
}
private var _openSide:Int = NVMDrawerOpenLeft
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Instantiate VC's with storyboard ID's
_leftViewController = instantiateViewControllers(leftViewControllerIdentifier)
_centerViewController = instantiateViewControllers(centerViewControllerIdentifier)
_rightViewController = instantiateViewControllers(rightViewControllerIdentifier)
// Call configDrawers() and pass the drawerSize variable.
drawDrawers(UIScreen.mainScreen().bounds.size)
self.view.addSubview(leftViewController.view)
self.view.addSubview(centerViewController.view)
self.view.addSubview(rightViewController.view)
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in
// This is for beginning of transition
self.drawDrawers(size)
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
// This is for after transition has completed.
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Drawing View
func drawDrawers(size:CGSize) {
// Calculate Center View's Size
let centerWidth = (size.width/drawerSize) * (drawerSize - 1)
// Left Drawer
leftViewController.view.frame = CGRect(x: 0.0, y: 0.0, width: size.width/drawerSize, height: size.height)
// Center Drawer
centerViewController.view.frame = CGRect(x: leftViewController.view.frame.width, y: 0.0, width: centerWidth, height: size.height)
// Right Drawer
rightViewController.view.frame = CGRect(x: centerViewController.view.frame.origin.x + centerViewController.view.frame.size.width, y: 0.0, width: size.width/drawerSize, height: size.height)
//rightViewController = rc
// Capture the Swipes
let swipeRight = UISwipeGestureRecognizer(target: self, action: Selector("swipeRightAction:"))
swipeRight.direction = .Right
centerViewController.view.addGestureRecognizer(swipeRight)
let swipeLeft = UISwipeGestureRecognizer(target: self, action: Selector("swipeLeftAction:"))
swipeLeft.direction = .Left
centerViewController.view.addGestureRecognizer(swipeLeft)
if(openSide == NVMDrawerController.NVMDrawerOpenLeft){
openLeftDrawer()
}
else{
openRightDrawer()
}
}
// MARK: - Open Drawers
func openLeftDrawer() {
_openSide = NVMDrawerController.NVMDrawerOpenLeft
UIView.animateWithDuration(0.1, delay: 0, options: UIViewAnimationOptions.CurveEaseIn, animations:
{ () -> Void in
// move views here
self.view.frame = CGRect(x: 0.0, y: 0.0, width: self.view.bounds.width, height: self.view.bounds.height)
}, completion:
{ finished in
})
}
func openRightDrawer() {
_openSide = NVMDrawerController.NVMDrawerOpenRight
UIView.animateWithDuration(0.1, delay: 0, options: UIViewAnimationOptions.CurveEaseIn, animations:
{ () -> Void in
// move views here
self.view.frame = CGRect(x: self.view.bounds.origin.x - self.leftViewController.view.bounds.size.width, y: 0.0, width: self.view.bounds.width, height: self.view.bounds.height)
}, completion:
{ finished in
})
}
// MARK: - Swipe Handling
func swipeRightAction(rec: UISwipeGestureRecognizer){
self.openLeftDrawer()
}
func swipeLeftAction(rec:UISwipeGestureRecognizer){
self.openRightDrawer()
}
// MARK: - Helpers
func instantiateViewControllers(storyboardID: String) -> UIViewController {
if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("\(storyboardID)") as? UIViewController{
return viewController;
}
return UIViewController();
}
}
When your view is outside of its superview,it can't receive any touch events.You should enumerate subviews in UIView(its superview) touchWithEvents function and make it receive the event.