How to add a Gradient Layer to a Collection View Cell - uicollectionviewcell

I am attempting to add a gradient to a collection view cell as seen below:
So far the result I get does not cover the entire cell:
I have a
collection view cell:
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var cellView: UIView!
let diagonalGradient = DiagonalGradient()
override func layoutSubviews() {
super.layoutSubviews()
diagonalGradient.frame = cellView.bounds
}
as well as a Custom Gradient Class:
class DiagonalGradient: UIView {
let gradient = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame:frame)
setupGradient(color: UIColor.red))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupGradient(color: UIColor.red))
}
func setupGradient(color: UIColor ) {
gradient.colors = [
UIColor.clear.cgColor,
color.cgColor
]
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
gradient.frame = bounds
layer.addSublayer(gradient)
}
}
Lastly, in the story board I assign the class of the cellView to my custom gradient class. How do I fix this?

You're setting the gradient layer frame at the wrong time.
Move it to layoutSubviews():
class DiagonalGradient: UIView {
let gradient = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame:frame)
setupGradient(color: UIColor.red))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupGradient(color: UIColor.red))
}
func setupGradient(color: UIColor ) {
gradient.colors = [
UIColor.clear.cgColor,
color.cgColor
]
gradient.startPoint = CGPoint(x: 0, y: 1)
gradient.endPoint = CGPoint(x: 1, y: 0)
// don't do this here
//gradient.frame = bounds
layer.addSublayer(gradient)
}
override func layoutSubviews() {
super.layoutSubviews()
// do this here
gradient.frame = bounds
}
}

Related

SwiftUI - navigationBarBackButton

How can I change navigationBarBackButton color in SwiftUI? (by
default it's blue)
How can I change navigationBarBackButton text in SwiftUI?
This is my code (an example):
struct ExercisesList : View {
var exercises : [Exercise]
var body: some View {
NavigationView {
List(self.exercises.identified(by: \.number)) {exercise in
NavigationLink(destination: ExerciseDetailView(exercise: exercise)) {
ExerciseRow(exercisE: exercise)
}
}
.navigationBarTitle(Text("Exercises"))
}
}
}
By default, the navigationBarBackButton in "ExerciseDetailView" has a text of Exercise and color of blue.
My question is how can I manipulate these two?
Here is a solution. Hope it will help. In this way, you can use custom UIColor also.
struct ExercisesList : View {
var exercises : [Exercise]
var body: some View {
UINavigationBar.appearance().tintColor = UIColor.red
return NavigationView {
List(self.exercises.identified(by: \.number)) { exercise in
NavigationLink(destination: ExerciseDetailView(exercise: exercise)) {
ExerciseRow(exercisE: exercise)
}
}
}
.navigationBarTitle(Text("Exercises"))
}
}
Currently, No direct methods are available for that(XCode 11 beta 3).
However You can use UINavigationBar method for that,
see below code :
struct ExercisesList : View {
var exercises : [Exercise]
init() {
//navigationBarBackButton color will change//
UINavigationBar.appearance().tintColor = .purple
// you can also set backgroundColor//
UINavigationBar.appearance().backgroundColor = .white
}
var body: some View {
NavigationView {
List(self.exercises.identified(by: \.number)) {exercise in
NavigationLink(destination: ExerciseDetailView(exercise: exercise)) {
ExerciseRow(exercisE: exercise)
}
}
.navigationBarTitle(Text("Exercises"))
}
}
}

Kotlin 2-way binding custom view

I have 1 custom view that extends ConstraintLayout and contains 1 EditText and 2 TextViews
On my custom view i define this attr (and others) :
<attr name="Text" format="string" />
and i use it like :
app:Text="#={login.email}"
Inside my custom view i define :
companion object {
#JvmStatic #BindingAdapter("Text")
fun setText(nMe : View, nText: String) {
nMe.nInput.setText(nText)
}
#InverseBindingAdapter(attribute = "Text")
fun getText(nMe : View) : String {
return nMe.nInput.text.toString()
}
witch works fine in one-way binding
app:Text="#{login.email}"
But when i try to use it in 2-way binding i get erros pointing to ActivityLoginBinding.java java.lang.String callbackArg_0 = mBindingComponent.null.getText(mEmail);
What to do to get 2-way binding?
L.E : After some research i end up with this :
#InverseBindingMethods(InverseBindingMethod(type =
CustomInput::class,attribute = "bind:Text",event =
"bind:textAttrChanged",method = "bind:getText"))
class CustomEditTextBinder {
companion object {
#JvmStatic
#BindingAdapter(value = ["textAttrChanged"])
fun setListener(editText: CustomInput, listener: InverseBindingListener?) {
if (listener != null) {
editText.nInput.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
}
override fun afterTextChanged(editable: Editable) {
listener.onChange()
}
})
}
}
#JvmStatic
#InverseBindingAdapter(attribute = "Text")
fun getText(nMe: CustomInput): String {
return nMe.nInput.text.toString()
}
#JvmStatic
#BindingAdapter("Text")
fun setText(editText: CustomInput, text: String?) {
text?.let {
if (it != editText.nInput.text.toString()) {
editText.nInput.setText(it)
}
}
}
}
}
But right now i get :
Could not find event TextAttrChanged
I think all you need is event = "android:textAttrChanged".
This works for me (set text to empty String if it is 0):
object DataBindingUtil {
#BindingAdapter("emptyIfZeroText")
#JvmStatic
fun setText(editText: EditText, text: String?) {
if (text == "0" || text == "0.0") editText.setText("") else editText.setText(text)
}
#InverseBindingAdapter(attribute = "emptyIfZeroText", event = "android:textAttrChanged")
#JvmStatic
fun getText(editText: EditText) = editText.text.toString()
}

TornadoFX overriding layoutChildren on Region

Im trying to translate this JavaFX class to TornadoFX. Hover im not able to figure out how protected void layoutChildren() should be done with TornadoFX?
This is the code I have so far:
class ReversiSquare(x: Int, y: Int) : View() {
var x by property(x)
fun xProperty() = getProperty(ReversiSquare::y)
var y by property(y)
fun yProperty() = getProperty(ReversiSquare::y)
var highlight: Region by singleAssign()
var highlightTransition: FadeTransition by singleAssign()
val model = ReversiModel
override val root = region {
region {
opacity = 0.0
style = "-fx-border-width: 3; -fx-border-color: dodgerblue"
highlight = this
}
// todo not sure this works with singleAssign
highlightTransition = FadeTransition(Duration.millis(200.0), highlight).apply {
fromValue = 0.0
toValue = 1.0
}
styleProperty().bind(Bindings.`when`(model.legalMove(x, y))
.then("-fx-background-color: derive(dodgerblue, -60%)")
.otherwise("-fx-background-color: burlywood"))
val light = Light.Distant().apply {
azimuth = -135.0
elevation = 30.0
}
effect = Lighting(light)
setPrefSize(200.0,200.0)
this += highlight
addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET) {
if(model.legalMove(x ,y).get()) {
with(highlightTransition) {
rate =1.0
play()
}
}
}
addEventHandler(MouseEvent.MOUSE_EXITED_TARGET) {
with(highlightTransition) {
rate = -1.0
play()
}
}
onDoubleClick {
model.play(x, y)
highlightTransition.rate = -1.0
highlightTransition.play()
}
}
}
I'm not sure what you mean by translating to TornadoFX, but writing the layoutChildren in Kotlin would look something like this:
override fun layoutChildren() {
layoutInArea(highlight, 0.0, 0.0, width, height, baselineOffset, HPos.CENTER, VPos.CENTER);
}
EDIT: You updated the code example to a View, so I think I understand what you want now :)
First, make sure the View doesn't require parameters, because that would make it impossible to inject this view. Either pass parameters using by param() or better yet, inject a ViewModel in the scope of the View, and inject that ViewModel into your View.
Maybe you can add x and y as properties to ReversiModel?
If you need to create a custom Region you can create what would be an anonymous inner class equivalent, in Java speak:
class ReversiSquare : View() {
val model: ReversiModel by inject()
override val root = object : Region() {
// ...
override fun layoutChildren() {
layoutInArea(highlight, 0.0, 0.0, width, height, baselineOffset, HPos.CENTER, VPos.CENTER);
}
}
}
To open this View now, create a new scope and push the ReversiModel into it:
// Create the model, set x, y and other initial state in the model
val model = ReversiModel()
model.x = 42
// Create a new scope and push the ReversiModel into it
val scope = Scope(model)
// Find the ReversiSquare in the new scope
find<ReversiSquare>(scope) {
// Do something with the sequare, like openWindow() or similar
}

how to make button (in annotationView map) open different links on each new pin

*Question is : How can i make the button (when pressed) open the link witch corresponds to the chosen shop on the pin?
I have an array of shops (for pins) (i created a class for it, and a class for pinAnnotation)
here i am creating button and pin image
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "annotationReusedId"
var anView = myMapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
} else {
anView!.annotation = annotation
}
anView!.image = UIImage(named: "upcycled")
anView?.frame = CGRect(x: 0, y: 0, width: 35, height: 35)
anView!.backgroundColor = UIColor.clear
anView!.canShowCallout = true
let rightButton = UIButton(type: .infoLight)
rightButton.tag = annotation.hash
rightButton.addTarget(self, action:#selector(openLinkInstagram), for: .touchUpInside)
//anView!.animatesDrop = true
anView!.canShowCallout = true
anView!.rightCalloutAccessoryView = rightButton
return anView
}
here is func for openning link
func openLinkInstagram() {
}

What is wrong with my constraints?

I am simply trying to create a view with a contained view that is 50% as tall and high. In the following code, I set constraints to achieve that when I add the view to the super-view. I set translatesAutoResizingMaskToConstraints to false, add the constraints via anchors. I also tried to call setNeedsUpdateConstraints, and add the same constraints in updateConstraints.
But in the following Playground code, I don't see the constrained subview testView. I expect to see an blue view half the size of the orange view, but all i see is orange.
I'm not sure what I am missing here.
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
class LView: UIView {
var testView = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .orange
setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() {
addSubview(testView)
testView.backgroundColor = .blue
testView.text = "....."
//testView.setNeedsUpdateConstraints()
testView.translatesAutoresizingMaskIntoConstraints = false
testView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.5).isActive = true
testView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.5).isActive = true
testView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
testView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
}
let testView = LView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
PlaygroundPage.current.liveView = testView

Resources