UIDynamics for heatmap - ios

For a customer I need to realize a UIView which should resemble this one:
To be screen
My idea was to use UIDynamics and some sort of UIFieldBehaviour. So I created a container view containerView, added a bunch of UIViews. Those internal views are added to a UIFieldBehavior.magneticField() (using addItem).
I also create a UICollisionBehaviour adding the same UIViews
Here below the code:
let _animator = UIDynamicAnimator.init(referenceView: self.pointsReceivedMap)
let _magnet = UIFieldBehavior.magneticField()
let _collisions = UICollisionBehaviour()
_collisions.translatesReferenceBoundsIntoBoundary = true
for index in 1..<10 {
let view = UIView();
/*add some UI style to view */
_collision.addItem(view)
_magnet.addItem(view)
}
_animator.addBehaviour(_magnet)
_animator.addBehaviour(_collision)
And this is what I get:
What I get
The yellow view is the container view passed as referenceView when the animator object is inited (self.pointsReceivedMap)

Related

How do you add a CAGradientLayer to a UIBackgroundConfiguration in the new collection views with compositional layouts?

I'd like to add a gradient to a collection view cell's background in the context of the new collection view with compositional layouts. Here's an example of how a cell's background is configured from Apple's sample code Implementing Modern Collection Views in line 180 of EmojiExplorerViewController:
func configuredGridCell() -> UICollectionView.CellRegistration<UICollectionViewCell, Emoji> {
return UICollectionView.CellRegistration<UICollectionViewCell, Emoji> { (cell, indexPath, emoji) in
var content = UIListContentConfiguration.cell()
content.text = emoji.text
content.textProperties.font = .boldSystemFont(ofSize: 38)
content.textProperties.alignment = .center
content.directionalLayoutMargins = .zero
cell.contentConfiguration = content
var background = UIBackgroundConfiguration.listPlainCell()
background.cornerRadius = 8
background.strokeColor = .systemGray3
background.strokeWidth = 1.0 / cell.traitCollection.displayScale
cell.backgroundConfiguration = background
}
}
Since the new UIBackgroundConfiguration is a structure rather than a layer-backed UIView subclass, I can't just add a CAGradientLayer instance as a sublayer.
What would be a good approach to adding a gradient to a cell background configuration?
Since the new UIBackgroundConfiguration is a structure rather than a layer-backed UIView subclass, I can't just add a CAGradientLayer instance as a sublayer.
Yes, you can. The fact that UIBackgroundConfiguration is a struct is irrelevant. It has a customView property that's a view, and that will be used as the background view (behind the content view) in the cell. So set that view to something (it is nil by default) and you're all set.
Here's an example. This is a toy table view for test purposes, but the test is exactly about configuration objects, so it is readily adaptable to demonstrate the technique. It doesn't matter whether you're using a table view, a collection view, or neither, as long as you are using something that has a UIBackgroundConfiguration property. As you can see, I've made a vertical gradient from black to red as the background to my cells.
Here's the relevant code. First, I have defined a gradient-carrier view type:
class MyGradientView : UIView {
override static var layerClass: AnyClass { CAGradientLayer.self }
}
Then, I use that view as the background view when I configure the cell:
var back = UIBackgroundConfiguration.listPlainCell()
let v = MyGradientView()
(v.layer as! CAGradientLayer).colors =
[UIColor.black.cgColor, UIColor.red.cgColor]
back.customView = v
cell.backgroundConfiguration = back
Whatever else you want to achieve is merely a variant of the above. For example, you could use an image view or a solid background view and combine them with the gradient view. The point is, the customView is the background view, and whatever view you set it to will be displayed in the background of the cell.
I should also point out that there is another way to do this, namely, to use a cell subclass and implement updateConfigurationUsingState:. The advantage of this approach is that once you've given the background configuration a customView, you can just modify that customView each time the method is called. You can use this technique to respond to selection, for example, as I have demonstrated in other answers here (such as https://stackoverflow.com/a/63064099/341994).

Adding and removing UIStackViews messing up my UIScrollView

Whenever I use the pickerview to switch views from Auto Rent to Schedule Rent it works perfectly. It is when I switch from Schedule Rent to Auto Rent that this black bar appears. I have attached the hierarchy of my content view. I thought it had to do with previous constraints added, so I remove a StackView whenever one view is chosen. For example, if Auto Rent is chosen, then I remove the StackView where the Schedule View is in:
//Holds Temp Stackviews
var stackViewHolder1: UIView?
var stackViewHolder2: UIView?
override func viewDidLoad() {
super.viewDidLoad()
stackViewHolder1 = stackViewMain.arrangedSubviews[0]
stackViewHolder2 = stackViewMain.arrangedSubviews[1]
}
if txtRentType.text == "Auto Rent" {
let tempView = stackViewHolder1
let tempView1 = stackViewHolder2
tempView!.isHidden = true
stackViewMain.removeArrangedSubview(tempView!)
if(tempView1!.isHidden == true){
tempView1!.isHidden = false
stackViewMain.addArrangedSubview(tempView1!)
}
else{
let tempView = stackViewHolder1
let tempView1 = stackViewHolder2
tempView1!.isHidden = true
stackViewMain.removeArrangedSubview(tempView1!)
if(tempView!.isHidden == true){
tempView!.isHidden = false
stackViewMain.addArrangedSubview(tempView!)
}
}
I have tried deleting one view and toggling only one view has being hidden and that removes the black bar issue. There is no constraint with the stackViews and Content View.
EDIT:
The screen highlighted is the scrollView. The one after is the contentView. UIWindow goes black in the back.
My Title Bar at the top ends up in the middle somehow.
You can try to modify your stack distribution property
stack.distribution = .equalCentering
After you won't need to use this:
.removeArrangedSubview()
.addArrangedSubview()
When you hide some view, the other view take all space of your stack, you don't need to update your constraints. You can try it on interface builder to see how it works.
are you pinning your scrollview and the content view to the bottom with constraints?
If the content view is a stack view you can pin it to the bottom as well with layout constraints and play with the content distribution.
You don't need to use remove/Add arranged subviews.
when hiding a view in a stackView its automatically removed.
so i think you can just hide or show the stackViewMain.subviews[0] o stackViewMain.subviews[1]
i'm with objc maybe i do a mistake but it would be something like this :
if txtRentType.text == "Auto Rent" {
stackViewMain.arrangedSubviews[0].isHidden = true;
stackViewMain.arrangedSubviews[1].isHidden = false;
}else{
stackViewMain.arrangedSubviews[1].isHidden = true;
stackViewMain.arrangedSubviews[0].isHidden = false;
}

Swift 3 - Access nested subviews properties in code

For a pure matter of training I'm developing a weather app coding the entire UI rather than using storyboard.
I have a nested structure of views as follows:
SuperView --> UIView (with 5 subviews of type UIView).
Each of the 5 UIViews contains: 1 UIImageView, 2 UILabels
Now, when I'm calling my delegate function to retrieve the weather I'm having trouble updating those values with weather icon, weather description, day.
I tried using Tags for each of the subviews but no joy.
To give you something to look at:
This is where I retrieve my forecast data (icons, description, day):
//MARK: Forecast Wheel elements
let forecastWeatherWheel = UIView()
var forecastDays = [String]()
var forecastDescriptions = [String]()
var forecastIcons = [String]()
func setForecastWeather(forecast: ForecastWeatherData) {
forecastDays = forecast.forecastDay
forecastDescriptions = forecast.weatherDescription
forecastIcons = forecast.icon
for (index,forecastContainerView) in (forecastWeatherWheel.subviews.filter{$0 is UIView}).enumerated(){
for (index,iconImageView) in (forecastContainerView.subviews.filter{$0 is UIImageView}).enumerated(){
let iconImage = iconImageView as! UIImageView
iconImage.image = UIImage(imageLiteralResourceName: forecastIcons[index])
}
}
}
With that nested for I've been - somehow - able to access the image property of my nested view but rather than looping through the array of icons it's using always the same Icon in all the 5 subviews...
Any help is highly appreciated as I'm struggling with this since more than 12 hrs :|
The real answer is of course to use a view subclass, with accessors for the image view and each label, instead of using the subview hierarchy like this. But here's what's wrong with what you're doing right now:
for (index,forecastContainerView) in (forecastWeatherWheel.subviews.filter{$0 is UIView}).enumerated(){
The filter here is pointless; everything in subviews is a UIView. You'll get 5 passes through here.
for (index,iconImageView) in (forecastContainerView.subviews.filter{$0 is UIImageView}).enumerated(){
Your filter here is only going to return a single view - the image view, since the others aren't image views. That means this loop is only going to execute once.
let iconImage = iconImageView as! UIImageView
iconImage.image = UIImage(imageLiteralResourceName: forecastIcons[index])
Which means that index here is your inner index, which is always 0.
Either use a different name for each index variable, or write it something like this (untested, typed in browser):
for (index, forecastContainerView) in forecastWeatherWheel.subviews.enumerated() {
let imageView = forecastContainerView.subviews.first(where: { $0 is UIImageView } ) as! UIImageView
imageView.image = UIImage(imageLiteralResourceName: forecastIcons[index]
}

Card.io Override frame getter

I'm using Card.io to scan cards inside a custom UIView. The issue I have is that the camera view is taking up the view frame resulting in borders left and right. There's a frame property called cameraPreviewFrame that assigns the window and I think I need to override this property and return it's width and height.
Is this possible or is there something else I need to do?
My current code is:
var cardScanView: CardIOView = {
let csv = CardIOView()
csv.guideColor = .blue
csv.hideCardIOLogo = true
csv.allowFreelyRotatingCardGuide = false
csv.backgroundColor = .purple
return csv
}()
I'm adding this programatically inside a collection view cell and it does work in it's current form. Just the view is off.
Thanks

UIView add padding from top

I am adding custom views to a UIPageViewController which is embedded inside of a UINavigationController, because of this I need to add a padding of 20.0 from the top of each child view inside of the UIPageViewController container in order for them to sit neatly under the navigation bar.
I'm currently using UIView's layoutMargins property, setting the new edge insets on the view before it is loaded into memory, here is my helper method for creating a new view:
private func instanciateViewController(pageType: ViewType) -> UIViewController? {
var pageView : UIViewController?
switch(pageType)
{
case .SHOP :
pageView = storyboard!.instantiateViewControllerWithIdentifier("ShopFloorViewController")
break
case .TASKS :
pageView = storyboard!.instantiateViewControllerWithIdentifier("TaskListTableViewController")
break
case .PERSONNEL :
pageView = storyboard!.instantiateViewControllerWithIdentifier("PersonnelListViewController")
break
}
pageView!.view.layoutMargins = UIEdgeInsetsMake(20, 0, 0, 0)
return pageView
}
The line: pageView!.view.layoutMargins = UIEdgeInsetsMake(50, 0, 0, 0) provides the correct inset for the first screen loaded, however any interaction on the scene then causes the view to re-render with no insets.
How can I make the insets on the child views inside my UIPageViewController permanent?
Try this (I haven't tested for this specific case but makes sense). Inside the UIPageViewController override viewWillLayoutSubviews. The default implementation of this method does nothing so just write your own code inside. Inside the method, loop through the subviews and adjust the edge insets. Hopefully this should work.

Resources