I'm new to RxSwift and had a question about the Observables.
In particular, I'm trying to have an Observable (based on viewDidAppear) emit only when a subview is visible (some boolean flag is true).
So far, both the skip- and take- operators (and their like) looked like the most promising. The only problem is, this subview can appear/disappear every time a user visits the parent view (depending on some conditions).
Based on my limited understanding, the above operators take into effect when the condition is meant once (and will complete). Is there something I'm misunderstanding or maybe there is a way to achieve my goal?
Any and all help is much appreciated!
The essence of functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration.
-- Heinrich Apfelmus
So the question is, what is the dynamic behavior of the value you are defining?
In order to make a sub-view of your VC's view appear and disappear, its isHidden property needs to be updated at the right times. So what are the triggers that cause this sub-view to appear and disappear? Turn those into Observables, combine them with some logic and bind that Observable<Bool> to the subview's rx.isHidden property.
Unfortunately, you haven't given enough information for me to help with that, but if you update your question, I will update my answer.
In the mean time, I'll leave you with this example from one of my production apps. The invariant is that the benefitKeysView should only be visible if there is more than one benefitColor.
func setupBenefitKeys(benefitKeysView: UIView, disposeBag: DisposeBag, benefitColors: Observable<BenefitColorsData>) {
benefitColors
.map { $0.benefitColors.count < 2 }
.startWith(true)
.bind(to: benefitKeysView.rx.isHidden)
.disposed(by: disposeBag)
}
struct BenefitColorsData {
let benefitColors: [UIColor]
}
Related
This question seems to have been asked several times, but I have yet to find an definitive answer. I'm totally aware of the fact that NSCell is "being gradually phased out". This is great (and long overdue!), but it doesn't help in the here and now. Suppose I want to create a subclass of NSImageView which (e.g.) clips an image with an arbitraily shaped mask. I might define it like this:
class MaskedImageView : NSImageView {
public override class var cellClass: AnyClass? {
get { MaskedImageCell.self }
set {}
}
MaskedImageCell, of course, inherits from NSImageCell. This code compiles just fine under Swift 5.x, but the cell class is never changed. Adding the statement super.cellClass = newValue to the above property setter likewise makes no difference. (In fact, this isn't how the underlying AppKit machinery establishes the cell class. If I add a print statement inside the property getter, I can see that AppKit is interrogating the cell class, as I'd expect, but it never uses it.)
Like I said, I'm aware that NSCell is on the way out. But there are surely multiple use cases where it would be useful to change the cellclass to add specifics required by a particular application. If this isn't possible NOW, then is one supposed to try and replicate the entire functionality of (eg) NSImageCell. I surely wouldn't want to do that!
All constructive comments gratefully received. And please do bear in mind that I'm not actually interested in adding weird-shaped masks to NSImageViews. 😄 What I'm after is a definitive solution to the problem of introducing one's own cell class into the mix. We were told 7 years ago that NSCell was being deprecated, but while it's still with us, we should have a mechanism for introducing custom cell classes.
In regards to UITableView & UICollectionView & their respective protocols, UITableViewDataSource & UITableViewDelegate + UICollectionViewDelegate & UICollectionViewDataSource -- are there any pros and cons to implementing one method, but not the other or a particular set of methods and not the rest of the methods or a unique combination of specific methods?
For example, if I am to implement heightForFooter but not viewForFooter - what happens or can happen? Does this negative impact performance or the scroll of the table view?
Is there a guide which shows that which if any methods are implemented, others should be combined and dually implemented alongside them?
Re: correctness
Is there a guide which shows that which if any methods are implemented, others should be combined and dually implemented alongside them?
Not from what I've seen, though sometimes the documentation mentions it. E.g. from the docs of NSTableViewDataSource:
If you’re not using Cocoa bindings to provide data to the table view, the following methods are required:
numberOfRows(in:)
tableView(_:objectValueFor:row:)
tableView(_:setObjectValue:for:row:) (cell-based tables only)
In general, if a method is optional, but required from the context, then there will be a runtime error telling you to implment it, e.g.:
Illegal NSTableView data source (Foo). Must implement numberOfRowsInTableView: and tableView:objectValueForTableColumn:row:
Either that, or stuff will silently fail (e.g. if you don't implement NSTableViewDelegate.tableView(_:viewFor:row:), no drawing will happen, and all your cells will just be blank views.
Re: performance
There shouldn't be any real performance difference with not implementing optional methods.
Each of these methods is likely called as if you had this in Swift (most of these frameworks are still implemented in Objective C):
let result: Result? = yourTarget.someMethod?(someArg)
which is really just shorthand for:
let result: Result? = yourTarget.responds(to: #selector(YourTargetsClass.someMethod))
? target.method(someArg)
: nil
That is, it's one dynamic method lookup to check if your object responds to the message, and one dynamic message send to actually invoke the method (for the case where it does respond).
There's a teeny tiny optimization that one could squeeze out here: if your method implementation is empty, then you're better off not having it at all. That will prevent a needless message send. Though it's obviously not something to worry about until you're sure it's a hotspot in a profiler.
Of course, nothing stops a library author from writing:
if yourObject.responds(to: #selector(someMessage)) {
doSomethingVeryExpensiveForNoGoodReason()
}
... but that's unlikely.
trying a few things out in Swift. I’m trying to get some things that seem muddled to me straightened out - mostly to do with how I deal with variables and referencing them in a project.
What I am trying to do is keep a variable (based on a struct) defined in ViewController accessed and updated from various other functions within an application.
So, a brief outline of the code I have is here. I actually wrote a smaller app to test my ideas out before applying them to something more complex.
I started in XCode with a Swift document based application for Mac OSX.
In ViewController.swift I have:
import Cocoa
class ViewController: NSViewController {
var myText = "Hello, some text"
#IBOutlet weak var textView1: NSTextField!
#IBAction func Button1(_ sender: Any) {
myText = "This is button 1 clicked"
myText = setText( thisText: &myText )
textView1.stringValue = myText
}
#IBAction func Button2(_ sender: Any) {
print("Button 2")
myText = "This is button 2 clicked"
textView1.stringValue = myText
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
textView1.stringValue = myText
}
override func viewDidAppear() {
let document = self.view.window?.windowController?.document as! Document
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
In SetText.swift, I have this:
import Foundation
func setText( thisText: inout String) -> String {
thisText = "Function"
return thisText
}
I liked the idea of sending variables to the Set Text function and then returning it, but thinking about it more makes me think that actually this could end up like a proverbial bowl of spaghetti with functions calling functions and who know what else. So I was thinking that something like this would probably make more sense:
import Foundation
func setText( thisText: inout String) {
let vc = ViewController()
// Read the variable from View Controller
var myTextHere = vc.myText
myTextHere = myTextHere + " More text"
// Set the variable in ViewController here
vc.myText = myTextHere
}
From my reading around on this subject, if I call ViewController(), it will create anew instance of the view (is that right, or am I misreading?). That’s already happened, so what I need is to reference the ViewController that called the function setText, or rather that owns that particular instance of code. As I’m thinking about a document based application, I’d obviously want to keep all instances of myText with each document’s ViewController.
My aim is to create something a bit more complex, using a variable based on a Struct to keep everything together. So:
myCard.image // holds a bitmap image
myCard.size // holds the size of the image
And so on. being able to access it in the form of ViewController().myCard to both read and write to is what I am thinking I need to do.
What I don’t want to do is use global variables.
Thanks.
I'm having a hard time seeing much correlation between the title of your question and the body of your question. In fact, I'm not even entirely sure that there's a question being asked. That said, I'll try to address the questions you appear to be asking (re-written how I think they're intended):
When you initialize a new view controller, does it create a new view?
Yes. There is a view property for every view controller, and it's not a shared component or a singleton or anything else like that. There is one main view for every view controller. It's almost certainly composed of dozens of other subviews, but there is one view that is the view for every view controller.
Is there a way to get metadata about the state of a view controller from outside, preferably in the form of a struct?
Absolutely. First, you'd need to define that Card struct. I'd recommend doing it in the same file as the view controller itself. You can define it outside of the view controller, or if you want stricter coupling and namespacing, you can do it inside the view controller. Just be aware that doing that latter would mean that the type name, when referenced outside the view controller, would be ViewController.Card rather than just Card.
Then you'd want to create either a computed property (var card: Card) or a method (func card() -> Card) on your view controller which builds and returns one of those based on the state. It sounds like you're already leaning toward the property approach.
Note: I would absolutely advise against having one that is a normal get/set property because then you'd have to constantly update it and modify it. The best thing to do is have a computed property which builds it on the fly. So when the property is called, it reaches into all your components to get the info you want (like image sizes, strings, etc) and then packages up and hands off the Card metadata struct. Doing it on-demand like this eliminates unnecessary complexity and consolidates the metadata logic into one place.
Some dangerous things in your code example:
I can't think of a good reason to implement viewDidAppear() but not call super.viewDidAppear(). Unless you have an enormously compelling reason to leave that out (I honestly can't think of a single one), do not do so.
I don't see any good reason for your first implementation of your setText(thisText:) method. The way you use it in your Button1(_ sender: Any) IBAction functionally does absolutely nothing. That method in general is screwy for several reasons: it's got an upper-case method name, sets the textView text by trying to assign to stringValue for some super strange reason, and does in three lines what could be done in one:
textView1.text = "This is button 1 clicked"
The second implementation of setText(thisText:) makes even less sense than the previous. The two biggest problems being 1) that you don't even use the thisText argument passed in, and 2) your method, which is called 'set text' is creating a whole new view controller every single time it gets called? That's a huge violation of "doing what it says on the tin." Methods should have a single responsibility and shouldn't do anything beyond that responsibility. I'd never in a million years look at a method called setText and think "I'll bet this initializes a view controller." Never.
I see this question has already been downvoted a bit (not by me), and I'd like to take a moment to coach you in using Stack Overflow: Ask clear, concise, specific questions about clear, concise, specific topics. As I said at the top of my answer, there doesn't appear to be a question anywhere in your post. I had to kind of make some up that I inferred from what you wrote.
Remember: coding isn't just wiggling your fingers while you think about an app. If you're doing the hard work of good engineering, you'll likely spend a ratio of about 10:1 (or more!) of staring at your screen to actually typing any code. Every time you write a line of code, you should be asking yourself, "Why am I writing this line of code? Is this necessary? Am I reinventing the wheel?)
Good luck!
I'd like to show B UIView iff A UIView is visible. I used ReactiveCocoa 2 in objective-c and tried to find a similar way to observe isHidden property of UIView in ReactiveSwift. I'm still trying to learn the framework and its usage, but couldn't come up with a good solution. I'd appreciate if anyone can give me an advice.
Here's the KVO example from the ReactiveSwift readme:
// A producer that sends the current value of `keyPath`, followed by
// subsequent changes.
//
// Terminate the KVO observation if the lifetime of `self` ends.
let producer = object.reactive.values(forKeyPath: #keyPath(key))
.take(during: self.reactive.lifetime)
So in your case you can do something like this (haven't actually tried this code, but it should convey the idea):
viewA.reactive.values(forKeyPath: #keyPath(isHidden))
.take(during: self.reactive.lifetime)
.startWithValues { hidden in viewB.isHidden = hidden }
UPDATE:
I just noticed that ReactiveCocoa includes a binding target for UIView`s isHidden property, so you can actually simplify the above code to:
viewB.reactive.isHidden <~ viewA.reactive.values(forKeyPath: #keyPath(isHidden))
Note that the take(during:) is no longer necessary when using <~, as <~ automatically ties disposal of the binding source to the lifetime of the binding target.
I'm trying to make a custom alert view that checks to see if a condition is true, and if so it builds itself and attaches itself to the window. However, if this condition is not true, I just want the view to deallocate itself, preferably in the init method. What is the best way to accomplish this?
Well it is better to follow SOLID principles in coding.
Singularity is an important thing.One function is supposed to do a single thing not many dependencies.
So Keep the view just to show the custom alert.And the check for showing must be done outside such that
//Please note this is the algorithm not the exact code in Obj-C.
if(condition==True)
{
//Display customView
}
else
{
//Do nothing
}