Xcode, Swift: How to center popover box - ios

Link to my github account with files, simply download zip: https://github.com/jzhang172/modalTest
When I click on the "popover" link, I would like to center the popover in the center of the screen.
I tried referencing some stackoverflow questions such as:
how to center a popoverview in swift
but no luck. I'm a noob in swift and I'm only using swift, not objective C.
Screenshot of what I see:

Replacing
controller?.sourceRect = CGRectMake(0.0, self.view.layer.bounds.height * 0.5,0.0,0.0)
with
controller?.sourceRect = CGRectMake(self.view.layer.bounds.width * 0.5, self.view.layer.bounds.height * 0.5,0.0,0.0)
will center the popover content horizontally and vertically.

You can center the UIPopover in your view with the code below.
let controller = vc.popoverPresentationController
controller?.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
controller?.sourceView = self.view
controller?.sourceRect = CGRectMake(0.0, self.view.layer.bounds.height * 0.5,0.0,0.0)
vc.preferredContentSize=CGSize(width: 400, height: 200)

Swift 3:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let popoverPresentationController = segue.destination.popoverPresentationController {
let controller = popoverPresentationController
controller.permittedArrowDirections = UIPopoverArrowDirection(rawValue: 0)
controller.sourceView = self.view
controller.sourceRect = CGRect(x: UIScreen.main.bounds.width * 0.5 - 200, y: UIScreen.main.bounds.height * 0.5 - 100, width: 400, height: 200)
segue.destination.preferredContentSize=CGSize(width: 400, height: 200)
}
}

Related

Swift 4 - Button over Tab Bar Item

I am trying to position a custom button over one of the item of my Tab bar.
func setupMiddleButton() {
let numberOfItems = CGFloat(tabBar.items!.count)
let tabBarItemSize = CGSize(width: tabBar.frame.width / numberOfItems, height: tabBar.frame.height)
let menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: tabBarItemSize.width, height: self.tabBar.frame.size.height))
var menuButtonFrame = menuButton.frame
menuButtonFrame.origin.y = self.view.bounds.height - menuButtonFrame.height
menuButtonFrame.origin.x = self.view.bounds.width/2 - menuButtonFrame.size.width/2
menuButton.frame = menuButtonFrame
menuButton.backgroundColor = UIColor.white
self.view.addSubview(menuButton)
self.view.layoutIfNeeded()
My issue is that with the previous code the button is not perfectly over the bar item (see picture):
Any suggestion? I really don't know how else to try.
Thank you!
I notice that this screenshot is of an iPhone X Simulator, which has a different layout at the bottom of the screen.
Your code works well on any other iPhone. In iOS 11 they introduced what's called the "Safe area". When you calculate the size and origin for your button, you will have to take that into account.
When you calculate the origin.y for your buttonFrame, you have to subtract the height for the safe-area at the bottom, like this:
menuButtonFrame.origin.y = self.view.bounds.height - menuButtonFrame.height - self.view.safeAreaInsets.bottom
This won't solve your problem though, as your code probably runs in viewDidLoad, which happens before the view knows it's supposed be displayed on an iPhone X with a safe area.
You can override viewDidLayoutSubviews for this, and set the correct frame for your button each time that is called.
This will fix your issue:
class CustomTabBarViewController: UITabBarController {
let menuButton = UIButton(frame: CGRect.zero)
override func viewDidLoad() {
super.viewDidLoad()
setupMiddleButton()
}
func setupMiddleButton() {
let numberOfItems = CGFloat(tabBar.items!.count)
let tabBarItemSize = CGSize(width: tabBar.frame.width / numberOfItems, height: tabBar.frame.height)
menuButton.frame = CGRect(x: 0, y: 0, width: tabBarItemSize.width, height: tabBar.frame.size.height)
var menuButtonFrame = menuButton.frame
menuButtonFrame.origin.y = self.view.bounds.height - menuButtonFrame.height - self.view.safeAreaInsets.bottom
menuButtonFrame.origin.x = self.view.bounds.width/2 - menuButtonFrame.size.width/2
menuButton.frame = menuButtonFrame
menuButton.backgroundColor = UIColor.green
self.view.addSubview(menuButton)
self.view.layoutIfNeeded()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
menuButton.frame.origin.y = self.view.bounds.height - menuButton.frame.height - self.view.safeAreaInsets.bottom
}
}
I know it's tempting to simply call setupMiddleButton from inside viewDidLayoutSubviews, but do not do that. viewDidLayoutSubviews should not be used to create buttons etc., it should only be used to move them accordingly to the rest of the view. You might want to set the entire frame of menuButton inside viewDidLayoutSubviews rather than only the origin.y like I did, especially if you need to support rotation/landscape-mode. In this very simple example, updating origin.y is enough.

iOS - Display a progress indicator at the center of the screen rather than the view

I want to display a progress indicator at the center of the screen, NOT the view. It should be the center of the view because the view is scrollable. Most answers only tells how to center it in the view. I have this code:
let screenBound = UIScreen.main.bounds
let progressIndc = UIActivityIndicatorView()
progressIndc.frame = CGRect(x: screenBound.width / 2 - 10,
y: screenBound.height / 2 - 10,
width: 20, height: 20)
progressIndc.hidesWhenStopped = true
progressIndc.color = UIColor.gray
progressIndc.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
// self.view is scroll view
self.view.addSubview(progressIndc)
progressIndc.startAnimating()
But it shown near the top in iPhone 7. What should be the right way? I can also do a blocking pop-up dialog with a progress indicator.
if you want to keep the indicator view at the center of screen while scrolling, you can add a overlay view to the current topmost UIWindow, then add your indicator view to the overlay view:
guard let topWindow = UIApplication.shared.windows.last else {return}
let overlayView = UIView(frame: topWindow.bounds)
overlayView.backgroundColor = UIColor.clear
topWindow.addSubview(overlayView)
let hudView = UIActivityIndicatorView()
hudView.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
overlayView.addSubview(hudView)
hudView.center = overlayView.center
you should do this after the topmost UIViewController's view was attached on the top UIWindow, for example, in viewDidAppearmethod.
you can use center property of UIView
progressIndc.center = self.view.center
for e.g
let screenBound = UIScreen.main.bounds
let progressIndc = UIActivityIndicatorView()
progressIndc.frame = CGRect(x: screenBound.width / 2 - 10,
y: screenBound.height / 2 - 10,
width: 20, height: 20)
progressIndc.hidesWhenStopped = true
progressIndc.color = UIColor.gray
progressIndc.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
// self.view is scroll view
progressIndc.center = self.view.center
self.view.addSubview(progressIndc)
progressIndc.startAnimating()
output
you can try this one to add you indicator on top of the screen. but it's not pretty solution -
AppDelegate.sharedInstance.window?.addSubview(progressIndc);

View controller origin changes every time it's presented

This is what my view controller should be:
This is what it is sometimes:
I want to display a view controller in the circle, however, almost every time the view controller in the circle (ResultViewController) is presented, it's place is different, though its properties doesn't change at all. Here's my code:
func openCircle(withCenter center: CGPoint, dataSource: ([Items], Int, String)){
self.addCircle(withCenter: center, dataSource: dataSource)
}
func addCircle(withCenter circleCenter: CGPoint, dataSource: ([Items], Int, String)) {
let longerSide = fmax(view.frame.size.height, view.frame.size.width)
let shorterSide = fmin(view.frame.size.height, view.frame.size.width)
let circleRadius = longerSide / 2
var resultViewOrigin = CGPoint()
var resultViewSize = CGSize()
if UIDevice.current.userInterfaceIdiom == .pad {
let rectWidth = shorterSide / 2
let rectHeight = sqrt(abs(circleRadius * circleRadius - rectWidth * rectWidth)) + view.frame.size.height - circleCenter.y
resultViewSize = CGSize(width: rectWidth, height: rectHeight)
resultViewOrigin = CGPoint(x: (view.frame.size.width - rectWidth) / 2, y: view.frame.size.height - rectHeight)
} else {
resultViewOrigin = CGPoint(x: 0.0, y: 0.0)
resultViewSize = CGSize(width: view.frame.size.width, height: view.frame.size.height)
}
let resultViewController = UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "ResultVC") as! ResultViewController
resultViewController.transitioningDelegate = self
resultViewController.modalPresentationStyle = .custom
resultViewController.dataSource = dataSource
resultViewController.view.frame = CGRect(origin: resultViewOrigin, size: resultViewSize)
transition.circle = UIView()
transition.startingPoint = circleCenter
transition.radius = circleRadius
transition.circle.frame = circleFrame(radius: transition.radius, center: transition.startingPoint)
present(resultViewController, animated: true)
}
It works well on the iPhone, not on the iPad, what's the problem?
I found the problem, it's actually a missing constraint on Regular-Regular size class caused this problem, I fixed it by adding a spacing to bottom layout guide to the part that used to get misplaced.
Thanks to everybody for your idea.
You can use a container view instead of presenting the view controller. You can create them programmatically or in interface builder (see Apple docs).

Adding View Controllers side by side into a Scroll View

How can i add my existing View Controllers into a Scroll View. I have tried the code below inside 'viewDidLoad' but it didn't work. Thanks
let view1 = ViewController6()
let view2 = ViewController2()
let view3 = ViewController3()
contentView.addSubview(view1)
contentView.addSubview(view2)
contentView.addSubview(view3)
self.contentView.contentSize = CGSize(width: self.view.frame.width * 3, height: self.view.frame.height)
Check this code Here I am doing the exactly thing that you want to achieve.
func setupScrollView()
{
//loop for
for i in 0..<3 {
//we instantiate our viewController from storyboard
let news2 = self.storyboard?.instantiateViewController(withIdentifier: "NewsMasterViewController") as! NewsMasterViewController
//we adjust the frame according
news2.view.frame = CGRect(x: CGFloat(i) * self.scrollView.frame.size.width + CGFloat(MasterViewController.margin), y: 0, width: self.scrollView.frame.size.width - CGFloat(MasterViewController.margin * 2), height: self.scrollView.frame.size.height)
//we call layoutSubviews for constraints adjustments
news2.view.layoutSubviews()
self.addChildViewController(news2)
//added the view of my viewController "news2" to scrollView
self.scrollView.addSubview(news2.view)
news2.didMove(toParentViewController: self)
//added the viewController "news2" to my array of viewControllers
newsMasterViewControllers.append(news2)
}
//adjusted contentSize of scrollView according the number of viewControllers added
self.scrollView.contentSize = CGSize(width: self.view.frame.size.width * 3, height: self.scrollView.frame.size.height);
}
I hope this helps you

center element when pressed in a ScrollView

i really how to do a thing wich is probably easy..
I've a ScrollView with some button in it.
That's how i create all my button in the scrollview.
var buttonList = [UIButton]()
func createButton() {
let imageArray = fillImageArray()
var lastButtonWidth: CGFloat = 0
for index in 0..<6 {
let frame1 = CGRect(x: ((self.view.frame.size.width / 2) - 27.5) + CGFloat(index * 70), y: 0, width: 55, height: 55 )
let button = UIButton(frame: frame1)
button.setImage(imageArray[index], forState: .Normal)
button.tag = index
button.addTarget(parentViewController, action: #selector(ViewController.buttonClicked(_:)), forControlEvents: .TouchUpInside)
self.scrollView.addSubview(button)
lastButtonWidth = frame1.origin.x
buttonList.append(button)
}
self.scrollView.contentSize = CGSizeMake(lastButtonWidth + 55, 0)
}
I want when i press one of my button to center him and positioning correctly the other buttons.
example :
If i press on 5 i want this result :
the button 5 is moved to the center.
Now what I would suggest using is the scroll view method scrollView.scrollRectToVisible(CGRect, animated: Bool). This will move the scroll view to make a certain part of your content visible.
To create the CGRect you could do something like this:
let scrollWidth = scrollView.frame.width
let scrollHeight = scrollView.frame.height
let desiredXCoor = button.frame.origin.x - ((scrollWidth / 2) - (button.frame.width / 2) )
let rect = CGRect(x: desiredXCoor, y: 0, width: scrollWidth, height: scrollHeight)
scrollView.scrollRectToVisible(rect, animated: true)
My math may be a bit off, but the essence is that you use the size of the scrollView and the UIButton to create a CGRect for the scroll view to move to. This means when a button is clicked, you could use an implementation like this:
func myMethod() {
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender: UIButton){
let scrollWidth = scrollView.frame.width
let scrollHeight = scrollView.frame.height
let desiredXCoor = sender.frame.origin.x - ((scrollWidth / 2) - (sender.frame.width / 2) )
let rect = CGRect(x: desiredXCoor, y: 0, width: scrollWidth, height: scrollHeight)
scrollView.scrollRectToVisible(rect, animated: true)
}
If adjusted properly to your project, this should allow you to do what you have outlined.
Hope this would help you.
In button action method write below code.
func buttonClicked(sender:UIButton) {
for view in self.scrollView.subviews {
if view.isKindOfClass(UIButton) && view.tag == sender.tag {
let xCenter = max(0, (view.center.x - self.scrollView.frame.width/2))
self.scrollView.setContentOffset(CGPointMake(xCenter, self.scrollView.contentOffset.y), animated: true)
break;
}
}
}
I suppose you want the button touched to be placed in the center,so there is a easy solution, you can use the button's center.x - scrollview.width / 2 as the offsetX to construct the contentOffset and take two boundary situation into consideration:offsetx < 0 and offsetx + scrollview.width > scroll.contentSize.width
Ok i just found by myself.
let centerScrollView = scrollView.frame.size.height * 2
UIView.animateWithDuration(0.2, delay: 0, options: UIViewAnimationOptions.CurveLinear, animations: {
self.scrollView.contentOffset = CGPoint(x: sender.frame.origin.x - centerScrollView, y: sender.frame.origin.y)
}, completion: nil)
and it's perfectly center. Thx everyone.

Resources