How to set a custom background view properly with UiActivityIndicator - ios

My app is using WKWebView to load my website. I need a UiActivityIndicator with a custom dark gray background view due to the UIActivityIndicator might be difficult to spot when it mixed up with my website's content. Previously, I followed https://coderwall.com/p/su1t1a/ios-customized-activity-indicator-with-swift to programmatically create an indicator. However, the tutorial is using default white and mine is large white with custom orange color. All I need just a backgroundView with an indicator inside of the backgroundView. Somehow it works until I noticed that the indicator is not that center and it ran off a little bit to the top left. Please see this image,
It is very obvious on my iPhone 5 and so on. So, I used another approach which is using auto layout. The background view is still not working as well. Here is my auto layout setup
This is my programmatically part.
loadingView.frame = CGRect(x: 0, y: 0, width: 60, height: 60)
loadingView.center = self.view.center
loadingView.backgroundColor = UIColor(red:211/255,green:211/255,blue:211/255, alpha: 1)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
loadingIndicator.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
loadingIndicator.isOpaque = false
loadingIndicator.center = CGPoint(x: loadingView.frame.size.width / 2.0, y: loadingView.frame.size.height / 2.0)
loadingIndicator.backgroundColor = UIColor.gray
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
loadingIndicator.color = UIColor(red:232/255,green:126/255,blue:4/255,alpha:1.0)
loadingIndicator.hidesWhenStopped = true
loadingView.addSubview(loadingIndicator)
self.view.addSubview(loadingView)
I would prefer using auto layout.

There is no issue in either your auto layout setup nor the code you are trying.
The off-center issue actually seems to be an iOS bug when setting a custom color on a UIActivityIndicatorView.
You can see this for yourself.
Drag a basic UIActivityIndicatorView
Set a background color
Set it's style to "Large White"
Now set a color
This doesn't happen when the style is White or Gray
Tested it in Xcode 9.3 Storyboard as well as Simulator with Debug > Color Blended layers - ON

Try to change this code
let loadingView = UIView()
let loadingIndicator = UIActivityIndicatorView()
loadingView.frame = CGRect(x: 0, y: 0, width: 60, height: 60)
loadingView.center = self.view.center
loadingView.backgroundColor = UIColor(red:211/255,green:211/255,blue:211/255, alpha: 1)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 10
loadingIndicator.frame = CGRect(x: 0, y: 0, width: 37, height: 37)
loadingIndicator.isOpaque = false
loadingIndicator.center = CGPoint(x: loadingView.bounds.size.width / 2, y: loadingView.bounds.size.height / 2)
loadingIndicator.backgroundColor = UIColor.gray
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge
loadingIndicator.color = UIColor(red:232/255,green:126/255,blue:4/255,alpha:1.0)
loadingIndicator.hidesWhenStopped = true
loadingView.addSubview(loadingIndicator)
loadingIndicator.tag = 500
for subView in loadingView.subviews{
if subView.tag == 500{
print(subView.frame)
print("Bounds", subView.bounds)
}
}
loadingIndicator.startAnimating()
self.view.addSubview(loadingView)
As per style of indicator view is whiteLarge, by default its 37 so change and check.
For auto layout :
Drag UIView with 60*60 width and height.(Give width,height, horizontal center, vertical center to your super view.)
Drag Indicator view into Your UIView.(Give horizontal center, vertical center to your view.)
select Indicator view set style to white large and check its frame.(37 by default and other style 20).
set constraints like below image.

Related

How to crop view with mask, but leave cropped-out parts partially opaque instead of hidden?

I want to crop out a portion of a view. I followed this article: "How to mask one UIView using another UIView", and this is my code currently:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
/// show a green border around the image view's original frame
let backgroundView = UIView(frame: CGRect(x: 50, y: 50, width: 200, height: 300))
backgroundView.layer.borderColor = UIColor.green.cgColor
backgroundView.layer.borderWidth = 4
view.addSubview(backgroundView)
let imageView = UIImageView(frame: CGRect(x: 50, y: 50, width: 200, height: 300))
imageView.image = UIImage(named: "TestImage")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
// MARK: - Mask code
let maskView = UIView(frame: CGRect(x: 80, y: 100, width: 100, height: 100))
maskView.backgroundColor = .blue /// ensure opaque
view.addSubview(maskView)
imageView.mask = maskView
}
}
The mask is working fine:
Without mask
With mask
However, I want the parts of the image view that are cropped out to still be there, but just have a lower alpha. This is what it should look like:
I've tried changing maskView.alpha to 0.25, but that just makes the part with the mask be less opaque.
How can I make the cropped-out parts still be there, but just a bit more transparent?
Preferably I don't want to make another view, because eventually I'll be using this on a camera preview layer — an extra view might have a cost on performance.
Edit: matt's answer
I tried adding a subview with a background color with less alpha:
let maskView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 300))
maskView.backgroundColor = UIColor.blue.withAlphaComponent(0.3)
let maskView2 = UIView(frame: CGRect(x: 80, y: 100, width: 100, height: 100))
maskView2.backgroundColor = UIColor.blue.withAlphaComponent(1)
maskView2.alpha = 0
maskView.addSubview(maskView2)
imageView.mask = maskView
But this is the result:
It’s all in the transparency of the colors you paint the mask with. (The hues — what we usually think of as color — are irrelevant.) The masking depends upon the degree of transparency. Areas of the mask that are partially transparent will make the masked view be partially transparent.
So make the mask the whole size of the target view, and make the whole mask a partially transparent color, except for the central area which is an opaque color.

how to set shadow of UItextview border?

Hi i want to set shadow of UItextView like below in image.
I have tried below code but it does not give me same result rather it also make the text of UITextView as shadow.
self.tv_comments.layer.shadowRadius = 5.0
self.tv_comments.layer.borderColor = UIColor.gray.cgColor
self.tv_comments.layer.borderWidth = 1
self.tv_comments.layer.shadowColor = UIColor.gray.cgColor
self.tv_comments.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
self.tv_comments.layer.shadowOpacity = 1.0
self.tv_comments.textColor = UIColor.black
Above code results me this view which is not required
The below code works fine
self.tv_comments.layer.shadowColor = UIColor.black.cgColor;
self.tv_comments.layer.shadowOffset = CGSize(width: 1.0, height: 1.0)
self.tv_comments.layer.shadowOpacity = 1.0
self.tv_comments.layer.shadowRadius = 5.0
self.tv_comments.layer.masksToBounds = false
However, when masksToBounds = false, any sublayers that extend outside the layer's boundaries will be visible. So UITextField scroll text outside the layer.
If this is a problem for you, just add another UIView under your UITextView and set it's layer to display shadow.
Is your UITextView background color is clear color ? If yes, then set the background color of UITextView or UITextView layer background color. Because setting UITextView background color nik will set it's layer's background color to nil.So
self.tv_comments.backgroundColor = UIColor.white
//or self.tv_comments.backgroundColor = UIColor.clear
//self.tv_comments.layer.backgroundColor = UIColor.white
self.tv_comments.layer.shadowRadius = 5.0
self.tv_comments.layer.borderColor = UIColor.gray.cgColor
self.tv_comments.layer.borderWidth = 1
self.tv_comments.layer.shadowColor = UIColor.gray.cgColor
self.tv_comments.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
self.tv_comments.layer.shadowOpacity = 1.0
self.tv_comments.textColor = UIColor.black
Your code has two issues:
1) Border
Your desired output does not have a border. So do not set one.
2) View clips shadow
By default a UIView clips its content to its bounds. As a result, you can not see anything drawn outside the bounds (your shadow). Set clipsToBounds to false.
Working example:
// Test view setup
let parent = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0))
parent.backgroundColor = UIColor.white
let tv_comments = UITextView(frame: CGRect(x: 50.0, y: 50.0, width: 100.0, height: 100.0))
tv_comments.text = "Test Test Test Test Test Test "
tv_comments.backgroundColor = UIColor.white
parent.addSubview(tv_comments)
// replace your code with the code below
tv_comments.clipsToBounds = false
tv_comments.layer.shadowRadius = 5.0
tv_comments.layer.shadowColor = UIColor.gray.cgColor
tv_comments.layer.shadowOffset = CGSize(width: 2.0, height: 2.0)
tv_comments.layer.shadowOpacity = 0.8
tv_comments.textColor = UIColor.black
Result:

Unable to properly configure UIScrollView (Offset on top)

I have been fighting with this all morning and can't seem to find a solution. I have created a UIImageView, filled it with red, then added it to a UIScrollView and set the contentSize to the size of the UIImageView. If I print the contentOffset i see (0, 0) and if I print the contentSize and the UIImageView.frame.size they are the same but the red "image" always appears smaller than what the scrollView thinks the contentSize is.
If I scroll all the way to the top I see a cyan stripe about 100 pixels high above the red image and the scroll bar will not make it all the way to the top of what I believe the top of my scroll view to be. Although the top of the scroll bar does line up with the top of my red window so it would seem as though the scroll view is confused as to where it actually lives. Or more likely, I'm confused
Here is my what seems like very simple code...
imgHorizon = UIImage.init(named:"horizon")!
imgBezel = UIImage.init(named:"bezel_transparent")!
imgWings = UIImage.init(named:"wings_transparent")!
imgViewHorizon = UIImageView.init()
imgViewBezel = UIImageView.init()
imgViewWings = UIImageView.init()
svHorizon = UIScrollView.init()
super.init(coder: aDecoder)
imgViewHorizon = UIImageView(frame: CGRect(x: 0, y: 0, width: imgBezel.size.width, height: imgHorizon.size.height))
imgViewHorizon.backgroundColor = UIColor.red
imgViewBezel = UIImageView(frame: CGRect(x: 0, y: 0, width: imgBezel.size.width, height: imgBezel.size.height))
imgViewBezel.contentMode = UIViewContentMode.center
imgViewBezel.clipsToBounds = true
imgViewBezel.image = imgBezel
imgViewWings = UIImageView(frame: CGRect(x: 0, y: 0, width: imgBezel.size.width, height: imgBezel.size.height))
imgViewWings.contentMode = UIViewContentMode.center
imgViewWings.clipsToBounds = true
imgViewWings.image = imgWings
svHorizon = UIScrollView(frame: CGRect(x: 0, y: 0, width: imgBezel.size.width, height: imgBezel.size.width))
svHorizon.contentSize = CGSize(width: imgBezel.size.width, height: imgHorizon.size.height)
svHorizon.contentMode = UIViewContentMode.scaleToFill
svHorizon.bounces = false
svHorizon.backgroundColor = UIColor.cyan
svHorizon.contentOffset = CGPoint(x: 0, y: 0)
svHorizon.addSubview(imgViewHorizon)
addSubview(svHorizon)
addSubview(imgViewBezel)
addSubview(imgViewWings)
From the discussion in the comments it turns out that the Adjust Scroll View Insets option was checked in the attributes inspector of the ViewController. Unchecking it resolved the problem. Have a look at the image below. You need to uncheck the highlighted option.

Are iOS "views" and HTML divs similar?

I found that placing a view in my app and sizing it to my needs is pretty similar to divs in HTML. Should I be using them this way?
Here's an example of a place I want to use a view.
I want to fill that in, should I be using a view here? or something more semantical?
End result with a view:
If you want to set a rectangle somewhere you could definitely use an UIView(), If you´re using your storyboard make sure to set the right constraint and if you´re doing it programmatically you could do the following to get it work with all phone sizes:
Swift 3.0:
let screen = UIScreen.main.bounds
let anotherView = UIView(frame: CGRect(x: 0, y: 0, width: screen.width, height: 45))
anotherView.backgroundColor = UIColor.blue
view.addSubview(anotherView)
Swift 2.x:
let screen = UIScreen.mainScreen().bounds
let anotherView = UIView(frame: CGRect(x: 0, y: 0, width: screen.width, height: 45))
anotherView.backgroundColor = UIColor.blueColor()
view.addSubview(anotherView)

Make a UIActivityIndicator with background, position centre of the main View in Swift

So What I am aiming for, is a UIActivityIndicator to position in the centre of the screen, with a background around the spinner, and then with a full background at half transparency. This image shows what I am trying to create:
I have no issues with the spinner etc, but getting the layers all positioned correctly. They all seem to sit below the centre. Please see the image below of what is actually happening:
The code is below:
var spinningActivityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
let container: UIView = UIView()
container.frame = self.view.frame
container.center = self.view.center
container.backgroundColor = UIColor(hue: 0/360, saturation: 0/100, brightness: 0/100, alpha: 0.4)
let loadingView: UIView = UIView()
loadingView.frame = CGRectMake(0, 0, 80, 80)
loadingView.center = self.view.center
loadingView.backgroundColor = UIColor(hue: 359/360, saturation: 67/100, brightness: 71/100, alpha: 1)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 40
spinningActivityIndicator.frame = CGRectMake(0, 0, 40, 40)
spinningActivityIndicator.hidesWhenStopped = true
spinningActivityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge
spinningActivityIndicator.center = CGPointMake(loadingView.frame.size.width / 2, loadingView.frame.size.height / 2)
loadingView.addSubview(spinningActivityIndicator)
container.addSubview(loadingView)
view.addSubview(container)
spinningActivityIndicator.startAnimating()
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
Can anyone help me fix this issue? Thanks!
Instead of adding it your view you can add it directly to application window. If you use window, your container can cover entire screen. Can you try this out?
var spinningActivityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
let window = UIApplication.sharedApplication().keyWindow
let container: UIView = UIView()
container.frame = UIScreen.mainScreen().bounds
container.backgroundColor = UIColor(hue: 0/360, saturation: 0/100, brightness: 0/100, alpha: 0.4)
let loadingView: UIView = UIView()
loadingView.frame = CGRectMake(0, 0, 80, 80)
loadingView.center = container.center
loadingView.backgroundColor = UIColor(hue: 359/360, saturation: 67/100, brightness: 71/100, alpha: 1)
loadingView.clipsToBounds = true
loadingView.layer.cornerRadius = 40
spinningActivityIndicator.frame = CGRectMake(0, 0, 40, 40)
spinningActivityIndicator.hidesWhenStopped = true
spinningActivityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.WhiteLarge
spinningActivityIndicator.center = CGPointMake(loadingView.frame.size.width / 2, loadingView.frame.size.height / 2)
loadingView.addSubview(spinningActivityIndicator)
container.addSubview(loadingView)
window.addSubview(container)
spinningActivityIndicator.startAnimating()
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
And for stopping indicator and removing view use
spinningActivityIndicator.stopAnimating()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
container.removeFromSuperview()
This sort of thing is always wrong:
loadingView.center = self.view.center
The reason is that center is reckoned in terms of the view's superview's coordinate system. Since these two views are themselves to be subview and superview of one another, the two center values are in two different coordinate systems. Thus, trying to set them equal to one another can never get you the result you want.
Instead, understand the difference between frame and bounds. The frame (and center) of the subview is given in terms of the bounds of its superview. Thus, the way to center a view within its superview is to put its center at the point
CGPointMake(
CGRectGetMidX(theSuperview.bounds),
CGRectGetMidY(theSuperview.bounds))
So, decide what view you want your activity indicator to be at the center of, make your activity indicator that view's subview, and set its center in accordance with that formula, and you'll be spot on.
For others looking for a cleaner answer (Swift 4)
self.activityIndicatorView = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
self.activityIndicatorView.hidesWhenStopped = true
self.activityIndicatorView.style = .whiteLarge
self.activityIndicatorView.backgroundColor = UIColor.darkGray.withAlphaComponent(0.75)
UIApplication.shared.keyWindow?.addSubview(self.activityIndicatorView)
self.activityIndicatorView.startAnimating()

Resources