Here is the code that creates my custom popUp:
extension UIViewController {
public func presentPopup(animated: Bool, completion: (() -> Void)? = nil) {
let popup = MBPopUpViewController(accentColor: UIColor(hexString: "E40C15"),
popUpTitle: "Hello",
popUpMessage: "Test PopUp",
popUpFirstButtonLabel: "1",
popUpSecondButtonLabel: "2")
popup.modalPresentationStyle = .overCurrentContext
present(popup, animated: animated, completion: completion)
}
}
Is there a way, how to make the first view controller a bit darker with fade animation, when the second appears?
Here you can see what I have now.
You can see an example here, in photos app. When the AlertViewController appears, the background becomes darker. Are there any ideas how to achieve that? Thanks :)
This sounds like you want a Modal Popover.
It is possible to make the background color of the view being placed over another a transparent color. You will need to set the color and everything for that view in it's viewDidLoad.
Ensure that the segue is marked Cover Vertical that so there's no wipe animation, and it's a smooth fade. Also enable either Over Current Context or Over Full Screen depending on how you would like this to look.
The end result will look something like below depending on what elements you place in your second View Controller that you are segueing to. (NOTE: the animation displayed in the gif is sped up. And the dark grey area would contain whatever it is that you place in the view).
There's some great resources out there to help you with making these. I can recommend Mark Moeykens on YouTube, he makes great videos about things like this.
Related
I created a UIStoryboardSegue to make a "Bottom Sheet segue". Our designer shared a screenshot of the app on his phone and the bottom sheet is displaying differently, despite the fact we are both on the same iOS version.
On mine and my simulator, when the bottom sheet opens, it lightens the source view and then shrinks it down a little, so it appears just barely behind the bottom sheet
On the same screen on the designers device, it dims the background and leaves the source view full size, showing the top of the buttons in the navigation bar
I've noticed the Apple maps bottom sheet behaves like the designers, no shrinking of the background view. But I can't see any settings that would affect this. How can I stop the sheet from resizing the source view on mine and function like it's supposed to?
Here's my code:
import UIKit
public class BottomSheetLargeSegue: UIStoryboardSegue {
override public func perform() {
guard let dest = destination.presentationController as? UISheetPresentationController else {
return
}
dest.detents = [.large()]
dest.prefersGrabberVisible = true
dest.preferredCornerRadius = 30
source.present(destination, animated: true)
}
}
Found a hack to force it to never minimise the source view at least, not really what I wanted, but keeps it consistent. Supposedly, .large() is always supposed to minimize the source view, you can avoid this in iOS 16 by creating a custom detent that is just a tiny bit smaller than large like so:
let customId = UISheetPresentationController.Detent.Identifier("large-minus-background-effect")
let customDetent = UISheetPresentationController.Detent.custom(identifier: customId) { context in
return context.maximumDetentValue - 0.1
}
dest.detents = [customDetent]
And as a bonus, found a way to control the dimming on the bottom sheet overlay. There is a containerView property on the presentationController, but it is nil when trying to access it in while in the segue. If you force code to run on the main thread, after the call to present, you can access the containerView and set your own color / color animation
e.g.
...
...
source.present(destination, animated: true)
DispatchQueue.main.async {
dest.containerView?.backgroundColor = .white
}
I have a UIButton on top of every other views, pushing the next screen, but it's unresponsive to tapping.
Debugging, I have noticed that the User Interaction is ON but also OFF. I mean:
When I select the button at the view hierarchy) it says is ON.
When I select the button directly at the storyboard, it says is OFF.
Take a look at the image above.
Also, the outlets are connected and the call is correct:
code
#IBAction public func onFrontPressed(_ sender: UIButton) {
let destination = OnboardingAViewController(nibName: "OnboardingAViewController", bundle: nil)
self.navigationController?.pushViewController(destination, animated: true)
}
Does anyone knows what's going on here?
Solved!
There was 1 fully transparent gradient view above the button.
When I deleted it, everything was working again.
Thanks, community!
I am pretty much trying to replicate the same tabBar popup as you see in the yelp app (before and after screenshots at the bottom) where no matter what view your in you can press on the center tabBar item and pop up will appear.
Coincidentally I have 5 tabBar items (like Yelp) and I am trying to have three popups with a image and title for each (like Yelp). Seeing that what I am trying to do is already done in an app shows me that this is possible, but I do not know how to do it. I have tried to change the types of relationships between view controllers or do it programmatically, but nothing seems to work. What am I missing or doing wrong?
Tabbar Controller Code:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController.tabBarItem.tag == 1 {
return false
} else {
return true
}
}
Before Press
After Press
One solution could be to adopt the UITabBarControllerDelegate.
This way we can use the func tabBarController(UITabBarController, didSelect: UIViewController) method of the delegate to alter the regular presentation behavior and show our popups instead. We could find the offset required (in terms of CGPoint) relative to the tab bar button and then apply that offset and add the popup buttons to as a subview. Note that for this method you would need to programmatically set the frames of your popup buttons.
Alternatively, you could also make a bunch of popup buttons and set their alphas to zero and one when clicked. Hope this helped! Thanks :)
Custom action for tab bar
UITabBarControllerDelegate documentation
I have a customized UISearchController with a custom UISearchBar as the title of a navigation controller, so that there's a persistent search bar control visible throughout the changes of child view controllers, which is shown in the first image of my gallery (not enough reputation to post images yet):
http://imgur.com/a/IikEw
However, when a user taps a search result displayed in a child table view controller, the navigation title is replaced with a text string, so that it looks like the second image in my gallery.
The problem occurs when I navigate back to the table view showing the search results, because for a brief moment, the search bar has a light gray background that looks like the third image in my gallery.
And when I slow down the animations in the simulator, I'm able to enter the Debug View Hierarchy in Xcode to see what the offending element is, which turns out to be a UIImageView named "UISearchBarBackground" that exists only until the transition animation ends, and the color snaps back to the intended result, as shown in the fourth image in my gallery.
In the initial view controller that initializes and sets the properties of my UISearchController, I've set the following properties of the search bar in viewDidLoad:
let controller = CustomSearchController(searchResultsController: self.searchResultsController)
controller.searchBar.backgroundColor = UIColor.clear
controller.searchBar.backgroundImage = nil
...and I have these custom classes, which I implemented so that I could get rid of the search controller's cancel button, which won't stay removed with delegate statements:
class CustomSearchBar: UISearchBar {
override func setShowsCancelButton(_ showsCancelButton: Bool, animated: Bool) {
super.setShowsCancelButton(false, animated: false)
}
}
class CustomSearchController: UISearchController {
lazy var _searchBar: CustomSearchBar = {
[unowned self] in
let customSearchBar = CustomSearchBar(frame: CGRect.zero)
return customSearchBar
}()
override var searchBar: UISearchBar {
get {
return _searchBar
}
}
}
As far as I can understand, at no point is my custom search controller deinitialized, and it is only initialized once, so I feel like the problem is appearing because of the drawing cycle, which temporarily places a visible view with a gray background before my settings take effect.
I'm pretty much at a loss as to what exactly is going on here, and how to solve this issue. I'm wondering if I'm just overlooking something simple, or if I instead have to create a custom transition object to solve the problem. I'm about an intermediate level with iOS architecture and Swift, but am always looking to learn more.
I've figured out that this issue is caused by not having a value set to the optional barTintColor property of UISearchBar. If the property is nil during a transition animation, then the view will appear light gray until the animation ends. To solve the problem, I set controller.searchBar.barTintColor = UIColor(red: 76/255, green: 203/255, blue: 124/255, alpha: 1), and now I'm getting the intended results.
Using iOS-9.2 and Swift-2.1:
Getting back to my rootViewController using the below code, unfortunately leads to a very strange behaviour of the status-bar of my App !
static func returnToRootViewController(sender: AnyObject) {
let initialscene = sender.storyboard?!.instantiateInitialViewController()
for _ in sender.view!!.window!.subviews {
sender.dismissViewControllerAnimated(true, completion: nil)
}
sender.view!!.window!.rootViewController = initialscene
}
The two images below show the status bar in its normal condition (i.e. left image) and after returning by above returnToRootViewController-Code (i.e right image with strange coloring) !
Prior to applying the above code, the navigation-controller was navigated to severeal modal popovers....
Any help on this appreciated !
You're dismissing the same viewcontroller several times. Calling dismissViewController will only dismiss the one presented by the sender, not the several underneath it (I'm assuming sender is the topmost one)
From this answer, it sounds like you might be able to dismiss all of them just by dismissing the first one presented by your rootViewController: https://stackoverflow.com/a/23566262/78496