Add to ContainerViews in a loop - swift

I want to use containerviews to contain three instances of a collectionview.
My outlets are:
#IBOutlet weak var topContainer: UIView!
#IBOutlet weak var middleContainer: UIView!
#IBOutlet weak var bottomContainer: UIView!
I can do it: with a disgusting solution with repeating code in viewdidload:
topContainer.translatesAutoresizingMaskIntoConstraints = false
middleContainer.translatesAutoresizingMaskIntoConstraints = false
bottomContainer.translatesAutoresizingMaskIntoConstraints = false
// add child view controller view to container
if let controller = storyboard!.instantiateViewController(withIdentifier: "collectionscroll") as? CollectionScrollViewController {
addChild(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
topContainer.addSubview(controller.view)
NSLayoutConstraint.activate([
controller.view.leadingAnchor.constraint(equalTo: topContainer.leadingAnchor),
controller.view.trailingAnchor.constraint(equalTo: topContainer.trailingAnchor),
controller.view.topAnchor.constraint(equalTo: topContainer.topAnchor),
controller.view.bottomAnchor.constraint(equalTo: topContainer.bottomAnchor)
])
controller.didMove(toParent: self)
}
if let controller = storyboard!.instantiateViewController(withIdentifier: "collectionscroll") as? CollectionScrollViewController {
addChild(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
middleContainer.addSubview(controller.view)
NSLayoutConstraint.activate([
controller.view.leadingAnchor.constraint(equalTo: middleContainer.leadingAnchor),
controller.view.trailingAnchor.constraint(equalTo: middleContainer.trailingAnchor),
controller.view.topAnchor.constraint(equalTo: middleContainer.topAnchor),
controller.view.bottomAnchor.constraint(equalTo: middleContainer.bottomAnchor)
])
controller.didMove(toParent: self)
}
if let controller = storyboard!.instantiateViewController(withIdentifier: "collectionscroll") as? CollectionScrollViewController {
addChild(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
bottomContainer.addSubview(controller.view)
NSLayoutConstraint.activate([
controller.view.leadingAnchor.constraint(equalTo: bottomContainer.leadingAnchor),
controller.view.trailingAnchor.constraint(equalTo: bottomContainer.trailingAnchor),
controller.view.topAnchor.constraint(equalTo: bottomContainer.topAnchor),
controller.view.bottomAnchor.constraint(equalTo: bottomContainer.bottomAnchor)
])
controller.didMove(toParent: self)
}
So to cut out repeating code I think of using a looop:
lazy var containers : [UIView] = [topContainer, middleContainer, bottomContainer]
for container in containers {
container.translatesAutoresizingMaskIntoConstraints = false
if let controller = storyboard!.instantiateViewController(withIdentifier: "collectionscroll") as? CollectionScrollViewController {
addChild(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
topContainer.addSubview(controller.view)
NSLayoutConstraint.activate([
controller.view.leadingAnchor.constraint(equalTo: container.leadingAnchor),
controller.view.trailingAnchor.constraint(equalTo: container.trailingAnchor),
controller.view.topAnchor.constraint(equalTo: container.topAnchor),
controller.view.bottomAnchor.constraint(equalTo: container.bottomAnchor)
])
controller.didMove(toParent: self)
}
}
Yet it doesn't work - the middle view does not populate and the last one does not scroll.
How can I populate my containers without copy pasta codez?

I see one error. When you transformed the code by adding the loop, you forgot to change one of the topContainers to container.
Change:
topContainer.addSubview(controller.view)
to:
container.addSubview(controller.view)

Related

WebAVPlayerController valueForUndefinedKey,this class is not key value coding-compliant for the key playingOnMatchPointDevice

Problem Encountered with below action :
[<WebAVPlayerController 0x600001308000> valueForUndefinedKey:]: this class is not key value coding-compliant for the key playingOnMatchPointDevice." 0x00006000025e05a0
Action Tested on iOS 15+:
hit Landscape(Full Screen) icon on the Video and the video crashes when it's full screen
I had been searching and could not find any solution on this problem. I am using WKWebView in Xcode 13.
I followed this link
iOS15 Webview using `Video` causes crashes - `WebAVPlayerController valueForUndefinedKey - playingOnMatchPointDevice `
But the problem is still not solved. Your help is greatly appreciated.
here the code
import UIKit
import WebKit
class TitlePreviewViewController: UIViewController,WKUIDelegate {
private let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 22, weight: .bold)
label.text = "Harry potter"
return label
}()
private let overviewLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 18, weight: .regular)
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.text = "This is the best movie ever to watch as a kid!"
return label
}()
lazy var webView: WKWebView = {
let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
let webView = WKWebView(frame:.zero,configuration: configuration)
if #available(iOS 10.0, *) {
configuration.mediaTypesRequiringUserActionForPlayback = []
} else {
configuration.requiresUserActionForMediaPlayback = false
}
webView.translatesAutoresizingMaskIntoConstraints = false
return webView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
view.addSubview(webView)
view.addSubview(titleLabel)
view.addSubview(overviewLabel)
configureConstraints()
webView.uiDelegate = self
webView.navigationDelegate = self
}
func configureConstraints() {
let webViewConstraints = [
webView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50),
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
webView.heightAnchor.constraint(equalToConstant: 300)
]
let titleLabelConstraints = [
titleLabel.topAnchor.constraint(equalTo: webView.bottomAnchor, constant: 20),
titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
]
let overviewLabelConstraints = [
overviewLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 15),
overviewLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
overviewLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor)
]
NSLayoutConstraint.activate(webViewConstraints)
NSLayoutConstraint.activate(titleLabelConstraints)
NSLayoutConstraint.activate(overviewLabelConstraints)
}
public func configure(with model: TitlePreviewViewModel) {
titleLabel.text = model.title
overviewLabel.text = model.titleOverview
guard let url = URL(string: "https://www.youtube.com/embed/\(Video Url )") else {
return
}
webView.load(URLRequest(url: url))
}
}
extension TitlePreviewViewController: WKNavigationDelegate{
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
}
}
Thanks

fetch core data date as string to display on label

I want my swift code to save 1 date in core data. on lbl1 i want the first core data date to be display. The label should only display the first date saved. Here is my code below which is not working. Nothing is being displayed on lbl1 when the function is called. I a link to my github profile below with this exact code.
https://github.com/redrock34/fetch
import UIKit
import CoreData
class ViewController: UIViewController {
var foodItems = [Food]()
var moc:NSManagedObjectContext!
var appDelegate = UIApplication.shared.delegate as? AppDelegate
var btn = UIButton()
var lbl1 = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
moc = appDelegate?.persistentContainer.viewContext
[btn,lbl1].forEach{
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
btn.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
btn.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1),
btn.topAnchor.constraint(equalTo: view.topAnchor),
btn.leadingAnchor.constraint(equalTo: view.leadingAnchor),
lbl1.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
lbl1.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1),
lbl1.topAnchor.constraint(equalTo: btn.bottomAnchor),
lbl1.leadingAnchor.constraint(equalTo: view.leadingAnchor),
])
btn.backgroundColor = .orange
lbl1.backgroundColor = .systemTeal
btn.addTarget(self, action: #selector(addFoodToDatabase(_:)), for: .touchUpInside)
}
#objc func addFoodToDatabase(_ sender: UIButton) {
let foodItem = Food(context: moc)
foodItem.added = NSDate() as Date
appDelegate?.saveContext()
place()
}
func place(){
let foodRequest:NSFetchRequest<Food> = Food.fetchRequest()
// 2
// 3
do {
try foodItems = moc.fetch(foodRequest)
}catch {
print("Could not load data")
}
lbl1.text = moc.name
}
}

Two-way databinding using Combine

I'm building a reusable custom stepper view.
I have it all working, and am using Combine for observing value changes. However, I'd like to improve it by using two-way databinding, but not sure if that is possible?
Here is my current code:
class Stepper: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
#Published var count = 0
private let stackView = UIStackView()
private let minusButton = BorderedRoundButton()
private let plusButton = BorderedRoundButton()
private let countLabel = UILabel()
private var cancellables = Set<AnyCancellable>()
func setup() {
translatesAutoresizingMaskIntoConstraints = false
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
minusButton.setTitleColor(.primaryTint, for: .normal)
plusButton.setTitleColor(.primaryTint, for: .normal)
minusButton.setTitle("-", for: .normal)
plusButton.setTitle("+", for: .normal)
countLabel.text = "0"
countLabel.textColor = .blackText
countLabel.font = .preferredFont(forTextStyle: .body)
countLabel.textAlignment = .center
stackView.addArrangedSubview(minusButton)
stackView.addArrangedSubview(countLabel)
stackView.addArrangedSubview(plusButton)
minusButton.tapPublisher.sink { [weak self] _ in
self?.count -= 1
}.store(in: &cancellables)
plusButton.tapPublisher.sink { [weak self] _ in
self?.count += 1
}.store(in: &cancellables)
$count.map { "\($0)" }.assign(to: \.text, on: countLabel).store(in: &cancellables)
$count.map { $0 > 0 }.assign(to: \.isEnabled, on: minusButton).store(in: &cancellables)
addSubview(stackView)
NSLayoutConstraint.activate([
minusButton.widthAnchor.constraint(equalToConstant: 38),
minusButton.heightAnchor.constraint(equalToConstant: 38),
plusButton.widthAnchor.constraint(equalToConstant: 38),
plusButton.heightAnchor.constraint(equalToConstant: 38),
countLabel.widthAnchor.constraint(equalToConstant: 44),
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
stackView.topAnchor.constraint(equalTo: topAnchor),
stackView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
}
And I am using it like this:
class PickerViewController: UIViewController {
#IBOutlet private var stepper: Stepper!
private var cancellables = Set<AnyCancellable>()
#Published var count = 0
override func viewDidLoad() {
super.viewDidLoad()
stepper.count = count
stepper.$count.sink { [weak self] value in
self?.count = value
}.store(in: &cancellables)
}
}
And that is the bit I want to improve: I now need to both set the initial value and then observe the changes. I know it's hardly a lot of code, but I am just wondering if I can do it in one line, with a sort of two-way databinding?
I thought about using SwiftUI's #Bindable propertywrapper (or a rebuilt version of it as to not have to import SwiftUI), so that PickerViewController.count is the one source, and the Stepper would automatically update it. However, then I have the problem that #Bindable is not observable itself, so these lines would no longer work:
$count.map { "\($0)" }.assign(to: \.text, on: countLabel).store(in: &cancellables)
$count.map { $0 > 0 }.assign(to: \.isEnabled, on: minusButton).store(in: &cancellables)
So my question is: can I improve the code to use two-way databinding, or should I just stick with setting the value and then observing it separately?

What is the difference between this code and just addChild()

This is the code I found in one of apples Swift tutorials: (in a UIViewController class)
private func attachChild(_ viewController: UIViewController) {
addChild(viewController)
if let subview = viewController.view {
subview.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(subview)
subview.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
subview.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
subview.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
subview.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
viewController.didMove(toParent: self)
}
I am wondering what the difference is between calling attachChild and just to addChild or in other words what does this function exactly do?

Swift, Pass the array in uiPopoverController

I want to pass the array in my popovercontroller and sort the table view controller, sort it and give it back to the main controller.
My code for the popovercontroller
protocol MyProtocol
{
func refreshPageController(sortedProperties:[Property])
}
class SortingPopoverController: UIViewController
{
#IBOutlet var propertyNameView: UIView!
#IBOutlet var addressNameView: UIView!
#IBOutlet var imgSortingPropertyName: UIImageView!
#IBOutlet var propNameSrtImage: UIImageView!
#IBOutlet var addressSrtImage: UIImageView!
#IBOutlet var imgTickPropertyName: UIImageView!
#IBOutlet var imgTickAddress: UIImageView!
var properties:[Property] = [Property]()
var utility = Utility()
var srtProperties : [Property] = []
var mDelegate: MyProtocol?
override func viewDidLoad()
{
super.viewDidLoad()
println(properties.count)
let propertyNameSorting = UITapGestureRecognizer(target: self, action: "propertyNameSorting:")
self.propertyNameView.addGestureRecognizer(propertyNameSorting)
let addressSorting = UITapGestureRecognizer(target: self, action: "addressSorting:")
self.addressNameView.addGestureRecognizer(addressSorting)
imgTickPropertyName.hidden = true
imgTickAddress.hidden = true
}
func removeViewColorSelection(uiViewRef: UIView,tickImgShow: UIImageView)
{
uiViewRef.backgroundColor = utility.uicolorFromHex(0xF0F0F0)
uiViewRef.alpha = 0.97
tickImgShow.hidden = true
}
func addSelectedColorView(uiViewRef: UIView,tickImgShow: UIImageView)
{
uiViewRef.backgroundColor = UIColor.whiteColor()
uiViewRef.alpha = 0.97
tickImgShow.hidden = false
}
func propertyNameSorting(sender:UITapGestureRecognizer)
{
println("propertyNameSorting")
if propertyNameSrt == false
{
ascSorting == false
addSelectedColorView(propertyNameView,tickImgShow: imgTickPropertyName)
propertyNameSrt = true
removeViewColorSelection(addressNameView,tickImgShow: imgTickAddress)
addressSrt = false
if ascSorting == false
{
ascSorting = true
propNameSrtImage.image = UIImage(named: "sorting-ascending-22pt")
properties.sort(sorterForbuildingAsc)
}
else
{
ascSorting = false
propNameSrtImage.image = UIImage(named: "sorting-desending-22pt")
properties.sort(sorterForbuildingDesc)
}
}
else
{
if ascSorting == false
{
ascSorting = true
propNameSrtImage.image = UIImage(named: "sorting-ascending-22pt")
properties.sort(sorterForbuildingAsc)
}
else
{
ascSorting = false
propNameSrtImage.image = UIImage(named: "sorting-desending-22pt")
properties.sort(sorterForbuildingDesc)
}
}
mDelegate?.refreshPageController(properties)
}
func addressSorting(sender:UITapGestureRecognizer)
{
println("addressSorting")
if addressSrt == false
{
ascSorting = false
addSelectedColorView(addressNameView,tickImgShow: imgTickAddress)
propertyNameSrt = false
removeViewColorSelection(propertyNameView,tickImgShow: imgTickPropertyName)
addressSrt = true
if ascSorting == false
{
ascSorting = true
addressSrtImage.image = UIImage(named: "sorting-ascending-22pt")
}
else
{
ascSorting = false
addressSrtImage.image = UIImage(named: "sorting-desending-22pt")
}
}
else
{
if ascSorting == false
{
ascSorting = true
addressSrtImage.image = UIImage(named: "sorting-ascending-22pt")
}
else
{
ascSorting = false
addressSrtImage.image = UIImage(named: "sorting-desending-22pt")
}
}
}
Code to open the controller from the main controller
var sortingPopView = SortingPopoverController(nibName: "PopView",bundle: nil )
sortingPopView.properties = properties
var sortingPopoverController = UIPopoverController(contentViewController: sortingPopView)
sortingPopoverController.popoverContentSize = CGSize(width: 250, height: 100)
sortingPopoverController.presentPopoverFromBarButtonItem(sortingBtn, permittedArrowDirections: UIPopoverArrowDirection.Up
, animated: true)
Issue is I can't call main view controller method to sort the array.
You forgot to set the delegate on you PopoverViewController.
So when you are calling this in your PopOverController:
mDelegate?.refreshPageController(properties)
Nothing happens because mDelegate is nil
When creating the PopoverViewController you have set the delegate:
var sortingPopoverController = UIPopoverController(contentViewController: sortingPopView)
sortingPopoverController.mDelegate = self