Swift iOS -Programmatic RootViewController is Light Gray? - uiviewcontroller

I'm just getting in to programmatic vc's with no more storyboards and I'm following YouTube's LetsBuildThatApp by Brian Voong for guidance https://youtu.be/NJxb7EKXF3U?list=PL0dzCUj1L5JHDWIO3x4wePhD8G4d1Fa6N.
I followed all the directions and for some reason when I launch my app I get this light gray haze over my screen and I can't figure out why? I can faintly see the navigation title and blue background but it's covered by a faded layer.
Step 1:
I deleted my storyboard file and sunder the General Tab under Deployment Info I deleted "Main" from Main Interface.
Step 2:
I changed my ProjectNavigator file to FeedController then Changed the file accordingly
import UIKit
class FeedController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Facebook Feed"
collectionView?.backgroundColor = UIColor.white
}
}
Step 3:
In AppDelegate I added a NavVC and made FeedVC it's root and made the NavVC the Window's root. I also change the NavBar and StatusBar color
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let feedController = FeedController(collectionViewLayout: UICollectionViewFlowLayout())
let navVC = UINavigationController(rootViewController: feedController)
window?.rootViewController = navVC
UINavigationBar.appearance().tintColor = UIColor.blue
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
application.statusBarStyle = .lightContent
return true
}
Step 4:
In info.plist I set View controller-based status bar appearance to NO
I can't figure out why I get this light gray haze over my screen
What am I missing here?

It looks like you are configuring the tintColor instead of the barTintColor. The tintColor changes the color for the navigation buttons and the barTintColor adjusts the navigation bar background color. You can watch this video for more details on customizing navigation bar appearance.
https://www.youtube.com/watch?v=RO8_mqRJO-4

Related

Transport bar live label in AVPlayerViewController (tvOS only)

It seems impossible to disable or change the red backgrounded LIVE label in the AVPlayerViewController's transport bar on tvOS 15.2. Also, it is enabled by default ever since 15.2 came out. Here is a picture of the label shown here:
I have tried to play around with some of the available options like this one:
guard let pvc = playerRenderController as? AVPlayerViewController else { return }
pvc.transportBarIncludesTitleView = false
It does hide the LIVE label but it also hides the title (Straight Rhythm) from the image.
Furthermore, the label text seems to be specific to the locale, so in Danish it would be DIREKTE instead of LIVE.
Here is a video that showcases this feature (time 03:08):
Any suggestions on how to hide/change this label?
Hello I found a workaround to do it.
After you play the stream call this function with parameters the playerViewController.view
private func removeLiveLabel(view: UIView) {
let list = ["_AVPlayerViewControllerContainerView", "_AVFocusContainerView", "UIView"]
for subview in view.subviews where list.contains(String(describing: type(of: view))) {
for subSubview in subview.subviews {
if subSubview.value(forKey: "class").debugDescription.contains("_AVxOverlayPlaybackAuxiliaryMetadataView") {
subSubview.removeFromSuperview()
return
}
}
removeLiveLabel(view: subview)
}
}
This function will search the subviews of the AVPlayerViewController object controls and will remove the badge from the view without removing the Title.

Adjusting the height of a wkwebview programmatically

This is my first question on Stack Overflow. I just started learning swift programming and got sucked into something.
I followed IAP tutorials on YouTube and successfully implemented AdMob banners and interstitial ads in my app. I was also able to turn off ads using the IAP. My question is:
I have a view in which I have two UI elements (WKWebViewand a GADBannerView). The WKWebView element covers 90% of the screen starting from x:0,y:0, whereas the GADBannerView element covers 10%. I turned off ads and hid the GADBannerView element using IAP.
Now I want to dynamically/programmatically adjust the WKWebView size to fill the entire screen, i.e 100%. In other words, I want the WKWebView element to extend over the hidden GADBannerView element.
This is because hiding the GADBannerView leaves a blank field which is not cool to the view and the WKWebView looks truncated.
Please note that neither of the views are subviews. Both are independent views added separately. I understand that I can initially make the web view fill entire screen, add the GADBannerView on top of it, and when I remove ads and hid the GADBannerView, the web view will fill screen. That is not what I want because some content of the web view can not be seen using this approach. If I have a button at the end of HTML page that loads on the web view, this button can not be clicked because it will always be behind the gad banner view even when scrolling reached the bottom. Yes, you can scroll and hold to see the button, but once you release it, it will go back down.
So as a recap, I have two separate views and want to hid one and extend the length of the other to cover the entire screen.
Please tell me how to achieve that.
thirdBannerView.isHidden = true //Hide the banner view
//then code below to increase the size of the web view to equal device //screen width and height i.e full screen.
func webViewDidFinishLoad(webView: WKWebView) {
//let screenBounds = UIScreen.main.bounds
// let heightq = screenBounds.height
//let widthq = screenBounds.width
//webView.frame.size.height = heightq
//webView.frame.size = webView.sizeThatFits(CGSize.zero)
//webView.frame = CGRectMake(0, 0, widthq, heightq);
webView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
}
This code is not effective at all as nothing changes. Please let me know how to achieve this.
This particular scenario looks promising for applying UIStackview. Add your two view ( WKWebview and GADBannerView). apply fixed height for the GADBannerview. Whenever necessary just hide the GADBannerview.
Sample code
class StackviewController : UIViewController {
let stackview: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.distribution = .fill
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// Your WKWebview here
let sampleWKWebView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// Your GADBannerView here
let sampleGADBannerView: UIView = {
let view = UIView()
view.backgroundColor = .green
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
func setupViews() {
view.addSubview(stackview)
stackview.addArrangedSubview(sampleWKWebView)
stackview.addArrangedSubview(sampleGADBannerView)
NSLayoutConstraint.activate([
stackview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0),
stackview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0),
stackview.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0),
stackview.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0),
sampleGADBannerView.heightAnchor.constraint(equalToConstant: 100.0)
])
// Enable this line to hide the GADBannerView
// sampleGADBannerView.isHidden = true
}
}
Here is the output
I use two UIView to represent the WKWebView & GADBannerView. In the sample code uncomment the following to hide the bottom banner like green view.
sampleGADBannerView.isHidden = true

UINavigationController disappears when I switch to a different tab while UISearchController is visible

In my app I have a UITabBarController with several UINavigationController's. I am trying to add a search feature like in Apple's Music app, in which a search icon appears in every UIViewController's navigatonItem. When this icon is tapped, a UISearchController is presented.
The problem I am facing is that if I present the UISearchController and switch to a different tab (before typing anything in the UISearchController's searchBar), when I come back to that tab the UINavigationController is not visible anymore. Even if I cancel the search, the UISearchController is dismissed but I cannot see my original UINavigationController. If I do type something into the search bar before switching to a different bar and back, the search results remain always visible but when I dismiss the UISearchController my UINavigationController does not show up. I can only see the UITabBarController's tabBar and everything else completely white.
However, if once the UISearchController has been dismissed, if I go to another tab and then back to the original one, the UINavigationController appears again.
Here's the code for my UIViewController subclass:
let searchResultsController = SearchResultsController()
var searchController: UISearchController!
var searchButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
searchButton = UIBarButtonItem(barButtonSystemItem: .Search, target: self, action: Selector("showSearch:"))
navigationItem.rightBarButtonItem = searchButton
searchController = UISearchController(searchResultsController: searchResultsController)
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
searchController.hidesNavigationBarDuringPresentation = false
searchController.dimsBackgroundDuringPresentation = false
}
func showSearch(sender: AnyObject? = nil) {
presentViewController(searchController, animated: true, completion: nil)
}

Sending data from one uiviewcontroller to another navigationcontroller using present modally

I am new to swift i am using storyboard and have used navigationcontrollers to connect from one viewcontroller to another. I want to send the name of the image clicked to the next viewcontroller which is connected modally in storyboard from the imageView. I searched lot about transferring data from oneviewcontroller to another viewcontroller connected with navigationcontroller modally but no solution was available. Please let me know if any of the code is required as i dont know how to go ahead with it. I know this might be silliest question but posting this after searching a lot on net.
EDIT according to #uraimo reply.
Do i need to provide name to every segue i created on storyboard?.
I have 2 fixed images on viewcontrollerA and i have placed a uibutton with transparent background and no text on each of them and then ctrl drag to navigation controller of viewcontrollerB for presenting modally and unwinding the backbutton i.e. UIBarButtonItem to viewcontrollerA by ctrl drag the back button of viewcontrollerB to exit of the viewcontrollerB and unwinding it.
This is how i have created navigation from any of the image click out of 3 images of viewcontrollerA to viewcontrollerB and back to viewcontrollerA on back button click of viewcontrollerB.
Please let me know if i am doing anything wrong and will your prepareForSegue code be useful in accomplishing my task.
Basically, both using IB or when you do it programmatically, you have to configure your new viewcontroller with all the data it needs before the segue is performed (or the controller is presented via code).
In your case, just set the image name (your custom view controller class YourViewController should have a specific String property to hold this value) overriding prepareForSegue in the current view controller class:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "yourModalSegueIdentifier" {
let imgName= (sender as! UIImageView)
let destination = segue.destinationViewController as UINavigationController
let yourController = destination.topViewController as YourViewController
yourController.imageName= <name here>
}
}
This solves the passing data question.
But in your case, you need the name of the clicked image, and that can be only obtained adding a click event through a UIGestureRecognizer to the UIImageView.
So, you'll need a uigesturerecognized that on click will perform the segue you've created. And also, you will not be able to get the name of the image asset (the one you use the creating an UIImage using imageNamed:) because it's not available anymore, and yo'll have to use the accessibilityIdentifier.
This makes everything way more complicated, it seems it could be done for the most part graphically here and here(but it's not really clear how to do it), but it's usually done via code.
Simplifying it a bit using a global variable:
var currentImage = ""
func viewDidLoad(){
...
...
let aTap = UITapGestureRecognizer(target: self, action: Selector("imageTapped:"))
aTap.numberOfTapsRequired = 1
//For every image, configure it with recognizer and accessibilityId:
firstImage.userInteractionEnabled = true
firstImage.addGestureRecognizer(aTap)
firstImage.accessibilityIdentifier = "firsImage"
...
}
func imageTapped(recognizer:UITapGestureRecognizer) {
let imageView = recognizer.view as! UIImageView
currentImage = imageView.accessibilityIdentifier
self.performSegueWithIdentifier("yourModalSegueIdentifier", sender: self)
}
And change this:
yourController.imageName= <name here>
to this:
yourController.imageName= currentImage
Update:
Do i need to provide name to every segue i created on storyboard?
Yes, it's the only way to identify them, every UIStoryboardSegue has an identifier. But remember, segues are not the only way to go from a controller to another, if you do it completely programmatically (no segues) you usually call "presentViewController". Segues are a storyboard concept.
Again, regarding the segue name/identifier, you didn't need it until now because you never referenced that segue from your code, you need it for both prepareForSegue and performSegueWithIdentifier. Just select the segue and give it a name on the right inspector pane.
The structure you describe seems ok, the only thing it's that i'm not so sure that the UIButtons are really needed, try with a modal segue from the imageview or directly from the viewcontroller to the destination view controller.
Update 2:
If you are starting and need a free course that will teach you the basics and also make you build a few interesting ios apps i recommend hackingwithswift.
check out how I did this
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "AddItem":
let destination = segue.destination as? UINavigationController
guard let itemViewController = destination?.topViewController as? ItemViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
itemViewController.collection = collection
case "EditCollection":
guard let collectionViewController = segue.destination as? EditCollectionViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
collectionViewController.collection = collection
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}

How to set UIWebView initial position?

How to set the initial focus in a HTML page using UIWebView. For example, below is the HTML page. If i load this in UIWebView, it shows from (0,0) coordinates of a HTML view. But i want to change the starting position as described in the below image. I tried to change the offset, but no use.
webView.scrollView.contentOffset = CGPointMake(100, 100).
You probably shouldn't hard code this because for one reason, it's hard-coded. If google, or whatever website you want to display alters their layout your positions will be obsolete. And you will have to resubmit your entire app just to off-set this.
My recommendation is use auto layout and 'scales page to fit' for the UIWebView.
But your code is accurate. Make sure you put it in webViewDidFinishLoad: or viewDidLoad and your UIWebViewDelegate & UIScrollViewDelegate are added to your header file and called in your implementation file :
self.webView.delegate = self;
self.webView.scrollView.delegate = self;
Place the UIWebView into a subview. A generic UIView will work.
When instantiating the UIWebView, give it screen bounds with a slightly lower width than the containing UIView. Weird, I know.
Set the UIWebView's contentOffset in the webViewDidFinishLoad method of your UIWebViewDelegate
The following code works for me, in Xcode 6.1.1. To test, just make this ViewController class the root view controller for your app:
import UIKit
class ViewController: UIViewController, UIWebViewDelegate {
override func loadView () {
let screenBounds : CGRect = UIScreen.mainScreen().bounds
var slimmerBounds : CGRect = UIScreen.mainScreen().bounds
slimmerBounds.size.width = slimmerBounds.size.width - 1
let webView = UIWebView(frame: slimmerBounds)
let url = NSURL (string: "http://google.com/")
let req = NSURLRequest(URL: url!)
webView.loadRequest(req)
webView.delegate = self;
let contentView = UIView(frame: screenBounds)
contentView.addSubview(webView)
self.view = contentView
}
func webViewDidFinishLoad(webView: UIWebView) {
webView.scrollView.contentOffset = CGPointMake(0, 100)
webView.bounds = UIScreen.mainScreen().bounds
}
}
By the way ... this will set the initial position of all URLs loaded into this Web view. If you only want to position the first page, you'll need to add some additional logic to webViewDidFinishLoad