With this code I get indicator but to top left corner of webView and behind borders
(I have DetailView storyboard but want to write code programmatically)
var webView = WKWebView()
var detailItem : CountriesFinal?
var spinner = UIActivityIndicatorView()
override func viewDidLoad() {
super.viewDidLoad()
spinner.frame = CGRect(x: webView.frame.width / 2, y: webView.frame.height / 2, width: 10, height: 10)
webView.addSubview(spinner)
spinner.startAnimating()
spinner.hidesWhenStopped = true
webView.backgroundColor = .blue
webView.translatesAutoresizingMaskIntoConstraints = false
webView.navigationDelegate = self
webView.frame = CGRect(x: 125, y: 150, width: 250, height: 150)
webView.layer.borderWidth = 5
webView.layer.borderColor = UIColor.red.cgColor
webView.contentMode = .scaleAspectFit
webView.backgroundColor = .blue
self.view.addSubview(webView)
I notice that this line doesn't change anything and I thought it may be the solution:
spinner.frame = CGRect(x: webView.frame.width / 2, y: webView.frame.height / 2, width: 10, height: 10)
I've tried to set translatesAutoresizingMaskIntoConstraints of spinner to false but then whole webView disappear with purple warning " Layout Issues: Position and size are ambiguous for WKWebView."
UPDATED CODE(webVIew not showing):
webView.frame = CGRect(x: 125, y: 150, width: 250, height: 150)
webView.backgroundColor = .blue
webView.translatesAutoresizingMaskIntoConstraints = false
webView.navigationDelegate = self
webView.layer.borderWidth = 5
webView.layer.borderColor = UIColor.red.cgColor
webView.contentMode = .scaleAspectFit
webView.backgroundColor = .blue
self.view.addSubview(webView)
self.view.insertSubview(spinner, aboveSubview: webView)
spinner.centerXAnchor.constraint(equalTo: webView.centerXAnchor).isActive = true
spinner.centerYAnchor.constraint(equalTo: webView.centerYAnchor).isActive = true
spinner.startAnimating()
spinner.hidesWhenStopped = true
In my opinion WKWebView is one of the system views that should probably be a "leaf" in your view hiercarchy. It is very complex in the content it manages and may not like it if you add subviews.
Instead of putting the spinner inside the WKWebView, I would probably put it into a view that is a sibling and stack it in front of the WebView in the z-order. It will float above the web view, but look like it is inside of it.
Here's a Playground I threw together that demonstrates the idea:
import UIKit
import WebKit
import PlaygroundSupport
NSSetUncaughtExceptionHandler { error in
debugPrint(error)
}
let controller = UIViewController()
let webView = WKWebView()
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
webView.load(URLRequest(url: URL(string: "https://www.apple.com")!))
let spinner = UIActivityIndicatorView()
spinner.translatesAutoresizingMaskIntoConstraints = false
controller.view.addSubview(webView)
webView.frame = controller.view.bounds
controller.view.insertSubview(spinner, aboveSubview: webView)
spinner.centerXAnchor.constraint(equalTo: webView.centerXAnchor).isActive = true
spinner.centerYAnchor.constraint(equalTo: webView.centerYAnchor).isActive = true
PlaygroundSupport.PlaygroundPage.current.liveView = controller
spinner.startAnimating()
Working code. Changed place of addSubview
webView.frame = CGRect(x: 125, y: 150, width: 250, height: 150)
webView.backgroundColor = .blue
webView.translatesAutoresizingMaskIntoConstraints = false
webView.layer.borderWidth = 5
webView.layer.borderColor = UIColor.red.cgColor
webView.contentMode = .scaleAspectFit
webView.backgroundColor = .blue
self.view.addSubview(webView)
self.webView.addSubview(self.spinner)//first we need to add subview then make center or something
spinner.center = CGPoint(x: webView.bounds.width / 2, y: webView.bounds.height / 2)
webView.navigationDelegate = self
spinner.startAnimating()
spinner.hidesWhenStopped = true
Related
I have added a card view from a Xib file which when tapped it slides from bottom-up to display the view.
I have am trying to customised the view and a drop shadow to the view which is not working.
The cornerRadius works fine otherwise.
func setupCard() {
menuCardVC = MenuCardVC(nibName:"MenuCardVC", bundle:nil)
menuCardVC.view.layer.shadowOpacity = 0.50
menuCardVC.view.layer.shadowRadius = 12
menuCardVC.view.layer.shadowColor = UIColor.black.cgColor
menuCardVC.view.layer.shadowOffset = CGSize.zero
menuCardVC.view.layer.cornerRadius = 25
self.addChild(menuCardVC)
self.view.addSubview(menuCardVC.view)
menuCardVC.view.frame = CGRect(x: 0, y: self.view.frame.height - cardHandleAreaHeight, width: self.view.bounds.width, height: cardHeight)
menuCardVC.view.clipsToBounds = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(VenueDetailsVC.handleCardTap(recognzier:)))
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(VenueDetailsVC.handleCardPan(recognizer:)))
menuCardVC.handleArea.addGestureRecognizer(tapGestureRecognizer)
menuCardVC.handleArea.addGestureRecognizer(panGestureRecognizer)
}//end setupCard
Remove the below line of code,
menuCardVC.view.clipsToBounds = true
Since your view hierarchy is simple you could use the following technique. Also, you don't need to use clipsToBounds.
let superview = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
superview.backgroundColor = .white
let subview = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
subview.backgroundColor = .red
// First provide the corner radius.
subview.layer.cornerRadius = 8
// Then provide the shadow parameters.
subview.layer.shadowOpacity = 1
// ...
// Do not clip to bounds since it will clip the shadow.
// ...
superview.addSubview(subview)
I am trying to make a view programatically, making a card with an image and some text and overlaying it with some color. I have turned off clip to bounds and added some colors to make it more visual.
So when I set
overlay.frame = self.frame
or
overlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
the overlay should cover the entire view but it does not, why is this?
This is what is showing
This is what I want to see, but with each card having a blue layer on top
private var leftImage: UIImageView = {
let i = UIImageView(frame: .zero)
i.contentMode = .scaleAspectFill
i.image = UIImage()
return i
}()
private var topLabel: UILabel = {
let label = UILabel(frame: .zero)
label.isUserInteractionEnabled = false
label.font = UIFont.boldSystemFont(ofSize: 16.0)
label.lineBreakMode = .byWordWrapping
label.adjustsFontSizeToFitWidth = true
label.numberOfLines = 2
return label
}()
var bottomLabel: UILabel = {
let label = UILabel(frame: .zero)
label.isUserInteractionEnabled = false
label.lineBreakMode = .byWordWrapping
label.font = UIFont.systemFont(ofSize: 12.0)
label.numberOfLines = 1
label.text = "60 seconds"
return label
}()
private var stackView: UIStackView = {
let stack = UIStackView(frame: .zero)
stack.axis = .vertical
stack.isUserInteractionEnabled = false
stack.distribution = .fillEqually
stack.layoutMargins = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
stack.isLayoutMarginsRelativeArrangement = true
return stack
}()
private var overlay: UIView = {
let view = UIView(frame: .zero)
view.backgroundColor = .blue
view.alpha = 0.8
//view.isHidden = true
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
create()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
create()
}
private func create() {
self.translatesAutoresizingMaskIntoConstraints = false
//leftview
leftImage.frame = CGRect(x: 0, y: 0, width: frame.width / 2, height: frame.height)
addSubview(leftImage)
//rightVIew
stackView.frame = CGRect(x: frame.midX, y: 0, width: frame.width / 2, height: frame.height)
clipsToBounds = false
//add views to stack
stackView.addArrangedSubview(topLabel)
stackView.addArrangedSubview(bottomLabel)
addSubview(stackView)
overlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
addSubview(overlay)
//card styling
layer.cornerRadius = 10
}
overlay.frame = self.frame
That can never be right, except by accident, because the frame of overlay and the frame of self are in two different coordinate systems. At the very least you want to say
overlay.frame = self.bounds // bounds, not frame
However, that isn't going to work either unless you say it in the right place. The right place is when layout occurs:
override func layoutSubviews() {
super.layoutSubviews()
self.overlay.frame = self.bounds
}
Simply adding that to your existing code should solve the problem.
The issue is that you're setting your overlay's dimensions like so:
overlay.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
before the superview has determined its final width and height. You'll need to do one of the following:
Call create() after the superview class has determined its final size
Continuously set the overlay's frame as the superview's width and height changes (see https://developer.apple.com/documentation/uikit/uiview/1622482-layoutsubviews)
Use AutoLayout constraints via a xib/storyboard file
I used this code in WebKit ios 11.2 where my google map frame not showing to fit to screen it showing like this I attached the screenshot.
let topBarHeight = UIApplication.shared.statusBarFrame.size.height +
(self.navigationController?.navigationBar.frame.height ?? 0.0)
webView.frame = CGRect.init(x: 0, y: topBarHeight + 20, width: self.view.frame.width, height: self.view.frame.height - topBarHeight)
self.view.addSubview(webView)
self.navigationController?.isNavigationBarHidden = true
webView.backgroundColor = UIColor.clear
webView.scrollView.isScrollEnabled = false
webView.scrollView.bounces = false
webView.loadHTMLString("<html><body><iframe width=\"\(self.webView.frame.size.width)\" height=\"\(self.webView.frame.size.height-70)\" frameborder=\"0\" style=\"border:0\" src=\"https://www.google.com/maps/embed/v1/place?key=\(GoogleApikey)&q=\(Company.Instance.shopAddress),\(Company.Instance.shopName)\"allowfullscreen></iframe></body></html>", baseURL: nil)
Constraints aren't fully set in viewDidLoad(). you should move your code in other life cycle methods like viewDidAppear() and use the following HTML String for loading it
webView.loadHTMLString("<html><body><iframe frameborder=\"0\" style=\"border:0;width: 100%;height:100%\" src=\"https://www.google.com/maps/embed/v1/place?key=\(GoogleApikey)&q=\(Company.Instance.shopAddress),\(Company.Instance.shopName)\"allowfullscreen></iframe></body></html>", baseURL: nil)
Where do you add your map?
You could try to add it inside function viewDidLoad. For example:
override func viewDidLoad() {
super.viewDidLoad()
webView.frame = CGRect.init(x: 0, y: topBarHeight + 20, width: self.view.frame.width, height: self.view.frame.height - topBarHeight)
self.view.addSubview(webView)
self.navigationController?.isNavigationBarHidden = true
webView.backgroundColor = UIColor.clear
webView.scrollView.isScrollEnabled = false
webView.scrollView.bounces = false
}
And you use it self.navigationController?.isNavigationBarHidden = true, but navigation bar is not hidden. Did you implement custom navigation bar and hide native bar? Or it does not have to be visible?
I have a UIView called containerView.
Upon creating the UIView & Adding it to the main view, I'm trying to add a little shadow. My code:
let containerView = UIView()
let bg_clear = UIColor(hexString: "#34495E")
containerView.backgroundColor = bg_clear
containerView.layer.cornerRadius = 15
containerView.clipsToBounds = false
containerView.dropShadow() // Generate Shadow
view.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.heightAnchor.constraint(equalToConstant: 50).isActive = true
containerView.widthAnchor.constraint(equalToConstant: 300).isActive = true
containerView.centerXAnchor.constraint(equalTo: tabBar.centerXAnchor).isActive = true
Code to generate the Shadow:
extension UIView {
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor(hexString: "#000000").cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -1, height: 1)
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
This should help :
extension UIView {
func addShadow(withOpacity opacity:Float, radius:CGFloat, andColor color:UIColor) {
self.layer.shadowColor = color.cgColor
self.layer.shadowOffset = CGSize(width:-1.0, height:1.0)
self.layer.shadowOpacity = opacity
self.layer.shadowRadius = radius
}
}
Example :
containerView.addShadow(withOpacity: 0.4, radius: 0.4, andColor: .black)
A few points to note:
Make sure your container view is not too near the left or the bottom of the screen, because the shadow is going show up to the left and below the container view.
Make sure you use a rounded rect for the shadow path, because your container view has a non-zero corner radius.
If you still can't see the shadow, try increasing the shadow offset.
You can copy and paste this code into a playground and open the live view:
extension UIView {
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -5, height: 5) // I made this larger
layer.shadowRadius = 1
// I used a rounded rect here
layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
let view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
view.backgroundColor = .white
let containerView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
let bg_clear = UIColor.green
containerView.backgroundColor = bg_clear
containerView.layer.cornerRadius = 15
containerView.clipsToBounds = false
containerView.dropShadow() // Generate Shadow
view.addSubview(containerView)
PlaygroundPage.current.liveView = view
You should see:
I am creating an animation which brings an image and a label from the left to the centre of the view.
imageLogo.isHidden = true is not hidden when App is run
labelLogo is not shown at all on the view
I have been reading tutorials, but I just don't see what is wrong with my code.
let paymentLogo = UIImage(named: "paymentImage")
var imageLogo:UIImageView!
var overlayView = UIView()
var logoAppeared:Bool!
let labelLogo = UILabel()
override func viewDidLayoutSubviews() {
//move picture off the screen here
self.imageLogo = UIImageView(image:paymentLogo)
imageLogo.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
imageLogo.center.x -= 400
self.view.addSubview(imageLogo)
self.labelLogo.frame =
CGRect(x: 0, y: 0, width: 200, height: 21)
self.labelLogo.center.x -= 400
self.labelLogo.text = "Booking Completed"
self.labelLogo.textAlignment = .center
self.view.addSubview(labelLogo)
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 2.0, delay: 0.1, options: [], animations: {
//animate paymentCompletedLogo
self.overlayView = UIView(frame: self.view.frame)
self.overlayView.backgroundColor = UIColor.black
self.overlayView.alpha = 0.4
//animate labelLogo
self.labelLogo.frame = CGRect(x: self.view.center.x, y: 90, width: 200, height: 21)
self.labelLogo.backgroundColor = UIColor.gray
self.labelLogo.text = "Booking Completed"
self.labelLogo.textColor = .black
self.labelLogo.textAlignment = .center
//animate imageLogo
self.imageLogo.frame =
CGRect(x: self.view.center.x,y: self.view.center.y,width: 100,height: 100)
self.view.addSubview(self.overlayView)
self.view.addSubview(self.imageLogo)
self.view.addSubview(self.labelLogo)
}) { finished in
self.overlayView.isHidden = true
self.imageLogo.isHidden = true //it is not hidden in simulator
self.logoAppeared = true
}
}
I tried removing following your code from animation method it worked fine.
You are trying to add subview in animation method. imageLogo and labelLogo is all ready added in view.
self.view.addSubview(self.overlayView)
self.view.addSubview(self.imageLogo)
self.view.addSubview(self.labelLogo)