With a custom UIAlertController, I am trying to set the background color to solid red. However, I am getting another blurry layer on top. How do I get rid of the blurry layer?
class CustomAlert: UIAlertController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.RED
}
}
let popUpEmailVerification: CustomAlert = {
let alert = CustomAlert(title: nil, message: "A verification email has been sent to your mailbox. Please open the link to finish verification.", preferredStyle: .alert)
return alert
}()
There's no direct API to achieve the effect you want, as far as I'm aware.
However, you can do what you're describing by messing around with the undocumented view hierarchy of the alert, like so:
let bgView = (alert.view.subviews.first?.subviews.first?.subviews.first!)! as UIView
and then set bgView.backgroundColor = .red. This is likely to bite you later.
Alternatively, you could use one of the many custom Alert libaries out here, SDCAlertView is one that I've used with good success.
Related
I have a share sheet/extension that, for some reason, is not fading the status bar (even though it fades the rest of the screen). I have included an image to demonstrate the issue. I simply want the entire screen to fade, including the status bar. In the past, I've had a similar issue with UIAlertControllers not fading the status bar.
i think its better to tweak it only necessary UIViewControllers (when needed) by using,
override var preferredStatusBarStyle: UIStatusBarStyle {
return UIStatusBarStyle.default //dark content
}
or on the parenting UINavigationController, to avoid above bugs you have experienced.
Are you presenting a UIAlertController with animated: set to true? I'm not able to reproduce this issue using the buo.showShareSheet() method:
let buo = BranchUniversalObject(canonicalIdentifier: "referrer/\(UUID().uuidString)")
buo.title = "Test"
buo.contentDescription = "Test"
let lp: BranchLinkProperties = BranchLinkProperties()
lp.feature = "referral"
lp.addControlParam("user_id", withValue: UUID().uuidString)
buo.showShareSheet(with: lp, andShareText: instructionString, from: self) { (params, success) in
}
Since I was seeing this as an app-wide issue, I started looking in my AppDelegate instead of the individual view controllers. I found a function, setStatusBarBackgroundColor(color: UIColor.white), called on app launch that appeared to be the cause.
func setStatusBarBackgroundColor(color: UIColor) {
guard let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView else { return }
statusBar.backgroundColor = color
}
Frankly, I'm not sure why I had written this, but removing it appears to solve the issue entirely.
I have a viewController EditMessage which has two UITextFields (UITextView) which use the keyboard and they work great. This part is basic standard stuff. When the keyboard is displayed, I register a tag gesture for the entire view, so that if the user clicks anywhere else, I dismiss the keyboard:
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(dismissKeyboard)))
In dismissKeyboard, this all works fine:
#objc func dismissKeyboard(sender: Any) {
self.view.endEditing(true)
}
However, I have a menu button(thumbnail image) implemented as a child view controller (UIViewController) on the same EditMessage view, which hijacks the screen via UIApplication.shared.keyWindow() to display an overlay and menu on the bottom of the screen. Built using the model/code from Brian Voong's YouTube channel to replicate a YouTube style slide in menu from the bottom. However, the keyboard is in the way. Since the child is a different view controller "endEditing" doesn't work (or maybe I am referencing the wrong view?).
class ButtonPickerController : UIViewController,
UIGestureRecognizerDelegate, UINavigationControllerDelegate {
var maxSize = CGFloat(60)
let thumbnail: UIImageView = {
let thumbnail = UIImageView()
thumbnail.backgroundColor = UIColor.lightGray
return thumbnail
}()
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.buttonTapped(sender:)))
tap.delegate = self
view.addGestureRecognizer(tap)
//view.backgroundColor = .yellow
view.contentMode = .scaleAspectFit
thumbnail.frame = CGRect(x: 0, y: 0, width: self.maxSize, height: self.maxSize)
setupSubviews()
}
Can someone point me in a good direction? This is my first question so hopefully I am asking properly.
I figured it out in the end. Thank you for the help. In my child view controller I did used the following statement when the button was tapped:
#objc func buttonTapped(sender: UITapGestureRecognizer? = nil) {
self.parent!.view.endEditing(true)
}
First, its not a good way to present overlays as UIViewController.
But a solution good be, to give the second viewcontroller a reference to the first one before viewDidLoad is called. Do you use Segues ? So in prepare would be the right place. In the second viewcontroller you create a property for the first one and then use this property as target when you create the UITapGestureRecognizer.
Another way is using a protocol and delegation.
How can one still background image in multiple view controllers be implemented?
With self.view.backgroundColor = UIColor(patternImage: UIImage(named: "file.png")) background would move along with UIViewController while screen switching.
Is it possible to place image on separate layer under view controllers?
Solution 1
Create a class called MyBackground:
class MyBackground {
class func addBackgroundImage() {
UIGraphicsBeginImageContext(self.view.frame.size)
UIImage(named: "file")?.drawInRect(self.view.bounds)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
view.backgroundColor = UIColor(patternImage: image)
}
}
If you want to call it in your AViewController, simply call MyBackground.addBackgroundImage()
Solution 2
Create a ViewController called MyViewController:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
addBackgroundImage()
}
func addBackgroundImage() {
UIGraphicsBeginImageContext(self.view.frame.size)
UIImage(named: "file")?.drawInRect(self.view.bounds)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
view.backgroundColor = UIColor(patternImage: image)
}
}
If you AViewController want to set this image, simply do like this:
AViewController: MyViewController //AViewController is a subclass of MyViewController
For example: I have an image call background, and I will set it as a background image of my ViewController:
If I just add it in one line as #startupthekid said:
If I add it as I said:
i think you can set background in your container view controllers, such as navi controller, tab controller, then set all your view controller's background to clear color.
A better answer is to use UIAppearance. If you're not familiar with it, UIAppearance is a proxy protocol that you send messages to and it modifies later instances of that class.
For example if I have a custom button class RoundButton:
RoundButton.appearance().titleFont = Font.Heavy(20)
And in my RoundButton class:
dynamic var titleFont: UIFont? {
get { return self.titleLabel?.font }
set { self.titleLabel?.font = newValue }
}
The dynamic keywords is incredibly important. It's used for objective-c compatibility and setting properties via UIAppearance that don't use it will cause a crash.
You can do as many custom properties as you want and there's a bunch already default in the os.
In your case you can do something like:
UIImageView.appearanceWhenContainedInInstancesOfClasses([MyCustomViewController.self)].customImage = UIImage(...)
You can of course get away with just a custom view controller but in my opinion, UIApperance keeps your UI code separate from your controller logic.
One way you could implement the behavior you want is a UIViewController subclass:
class MyCustomViewController: UIViewController {
private let backgroundImageView = UIImageView()
dynamic var backgroundImage: UIImage? {
get { self.backgroundImageView.image }
set { self.backgroundImageView.image = newValue }
}
}
and then modify the backgroundImage property on the appearance instance of MyCustomViewController.
MyCustomViewController.appearance().backgroundImage = ...
EDIT: Given that your question was misleading and that you instead want a shared image that never moves throughout all your view controllers, you can simply insert an image view into the application window:
if let window = UIApplication.sharedApplication().windows.first {
let imageView = UIImageView(image: UIImage(named: "face-mask"))
window.addSubview(imageView)
imageView.frame = window.bounds
}
If you like Autolayout, like I do, then I'd set constraints to pin the image view to the window edges.
you should set the image in viewDidload of every viewController
Update :
either you should set in every view controller or you can set to windows . Second thing if your background changes dynamically with different images then you can use prepareforsegue method to pass images to nextviewcontroller if images are different for each viewcontroller. if same image for every viewcontroller then you can set it to window.
:)
there are many answers on Stack Overflow about this but none seem to work. how do you make the background color of a UIAlertViewController truly clear?
i have at the moment:
let errorAlert = UIAlertController(title: "Title", message: "Message", preferredStyle: UIAlertControllerStyle.Alert)
let subview = errorAlert.view.subviews.first! as UIView
let alertContentView = subview.subviews.first! as UIView
alertContentView.backgroundColor = UIColor.clearColor()
errorAlert.view.backgroundColor = UIColor.clearColor()
showViewController(errorAlert, sender: self)
but the result is a kind of white tinted, transparent-ish background over the image... is there anyway to remove this tinted background?
In order to achieve this the color of all subviews needs to be set to UIColor.clear. In addition, all UIVisualEffectView child views need to be removed. This can be accomplished by using a recursive function (Swift 4):
func clearBackgroundColor(of view: UIView) {
if let effectsView = view as? UIVisualEffectView {
effectsView.removeFromSuperview()
return
}
view.backgroundColor = .clear
view.subviews.forEach { (subview) in
clearBackground(of: subview)
}
}
Call this right after creating the UIAlertController instance:
let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
clearBackgroundColor(of: alert.view)
If you now want to change the appearance of the alert:
alert.view.layer.backgroundColor = UIColor.red.withAlphaComponent(0.6).cgColor
alert.view.layer.cornerRadius = 5
You need to access the superview of the UIAlertController
alertView.view.superview?.backgroundColor = UIColor.clearColor()
I'd like to use the UIViewController's input accessory view like this:
override func canBecomeFirstResponder() -> Bool {
return true
}
override var inputAccessoryView: UIView! {
return self.bar
}
but the issue is that I have a drawer like view and when I slide the view open, the input view stays on the window. How can I keep the input view on the center view like Slack does it.
Where my input view stays at the bottom, taking up the full screen (the red is the input view in the image below):
There are two ways to do this exactly like Slack doing it, Meiwin has a medium post here A Stickler for Details: Implementing Sticky Input Field in iOS to show how he managed to do this which he actually puts an empty UIView as an inputAccessoryView then track it’s coordinates on screen to know where to put his custom view in relation with the empty view, this way can be helpful if you are going to support SplitViewController on iPad, but if you are not interested in this way, you can see how I managed to do this like this image
Here is before swiping
Here is after
All I did was actually taking a snapshot from the inputAccessoryView window and putting it on the NavigationController of the TableViewController
I am using SideMenu from Jon Kent and it’s pretty easy to do it with the UISideMenuNavigationControllerDelegate
var isInputAccessoryViewEnabled = true {
didSet {
self.inputAccessoryView?.isHidden = !self.isInputAccessoryViewEnabled
if self.isInputAccessoryViewEnabled {self.becomeFirstResponder()}
}
}
func sideMenuWillAppear(menu: UISideMenuNavigationController, animated: Bool) {
let inputWindow = UIApplication.shared.windows.filter({$0.className == "UITextEffectsWindow"}).first
self.inputAccessoryViewSnapShot = inputWindow?.snapshotView(afterScreenUpdates: false)
if let snapShotView = self.inputAccessoryViewSnapShot, let navView = self.navigationController?.view {
navView.addSubview(snapShotView)
}
self.isInputAccessoryViewEnabled = false
}
func sideMenuDidDisappear(menu: UISideMenuNavigationController, animated: Bool) {
self.inputAccessoryViewSnapShot?.removeFromSuperview()
self.isInputAccessoryViewEnabled = true
}
I hope that helps :)