I am having a hard time updating the UI on my Swift app while observing a Firebase node. I have a red_value, a green_value, and a blue_value stored in firebase which is being fetched and used to construct a UIColor which is then used to update the background color of a button on my UI. I am successfully fetching the color values, but when i try to update the UI from within the observe closure, the color always turns out to be white and I can't figure out why.
My code can be found below for a better idea of what i am trying to accomplish when loading the view:
override func viewDidLoad() {
super.viewDidLoad()
self.charitiesReference.child(charityUsername!).observe(.value) {
snapshot in
let values = snapshot.value as! [String: Any]
let redValue = values["red_value"] as! CGFloat
let greenValue = values["green_value"] as! CGFloat
let blueValue = values["blue_value"] as! CGFloat
let charityColor = UIColor(red: redValue, green: greenValue, blue: blueValue, alpha: 1.0)
let ripplerFollowers = values["follower_ripplers"] as! [String: Bool]
if ripplerFollowers["\(self.username!)"] ?? false {
self.followOrFollowingButton.setTitle("Following", for: .normal)
//ERROR -> The line below is producing a white background on my button
self.followOrFollowingButton.backgroundColor = charityColor
} else {
self.followOrFollowingButton.setTitle("Follow", for: .normal)
self.followOrFollowingButton.backgroundColor = .clear
}
}
}
Update:
When I change self.followOrFollowingButton.backgroundColor = charityColor line to self.followOrFollowingButton.backgroundColor = UIColor(red: 0, green: 120, blue: 120, alpha: 1.0)
it works as intended, which means that something is going on with the redValue, greenValue, & blueValue variables
Thank you in advance for your help!
As it turned out, I had forgotten to divide each of the color values by 255 to put them in the 0.0 - 1.0 range. if the value is above 1.0, it is set to 1.0, which resulted in the white color.
After dividing by 255, it's working like a charm
Related
So I'm using Metal to write custom shaders for my entities in RealityKit so that I can create gradients, but the colors are rendering strangely once I apply the gradient. The color renders correctly at full RGB values, e.g. (1,0,0), (1,1,1), (1,1,0), etc. but when using decimal values the render is incorrect. Here's a screenshot showing the values. The squares in front have simple single color shaders applied and the ones in the back are colored with a UIColor on the Material in RealityKit. In all of these, I'm using UnlitMaterial and the base MeshResource.generatePlane() function.
These are the color values used as UIColors in the back, and as Metal half3s in the front.
let color1 = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
let color2 = UIColor(red: 1, green: 0.25, blue: 0.25, alpha: 1)
let color3 = UIColor(red: 1, green: 0.5, blue: 0.5, alpha: 1)
let color4 = UIColor(red: 1, green: 0.75, blue: 0.75, alpha: 1)
let color5 = UIColor(red: 1, green: 0.875, blue: 0.875, alpha: 1)
let color6 = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
Here's one of the Metal Shaders
[[visible]]
void colorShader2(realitykit::surface_parameters params) {
// Gets surface parameters
auto surface = params.surface();
half value = 0.25;
// set color to RGB Values
half3 color = half3(1,value,value);
// Set the base color to the color
surface.set_emissive_color(color);
surface.set_base_color(color);
surface.set_opacity(half(1.0));
}
Here's the Swift code to apply the shader, largely pulled from Apple's documentation. These functions are in an extension of the Entity type.
func loadCustomSurfaceShader(mySurfaceShader: String) -> CustomMaterial.SurfaceShader {
// Get the Metal Device.
guard let device = MTLCreateSystemDefaultDevice() else {
fatalError("Error creating default metal device.")
}
// Get a reference to the Metal library.
guard let library = device.makeDefaultLibrary() else {
fatalError("Error creating default Metal library")
}
// Load a surface shader function named mySurfaceShader.
let surfaceShader = CustomMaterial.SurfaceShader(named: mySurfaceShader,
in: library)
return surfaceShader
}
func replaceMaterialWithCustomMaterial(entity: Entity, surfaceShader: CustomMaterial.SurfaceShader) {
// Make sure the entity has a ModelComponent.
guard var modelComponent = entity.components[ModelComponent.self] as? ModelComponent else {
return
}
// Loop through the entity's materials and replace the existing material with
// one based on the original material.
guard let customMaterials = try? modelComponent.materials.map({ material -> CustomMaterial in
let customMaterial = try CustomMaterial(from: material, surfaceShader: surfaceShader)
return customMaterial
}) else { return}
modelComponent.materials = customMaterials
entity.components[ModelComponent.self] = modelComponent
}
func applyShader(_ shader: LookShaders) {
let shader = self.loadCustomSurfaceShader(mySurfaceShader: shader.rawValue)
self.replaceMaterialWithCustomMaterial(entity: self, surfaceShader: shader)
}
I'm pretty stumped as to why this is happening, but I'm sure there might be something simple enough I'm missing.
I am using RangeSeekSlider for displaying slider in my app. In normal case i.e. for all the LTR languages(Left to Right) it is working perfectly. But when working with languages like Arabic which require the slider to start from Right to Left i.e. opposite of the normal it is not working for me.
For your reference i have tried following piece of code but it did not worked for me,
#IBOutlet weak var sliderAmount: RangeSeekSlider!
override func viewDidLoad() {
super.viewDidLoad()
if AppUtils.checkIfArabic() == true{
sliderAmount.semanticContentAttribute = .forceRightToLeft
}else{
self.sliderAmount.semanticContentAttribute = .forceLeftToRight
}
// Do any additional setup after loading the view.
}
Following is the screenshot of normal slider which is setup using below code
//MARK:- Setup Slider View
func setupSliderView(){
sliderAmount.disableRange = true
sliderAmount.enableStep = true
sliderAmount.step = 500
sliderAmount.delegate = self
sliderAmount.minValue = 500.0
lblAmt.text = "500"
sliderAmount.maxValue = 25000.0
sliderAmount.handleImage = UIImage(named: "ic_SliderHandler")
sliderAmount.handleDiameter = 35.0
sliderAmount.lineHeight = 7.0
sliderAmount.hideLabels = true
sliderAmount.tintColor = UIColor(displayP3Red: 235/255, green: 235/255, blue: 235/255, alpha: 1.0)
sliderAmount.colorBetweenHandles = UIColor(displayP3Red: 135/255, green: 102/255, blue: 123/255, alpha: 1.0)
}
Current output:
TIA.
Solved the issue using following piece of code
if L102Language.currentAppleLanguage() == "ar" { //Arabic
UIView.animate(withDuration: 0.1) {
self.sliderAmount.transform = CGAffineTransform(scaleX: -1, y: 1);
}
}
So, the Output now is,
Problem solved, s. comment on first answer...
I am using a plugin called Carlos to load images from the cache if they have already been fetched once. However, the images I load only appear in the first of my (3) pages (I have a scrollView with pagingEnabled set to true and each of these pages shall contain the userProfilePicture). In addition to that, the image(s) appearing on the first page also has(-ve) no offsets. Combining those two informations one can assume that all (3 due to 3 pages) images are currently located on the first page with an offset of 0 and 0 for x and y respectively (see image below)(therefore I added '(s)' and '(-ve)' previously). How can I place the loaded image in the UIImageView? Does anyone have an idea how to solve this problem?
Code (Swift)
let userProfilePicture: UIImageView = {
let statusBarHeight = UIApplication.shared.statusBarFrame.height
let profileImageFrame = CGRect(x: statusBarHeight / 2, y: statusBarHeight, width: 50, height: 50)
let profileImageView = UIImageView(frame: profileImageFrame)
//set backgroundcolor (placeholder as sort of a place holder)
profileImageView.backgroundColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5)
let imageUrl = NSURL(string: "https://like.to.myImageWith50pixels.jpg")!
let cache = MemoryCacheLevel().compose(DiskCacheLevel()).compose(NetworkFetcher())
cache.get((imageUrl as NetworkFetcher.KeyType) as NetworkFetcher.KeyType)
.onSuccess { value in
let profileImage = UIImage(data: value as Data)
profileImageView.image = profileImage
}
.onFailure { error in
print("An error occurred :( \(error)")
}
profileImageView.translatesAutoresizingMaskIntoConstraints = false
//round picture
profileImageView.layer.borderWidth = 1
profileImageView.layer.masksToBounds = false
profileImageView.layer.borderColor = UIColor(red: 0.75, green: 0.75, blue: 0.75, alpha: 0.75).cgColor
profileImageView.layer.cornerRadius = profileImageView.frame.height/2
profileImageView.clipsToBounds = true
return profileImageView
}()
one more solution I made it. Make a singleton class with a NSCache parameter. using GCD fetch images and save image data for location key.
for every time you want image. use singleton class NSCache parameter to fetch and image from web or local cache which every is available first.
Is there anyway to show background gradients on the storyboard instead of firing up the simulator every-time to see what it looks like
I've created and extension
import UIKit
extension CAGradientLayer {
func stopwatchColour() -> CAGradientLayer {
let topColour = UIColor(red: (11/255.0), green: (128/255.0), blue: (105/255.0), alpha: 1)
let bottomColour = UIColor(red: (39/255.0), green: (90/255.0), blue: (78/255.0), alpha: 1)
let gradientColours: [CGColor] = [topColour.cgColor, bottomColour.cgColor]
let gradientLocations: [Float] = [0.0, 1.0]
let gradientLayer: CAGradientLayer = CAGradientLayer()
gradientLayer.colors = gradientColours
gradientLayer.locations = gradientLocations as [NSNumber]
return gradientLayer
}
}
Then from my controller
let background = CAGradientLayer().stopwatchColour()
background.frame = self.view.bounds
self.view.layer.insertSublayer(background, at: 0)
I recommend trying the code in a swift playground. Swift Playground executes your code every time you type. You can see what your background looks like and edit it however you wish. Then you can paste your code into your Xcode project without having to run the simulator.
Good luck
I have a function that creates a CGRect and I am trying to assign a random color to each of them.
I create the colors as variables with the type UIColor and then put them into an array called colors. Then, I create a random number generator and call it when defining the background color of the CGRect, but I get the error:
Cannot call value of non-function type "[UIColor}"
Why is this? Here is my code:
func addBox(location: CGRect) -> UIView {
let newBox = UIView(frame: location)
let red = UIColor(red: (242.0/255.0), green: (186.0/255.0), blue: (201.0/255.0), alpha: 1.0)
let green = UIColor(red: (186.0/255.0), green: (242.0/255.0), blue: (216.0/255.0), alpha: 1.0)
let yellow = UIColor(red: (242.0/255.0), green: (226.0/255.0), blue: (186.0/255.0), alpha: 1.0)
let blue = UIColor(red: (186.0/255.0), green: (216.0/255.0), blue: (242.0/255.0), alpha: 1.0)
let colors = [red, green, yellow, blue]
let randomNum:UInt32 = arc4random_uniform(4)
newBox.backgroundColor = UIColor(colors(randomNum))
hView.insertSubview(newBox, at: 0)
return newBox
}
If anyone could solve this that would be amazing. Any help would be immensely appreciated!! Thanks a ton in advance.
This:
newBox.backgroundColor = UIColor(colors(randomNum))
should be:
newBox.backgroundColor = colors[randomNum]
colors is an array of UIColor. You just need one element from the array.
You should also change:
let randomNum:UInt32 = arc4random_uniform(4)
to:
let randomNum = Int(arc4random_uniform(colors.count))
This way if you add more colors to the array, you don't need to adjust this line. It makes your code less error prone.