Swift iOS -How to Add Subview to Window's Center? - swift

I have an activity indicator that gets presented on an iPhone and iPad. In the iPad in split screen mode it gets presented to whichever side of the view that called it. I would instead like it to get presented in the middle/center the window's screen. If I do it this way wether on the iPhone in portrait or iPad in split screen mode it will always be in the center of the screen.
How do I do this?
MyView: UIViewController{
let actInd = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
#IBAction fileprivate func buttonPressed(_ sender: UIButton) {
guard let window = UIApplication.shared.keyWindow else { return }
//how to add actInd as subview to the window' screen?
actInd.startAnimating()
}
}

It's pretty simple. Turn off the auto-resizing mask. Add the add actInd to window, then set the center anchors.
actInd.translatesAutoresizingMaskIntoConstraints = false
window.addSubview(actInd)
actInd.centerXAnchor.constraint(equalTo: window.centerXAnchor).isActive = true
actInd.centerYAnchor.constraint(equalTo: window.centerYAnchor).isActive = true

Window is subclass of UIView. Just add it as it's subview like you're adding a view to another view. But remember that window is shared throughout your app, so adding it every-time will consume memory, remove it after your job is done.
If you want to center it in the window, you can use autoResizingMask or add constraints to it.

Related

Snapshot of screen shows toolbar on device but not on simulator

I have a toolbar that's instantiated on viewDidLoad on top of a webkit. When I take a snapshot on the simulator, the toolbar is missing which is what I would like. When built on the device, the toolbar is there.
I tried to hide the toolbar with:
toolbar.isHidden = true
but the application crashes with toolbar being nil. If I change it to:
toolbar?.isHidden = true
It still shows up considering it still thinks it's nil.
The toolbar is set up on viewDidLoad by calling another function:
var toolbar : UIToolbar!
override func viewDidLoad() {
super.viewDidLoad()
setUpToolBar()
}
func setUpToolBar() {
let saveButton = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(takeScreenshot))
...
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 300, width: 200, height: 50))
toolbar.setItems([saveButton,flexibleSpaceFillerLeft,userAgentButton,flexibleSpaceFillerRight,doneButton], animated: true)
view.addSubview(toolbar)
}
The code for my snapshot is below. This is where I tried to hide the toolbar before taking the snapshot.
#objc func takeScreenshot() {
webView.takeSnapshot(with: nil, completionHandler: { (image,error) in
if let image = image {
self.screenshotOfWindow = image
self.showScreenshotEffect()
self.saveAllData()
} else {
print (error?.localizedDescription as Any)
}
})
}
Here's the screen I need to take a screenshot of:
The red box in the screenshot is the bar that I need to disappear from the screenshot.
I'd like to be able to take the screenshot without the bottom bar in view. As stated before, this works in the simulator, but the device always shows the bar. There's also a "navigation controller" gap at the top of the screenshot since the top bar covers part of the screen at top, but this is just blank and something I can address later.
I just wanted to come back and answer how I solved this. The webview is embedded in a navigation controller, but I was creating the toolbar programmatically on viewDidLoad by calling a setupToolBar function I had created early on in the project. I could hide the toolbar, but it was still being captured while taking the screenshot. I commented all of that code out and used the navigation controller's toolbar instead. Now when I take the screenshot, the bottom and top bar of the navigation controller is not part of the screenshot.

How to Disable Multi Touch on UIBarButtonItems & UI Buttons?

My app has quite a few buttons on each screen as well as a UIBarButtonItem back button and I have problems with people being able to multi click buttons. I need only 1 button to be clickable at a time.
Does anyone know how to make a UIBarButtonItem back button exclusive to touch?
I've managed to disable multi clicking the UIButtons by setting each one's view to isExclusiveTouch = true but this doesn't seem to count for the back button in the navigation bar.
The back button doesn't seem to adhere to isExclusiveTouch.
Does anyone have a simple work around that doesn't involve coding each and every buttons send events?
Many Thanks,
Krivvenz.
you can enable exclusive touch simply this will stop multiple touch until first touch is not done
buttton.exclusiveTouch = true
You could write an extension for UIBarButtonItem to add isExclusiveTouch?
you can simply disable the multi-touch property of the super view. You can also find this property in the storyboard.
you could try this for the scene where you want to disable multiple touch.
let skView = self.view as! SKView
skView.isMultipleTouchEnabled = false
I have found a solution to this. isExclusiveTouch is the solution in the end, but the reason why this property didn't do anything is because you need to set it also on all of the subviews of each button that you want to set as isExclusiveTouch = true. Then it works as expected :)
It's working fine in Swift
self.view.isMultipleTouchEnabled = false
buttonHistory.isExclusiveTouch = true
In addition of #Luky LĂ­zal answer, you need pay attention that all subviews of a view you want disable multi-touching (exactly all in hierarchy, actually subviews of subviews) must be set as isExclusiveTouch = true.
You can run through all of them recursively like that:
extension UIView
{
func allSubViews() -> [UIView] {
var all: [UIView] = []
func getSubview(view: UIView) {
all.append(view)
guard view.subviews.count > 0 else { return }
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: self)
return all
}
}
// Call this method when all views in your parent view were set
func disableMultipleTouching() {
self.isMultipleTouchEnabled = false
self.allSubViews().forEach { $0.isExclusiveTouch = true }
}

Displaying UIDatePicker as Popover within UITableView on iPhone

I would like display a date picker with a UIModalPresentationPopover presentation style, and while it's working fine on an iPad, it results in a black screen when displayed on an iPhone. Edit: Specifically, it is simply presented full screen, so the effect is a black screen unless I add colours to elements manually. The default colours work fine for Popover presentation.
The code generating the picker and the popover is:
let datePicker = UIDatePicker()
datePicker.datePickerMode = .DateAndTime
datePicker.minuteInterval = 5
let vc = UIViewController()
vc.view.addSubview(datePicker!)
vc.preferredContentSize = datePicker.frame.size
vc.modalPresentationStyle = .Popover
let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 1))
let ppc = vc.popoverPresentationController
ppc?.delegate = self
ppc?.sourceRect = cell.accessoryView!.frame
ppc?.sourceView = cell
self.presentViewController(vc, animated: true, completion: nil)
The value of cell appears correct on both types of devices, but I notice that the size of the view of the presented viewController gets messed up on the iPhone. After presentation is finished, the value of preferredContentSize property is (320.0, 216.0) on both devices, but view.frame.size becomes (320.0, 568.0) on the iPhone (which is full screen on iPhone 5, and it acts similarly on iPhone as well), while on iPad it still matches the preferred size.
Looks great on iPad:
I've had no trouble presenting the same kind of UI from a UIViewController that was not UITableViewController. Is there anything else that needs to be done to allow this style of presentation with a tableView on an iPhone?
Edit
Fixed. To allow this, need to add a method to the presenting controller:
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
This is what I found on the docs from UIViewController
modalPresentationStyle
The presentation style determines how a modally
presented view controller is displayed onscreen. In a horizontally
compact environment, modal view controllers are always presented
full-screen
What you can do is, on the iPhone, set the backgroundColor of the view to black with `.15 alpha, and position the picker at the bottom.
You might need to set modalPresentationStyle to UIModalPresentationOverFullScreen, because otherwise it removes the views underneath.

Right part of UIScrollView doesn't work in Landscape mode

Using the Interface Builder, I created a view with a UIScrollView in it.
I programmaticly add the buttons to the empty UIScrollView.
When the orientation changes, I use
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
to call a method that resets the buttons on screen.
After that, the uiScrollView-content gets a new size using setContentSize.
No matter the width of the ContentSize, I can only interact (scroll/or tap a button) on the first 320px of the screen - which is the screen width in portrait mode.
When I set the contentSize-width to 2000, I can scroll to the left, but only with my fingers on the first 320 px instead of the full 480 (using a 3.5 inch iPhone).
What am I missing?
You need to resize the scrollview's frame, not just the content size (in fact, often you don't need to resize the content size, as the content may not change following an orientation change, but just the frame).
I had the same problem with a Popup (UIView) that was called from a (UIViewController).
In the UIViewController the popup was created as followed
func createPopup() {
let myPopup = MyPopup(frame: UIScreen.mainScreen().bounds)
self.view.addSubview(myPopup)
}
In the Popup (UIView) I needed to override layoutSubviews as follows:
override func layoutSubviews() {
super.layoutSubviews()
self.setNeedsDisplay()
let mainScreenBounds = UIScreen.mainScreen().bounds
view.frame = mainScreenBounds
super.frame = mainScreenBounds <-- Need to update the parent
** perform additional rotation/orientation code here **
}

iPhone popup menu like iPad popover?

How can i implement this popup menu in iphone app like a popover in ipad?
EDIT: This is the best at moment: https://github.com/runway20/PopoverView
iOS 8 and later
Beginning with iOS 8, you can use UIPopoverPresentationController for iPhones in addition to iPads.
Setup
Add a UIBarButtonItem to your main View Controller.
Add another View Controller to the storyboard. Change it to the size that you want the popover to be and add any content that you want it to have. For my example I just added a UILabel. If you want a whole menu, then just add a table view or list of buttons.
Add a segue from the bar button item to the view controller that you will use as the popover. Rather than show, choose Present as Popover.
Select the segue in the storyboard and set the identifier to popoverSegue (or whatever string you called it in the code).
In the Attributes inspector for the popover view controller, check Use Preferred Explicit Size and confirm that it is the size you want it to be.
Code
This is the code for the main view controller that has the bar button item in it.
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "popoverSegue" {
let popoverViewController = segue.destinationViewController
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverViewController.popoverPresentationController!.delegate = self
}
}
// MARK: - UIPopoverPresentationControllerDelegate method
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
// Force popover style
return UIModalPresentationStyle.None
}
}
Popover at an arbitrary anchor point
If you want to set the popover to appear somewhere besides a bar button item (on a UIButton for example) then you need to set the sourceView and sourceRect. See this answer for details.
Further reading
The above example comes mostly from the first link.
iPad Style Popovers on the iPhone with Swift
iOS 8 Popover Presentations
UIPopoverPresentationController on iOS 8 iPhone
General overview of popup options in iOS
Have a look at the iPhone UIPopoverController implementation: WEPopover
On iPhone you would generally use a UIActionSheet for a stack of buttons like that. It slides up from the bottom, rather than popping up next to the button, but that's the standard behavior on iPhone.
There is one that is even better than WEPopover. Developed by a company called 50pixels, it is called FPPopover.
You can download FPPopover at https://github.com/50pixels/FPPopover
You would have to manually instantiate a UIView using a custom background image or drawing with transparency, add some UIButtons (or other type of custom view) on top, and also somehow handle all touches outside that view.
Note that is is non-standard UI. An actionsheet would be more HIG compliant.
To get a popover from a right side bar button item on a navigation controller that is part of a tableview controller, the following worked for me for Swift 4 and Xcode 9.
Follow the steps in Suragch answer above (as edited by the Community.)
Do not implement the Segue as shown in the answer above. For some reason, the segue causes the popover to go full screen despite setting the explicit size.
Give your popover view controller a title in Attributes Inspector
Add the following code in the TableView controller where the popup will show.
Modify the string identifier (the one here is referencing a Constant.swift file)
Modify "as! FilterVC" to use the title of the your popover view controller.
/// Shows a filter popover view
#IBAction func filterBtnPressed(_ sender: UIBarButtonItem) {
let popover = storyboard?.instantiateViewController(withIdentifier: FILTER_VC) as! FilterVC
popover.modalPresentationStyle = UIModalPresentationStyle.popover
popover.popoverPresentationController?.backgroundColor = UIColor.green
popover.popoverPresentationController?.delegate = self
popover.popoverPresentationController?.backgroundColor = ColorPalette.Blue.Medium
popover.popoverPresentationController?.sourceView = self.view
popover.popoverPresentationController?.sourceRect = CGRect(x: self.view!.bounds.width, y: 0, width: 0, height: 0)
popover.popoverPresentationController?.permittedArrowDirections = .up
self.present(popover, animated: true)
} }
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.none
}
You can check WYPopoverController: https://github.com/sammcewan/WYPopoverController
The screenshot above is not a UIActionSheet. It looks like a simple UIView subclass with custom UIButtons on top of it. So go ahead and create the subclass according to your needs and then add it as a subview to your view every time you need it.