My Swift framework not working in ObjectiveC but works fine in Swift - objective-c

I have created a Swift framework that creating a button programmatically, and the action of the button is showing a webView and close it, my framework works fine in Swift project but in Objective C it just create button but it is not taking action and does not show my webView,
Note: I am getting the button configuration from server via JSON using SwiftyJSON
below is the my Swift Class in addition to SwiftyJSON in the framework project
import Foundation
import UIKit
import WebKit
#objc public class predictionButton : NSObject {
public override init(){
}
var buttonConfigApi = "XXXXXXXXX"
var buttonTextColor = String()
var buttonText = String()
var buttonBackgroundColor = String()
var buttonVisibility = Bool()
var indexURL = String()
var authURL = String()
var module = String()
var buttonAlignment = String()
var globalURL = String()
var webViewX = CGFloat()
var webViewY = CGFloat()
var webViewXiWidth = CGFloat()
var webViewHeight = CGFloat()
#objc public var globalView = UIView()
var parseFlag = Bool()
var buttonAlignmentValue = String()
#objc public func addButtonPredict(view: UIView, phone: String, token: String){
parseJSON()
sleep(5)
var phonTok = "{'a':\(phone),'b':\(token)}"
let utf8str = phonTok.data(using: String.Encoding.utf8)
let data = (phonTok).data(using: String.Encoding.utf8)
let base64 = data!.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
var finalURL = String()
self.globalView = view
finalURL = "\(self.indexURL)?d=\(base64)"
self.globalURL = finalURL
let button = UIButton(frame: buttonPosition(view: view, alignValue: buttonAlignment))
print((self.globalURL))
button.backgroundColor = hexStringToUIColor(hex: self.buttonBackgroundColor)
print("global color value \(buttonBackgroundColor)")
button.setTitle("\(self.buttonText)", for: .normal)
button.setTitleColor(hexStringToUIColor(hex: self.buttonTextColor), for: .normal)
let buttonFontSize = 15
let buttonTitleSize = (buttonText as NSString).size(withAttributes: [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: CGFloat(buttonFontSize + 1))])
button.frame.size.height = buttonTitleSize.height * 2
button.frame.size.width = buttonTitleSize.width * 1.5
view.addSubview(button)
button.tag = 5
button.addTarget(self, action: #selector(nest), for: .touchUpInside)
}
#objc func nest (){
let frame = webViewposition(view: self.globalView)
let webView = WKWebView(frame: frame)
webView.load(NSURLRequest(url: NSURL(string: globalURL)! as URL) as URLRequest)
webView.contentMode = .scaleAspectFit
webView.tag = 100
if let viewWithTag = self.globalView.viewWithTag(100) {
viewWithTag.removeFromSuperview()
}else{
print("No!")
self.globalView.addSubview(webView)
}
}
#objc public func parseJSON(){
print("flag")
let requestURL: NSURL = NSURL(string: buttonConfigApi)!
let urlRequest: URLRequest = URLRequest(url: requestURL as URL)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest as URLRequest) {
(data, response, error) -> Void in
if data != nil {
do
{
let readableJSON = try JSON(data: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
let Name = readableJSON["data"]["buttonText"].stringValue as String!
let buttonTextColor = readableJSON["data"]["buttonTextColorCode"].stringValue as String!
let buttonBackgroundColorCode = readableJSON["data"]["buttonBackgroundColorCode"].stringValue as String!
let visibility = readableJSON["data"]["visibility"].boolValue
let indexURL = readableJSON["data"]["indexURL"].stringValue as String!
let authURL = readableJSON["data"]["authURL"].stringValue as String!
let module = readableJSON["data"]["module"].stringValue as String!
let alignment = readableJSON["data"]["alignment"].stringValue as String!
self.buttonText = Name!
self.buttonTextColor = buttonTextColor!
self.buttonBackgroundColor = buttonBackgroundColorCode!
self.buttonAlignment = self.alignText(text: alignment!)
self.indexURL = indexURL!
self.authURL = authURL!
self.module = module!
print("\(Name!) \n \(buttonTextColor!) \n \(buttonBackgroundColorCode!) \n \(indexURL!) \n \(alignment!)")
print("done")
print("Color value:\(buttonBackgroundColorCode!)")
} catch {
print(error)
}
}
}
task.resume()
}
#objc func alignText (text: String) -> String {
var alignText = String()
let newString = text.replacingOccurrences(of: "_", with: "",
options: .literal, range: nil)
return newString.lowercased()
}
#objc func hexStringToUIColor (hex:String) -> UIColor {
var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
if (cString.hasPrefix("#")) {
cString.remove(at: cString.startIndex)
}
if ((cString.count) != 6) {
print("became gray")
return UIColor.gray
}
var rgbValue:UInt32 = 0
Scanner(string: cString).scanHexInt32(&rgbValue)
return UIColor(
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
#objc func webViewposition(view: UIView) -> CGRect {
var frame = CGRect()
if UIDevice.current.orientation == UIDeviceOrientation.landscapeLeft {
frame = CGRect(x: (view.frame.width) - (view.frame.width / 4) - 120 , y: 20 , width: view.frame.width / 3 , height: view.frame.height - 60 )
} else if UIDevice.current.orientation == UIDeviceOrientation.landscapeRight {
frame = CGRect(x: (view.frame.width) - (view.frame.width / 4) - 120, y: 20 , width: view.frame.width / 3 , height: view.frame.height - 60 )
} else if UIDevice.current.orientation == UIDeviceOrientation.portrait {
frame = CGRect(x: 0, y: 110, width: view.frame.width, height: view.frame.height - 60 )
} else if UIDevice.current.orientation == UIDeviceOrientation.portraitUpsideDown {
frame = CGRect(x: 0, y: 100, width: view.frame.width, height: view.frame.height - 60 )
}
return frame
}
#objc func buttonPosition(view: UIView, alignValue: String)-> CGRect {
var position = CGRect()
switch (alignValue){
case "lefttop":
print("button top left")
position = CGRect(x: 25, y: 50, width: 100, height: 100)
case "righttop":
print("button top right")
position = CGRect(x: view.frame.width - 150, y: 50, width: 100, height: 100)
case "letbottom":
print("button bottom left")
position = CGRect(x: 25, y: view.frame.height - 150, width: 100, height: 100)
case "rightbottom":
print("button bottom right")
position = CGRect(x: view.frame.width - 25, y: view.frame.height - 150, width: 100, height: 100)
default:
position = CGRect(x: 25, y: 50, width: 100, height: 100)
}
return position
}
}
extension UIColor {
convenience init(red: Int, green: Int, blue: Int) {
assert(red >= 0 && red <= 255, "Invalid red component")
assert(green >= 0 && green <= 255, "Invalid green component")
assert(blue >= 0 && blue <= 255, "Invalid blue component")
self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) /
255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
}
convenience init(rgb: Int) {
self.init(
red: (rgb >> 16) & 0xFF,
green: (rgb >> 8) & 0xFF,
blue: rgb & 0xFF
)
}
}
extension String {
var numberValue:NSNumber? {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter.number(from: self)
}
}
and I have created a Objective-C and add the built framework in it and its .m file code is below :
#import "ViewController.h"
#import myFrameWork;
#import <myFrameWork/myFrameWork-Swift.h>
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
predictionButton *btn = [[predictionButton alloc] init];
[btn addButtonPredictWithView:self.view phone:#"XXXXXXX"
token:#"XXXXXXXXXXXXXXXXXXXXXXXXXXX"];
}
#end
Note: this framework works fine in Swift project
Here is working swift code:
import UIKit
import myFrameWork
import WebKit
class ViewController: UIViewController {
var button = predictionButton()
override func viewDidLoad() {
super.viewDidLoad()
button.addButtonPredict(view: self.view, phone: "XXXXXXXXXX", token: "XXXXXXXXXXXXXXXXXXXXXXXXX")
}

Related

How to stop UICollectionViewDiffableDataSource to scroll the collection view while apply snapshot?

I have a UICollectionViewDiffableDataSource for vertical scrolling, with dynamic height cells.
Every time I call 'apply' on the datasource the collection view scrolls a bit and it doesn't look good.
How do I prevent it from scrolling?
private func applySnapshot(completion: #escaping () -> ()) {
let sections = presenter.getSections().filter({$0.date != nil} ).map({$0.date!})
var snapshot = Snapshot()
snapshot.appendSections(sections)
sections.forEach { (date) in
if var events = presenter.getEvents(by: date) {
if events.isEmpty {
events = [Event(emptyEventDate: date)]
}
snapshot.appendItems(events, toSection: date)
}
}
self.dataSource.apply(snapshot, animatingDifferences: false) {
completion()
}
}
private func createLayout() -> UICollectionViewLayout {
let kHeaderHeight: CGFloat = 25
let kSectionInsetTop: CGFloat = 8
let kSectionInsetLeadingTrailing: CGFloat = 15
let kSectionInsetBottom: CGFloat = 28
let kItemSpacing: CGFloat = 12
let estimatedHeight = CGFloat(400)
let layoutSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(estimatedHeight))
let item = NSCollectionLayoutItem(layoutSize: layoutSize)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: layoutSize,
subitem: item,
count: 1)
group.contentInsets = NSDirectionalEdgeInsets(
top: 0,
leading: kSectionInsetLeadingTrailing,
bottom: 0,
trailing: kSectionInsetLeadingTrailing)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(
top: kSectionInsetTop,
leading: 0,
bottom: kSectionInsetBottom,
trailing: 0)
section.interGroupSpacing = kItemSpacing
let headerSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(kHeaderHeight)
)
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
)
sectionHeader.pinToVisibleBounds = true
section.boundarySupplementaryItems = [sectionHeader]
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
I appreciate your help 🙏🏻
Edit:
I see that the view "jumps"/scrolls a bit when I insert a new item/section before the visible cells. If I insert after it doesn't happen.

programmatically create VC, Use integers from UItext field in formula and perform a calculation when UIButton is tapped

all, this code does not throw errors now but the screen is blank. I am trying to create a simple calculator. user will input numbers into 3 separate UIText fields, push a button and a UILabel would give answer. I know i do not have labels yet. i first built a container to hold text fields but then my "CalculateTapped" could not see the textFields as variables.
Question. 1 why is my screen now black?
Question. 2. why could i not get CalculateTapped to read UIText fields from my lazy var "" view earlier?
If anyone has any input i would greatly appreciate it. trying to get away from storyboards.
import UIKit
class PitchLineVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let view = UIView()
()
view.backgroundColor = .white
view.anchor(top: view.topAnchor, left: view.leftAnchor,bottom: view.bottomAnchor, right: view.rightAnchor);
configureNavigationBar()
// looks for keyboard instance
NotificationCenter.default.addObserver(self, selector: #selector(PitchLineVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(PitchLineVC.keyboarWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
// keeps screen from rotating
override var shouldAutorotate: Bool {
return false
}
// Add text fields for user input to calculate pitch
private lazy var areaTxt: UITextField! = {
let area = UITextField()
area.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -100).isActive = true
area.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -300).isActive = true
area.placeholder = "Bar Area"
area.textAlignment = .center
area.borderStyle = UITextField.BorderStyle.line
area.backgroundColor = UIColor.white
area.textColor = UIColor.black
area.keyboardType = UIKeyboardType.decimalPad
// view.addSubview(area)
return(area)
}()
private lazy var areaTop: UITextField! = {
let area = UITextField()
area.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
area.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -200).isActive = true
area.placeholder = "Top Area of Bar"
area.textAlignment = .center
area.borderStyle = UITextField.BorderStyle.line
area.backgroundColor = UIColor.white
area.textColor = UIColor.black
area.keyboardType = UIKeyboardType.decimalPad
// view.addSubview(area)
return(area)
}()
private lazy var lineLength: UITextField! = {
let line = UITextField()
line.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -100).isActive = true
line.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -300).isActive = true
// line.anchor(top: view.bottomAnchor, paddingTop: -80,width: 100, height: 35)
line.placeholder = "Line Length"
line.textAlignment = .center
line.borderStyle = UITextField.BorderStyle.line
line.backgroundColor = UIColor.white
line.textColor = UIColor.black
line.keyboardType = UIKeyboardType.decimalPad
// view.addSubview(line)
return (line)
}()
#objc func CalculateTapped(){
print("Button Tapped")
let AreaTotal = Double(areaTxt.text!)
let AreaTop = Double(areaTop.text!)
let Line = Double(lineLength.text!)
if AreaTotal != nil && AreaTop != nil && Line != nil{
let LineMove = -(((AreaTotal! / 2) - AreaTop!) / Line!)
movepitchline.text = NSString(format: "%.3f", LineMove) as String
}
}
func addCalculateButton(){
let Calculate = UIButton()
Calculate.addTarget(self, action: #selector(CalculateTapped), for: .touchUpInside)
Calculate.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -100).isActive = true
Calculate.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -400).isActive = true
Calculate.anchor(top: view.bottomAnchor, paddingTop: -80,
width: 100, height: 35)
view.addSubview(Calculate)
}
let movepitchline: UILabel = {
let label = UILabel()
label.textAlignment = .right
label.frame = CGRect(x: 10, y: 380, width: 220, height: 35)
label.text = "Move Pitch:"
label.font = UIFont.boldSystemFont(ofSize: 23)
label.textColor = .black
return label
}()
// When keyboard displays the UIView shift up
#objc func keyboardWillShow(notification: NSNotification) {
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
// if keyboard size is not available for some reason, dont do anything
return
}
// move the root view up by the distance of keyboard height
self.view.frame.origin.y = 150 - keyboardSize.height
}
#objc func keyboarWillHide(notification: NSNotification){
self.view.frame.origin.y = 0//\\100 + keyboardSize.height
}
// select outside text box to dismiss selection
#objc func handleDismiss(notification: NSNotification) {
dismiss(animated: true, completion: nil)
}
func configureNavigationBar() {
navigationController?.navigationBar.barTintColor = .appBlue
navigationController?.navigationBar.barStyle = .black
navigationItem.title = "Pass Pitch and Diameters"
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Home_2x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleDismiss))
}
}
import UIKit
class PitchLineVC: UIViewController {
lazy var pitchlogoView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFit
iv.clipsToBounds = true
iv.image = #imageLiteral(resourceName: "PITCH LINE")
return iv
}()
lazy var areaTxt: UITextField! = {
let tf = UITextField()
tf.placeholder = "Bar Area"
tf.textAlignment = .center
tf.borderStyle = UITextField.BorderStyle.line
tf.backgroundColor = UIColor.white
tf.textColor = UIColor.black
tf.keyboardType = UIKeyboardType.decimalPad
return(tf)
}()
lazy var areaTop: UITextField! = {
let tf = UITextField()
tf.placeholder = "Top Area of Bar"
tf.textAlignment = .center
tf.borderStyle = UITextField.BorderStyle.line
tf.backgroundColor = UIColor.white
tf.textColor = UIColor.black
tf.keyboardType = UIKeyboardType.decimalPad
return(tf)
}()
lazy var lineLength: UITextField! = {
let tf = UITextField()
tf.placeholder = "Line Length"
tf.textAlignment = .center
tf.borderStyle = UITextField.BorderStyle.line
tf.backgroundColor = UIColor.white
tf.textColor = UIColor.black
tf.keyboardType = UIKeyboardType.decimalPad
return (tf)
}()
lazy var movepitchline1: UILabel = {
let lb = UILabel()
lb.textAlignment = .right
lb.frame = CGRect(x: 10, y: 380, width: 220, height: 35)
lb.text = "Move Pitch: + is up, - is down"
lb.textAlignment = .center
lb.font = UIFont.boldSystemFont(ofSize: 23)
lb.textColor = .black
return (lb)
}()
lazy var movepitchline: UILabel = {
let lb = UILabel()
lb.textAlignment = .right
lb.frame = CGRect(x: 10, y: 380, width: 220, height: 35)
lb.text = ""
lb.textAlignment = .center
lb.font = UIFont.boldSystemFont(ofSize: 23)
lb.textColor = .black
return (lb)
}()
let calculateButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.init(red: 48/255, green: 173/255, blue: 99/255, alpha: 1)
button.layer.cornerRadius = 25.0
button.tintColor = UIColor.white
button.layer.shadowColor = UIColor.darkGray.cgColor
button.layer.shadowRadius = 6
button.layer.shadowOpacity = 0.7
button.layer.shadowOffset = CGSize(width: 0, height: 0)
button.setTitle("Calculate", for: .normal)
button.titleLabel?.font = UIFont(name: "Copperplate", size: 22)
button.addTarget(self, action: #selector(handleCalculate), for: .touchUpInside)
return button
}()
#objc func handleDismiss(notification: NSNotification) {
dismiss(animated: true, completion: nil)
}
//sets trigger for keyboard dismiss
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
super.touchesBegan(touches, with: event)
}
// Mark: Init
override func viewDidLoad() {
super.viewDidLoad()
configureNavigationBar()
configureViewComponent()
// looks for keyboard instance
NotificationCenter.default.addObserver(self, selector: #selector(PitchLineVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(PitchLineVC.keyboarWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
// keeps screen from rotating
override var shouldAutorotate: Bool {
return false
}
// Mark: Selectors
#objc func handleCalculate()
{
print("calculate")
guard let AreaTotal = Double(areaTxt.text!) else {return}
guard let AreaTop = Double(areaTop.text!) else {return}
guard let Line = Double(lineLength.text!) else {return}
if AreaTotal != 0 && AreaTop != 0 && Line != 0{
let LineMove = -(((AreaTotal / 2) - AreaTop) / Line)
print(LineMove)
movepitchline.text = NSString(format: "%.3f", LineMove) as String
} else{
print("Error in fields, please check entry")
}
}
func configureViewComponent(){
view.backgroundColor = .white
view.addSubview(pitchlogoView)
pitchlogoView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 120, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 250, height: 110)
view.addSubview(areaTxt)
areaTxt.anchor(top: pitchlogoView.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 15, paddingLeft: 80, paddingBottom: 0, paddingRight: 80, width: 0, height: 30)
view.addSubview(areaTop)
areaTop.anchor(top: areaTxt.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 20, paddingLeft: 80, paddingBottom: 0, paddingRight: 80, width: 0, height: 30)
view.addSubview(lineLength)
lineLength.anchor(top: areaTop.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 20, paddingLeft: 80, paddingBottom: 0, paddingRight: 80, width: 0, height: 30)
view.addSubview(calculateButton)
calculateButton.anchor(top: lineLength.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 30, paddingLeft: 32, paddingBottom: 0, paddingRight: 32, width: 0, height: 50)
view.addSubview(movepitchline1)
movepitchline1.anchor(top: calculateButton.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 10, paddingLeft: 32, paddingBottom: 0, paddingRight: 32, width: 0, height: 40)
view.addSubview(movepitchline)
movepitchline.anchor(top: movepitchline1.bottomAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 10, paddingLeft: 32, paddingBottom: 0, paddingRight: 32, width: 0, height: 40)
}
// When keyboard displays the UIView shift up
#objc func keyboardWillShow(notification: NSNotification) {
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
// if keyboard size is not available for some reason, dont do anything
return
}
// move the root view up by the distance of keyboard height
self.view.frame.origin.y = 165 - keyboardSize.height
}
#objc func keyboarWillHide(notification: NSNotification){
self.view.frame.origin.y = 0
}
// select outside text box to dismiss selection
func configureNavigationBar() {
navigationController?.navigationBar.barTintColor = .appBlue
navigationController?.navigationBar.barStyle = .black
navigationItem.title = "Pass Pitch and Diameters"
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Home_2x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleDismiss))
}
}

How to overlay a view on the status bar in ios13

fileprivate lazy var netTipWindow:UIWindow = {
let window = UIWindow(frame: CGRect(x: 0, y: topMargins, width: UIScreenW, height: realheight))
window.backgroundColor = .clear
window.windowLevel = .alert
window.isHidden = false
window.rootViewController = UIViewController()
window.rootViewController?.view.addSubview(self)
frame = CGRect(x: 0, y: -(topMargins + realheight), width: UIScreenW, height: realheight)
return window
}()
My Code works well in ios 12, but it works like the picture.
Also, I tried this code, it makes no difference
fileprivate lazy var netTipWindow:UIWindow = {
let window = UIWindow(frame: CGRect(x: 0, y: topMargins, width: UIScreenW, height: realheight))
window.backgroundColor = .clear
window.windowLevel = .alert
window.isHidden = false
if #available(iOS 13, *) {
window.addSubview(self)
} else {
window.rootViewController = UIViewController()
window.rootViewController?.view.addSubview(self)
}
frame = CGRect(x: 0, y: -(topMargins + realheight), width: UIScreenW, height: realheight)
return window
}()
iOS 13 status bar
this code....
- (BOOL)prefersStatusBarHidden {
if (#available(iOS 13.0, *)) {
// safearea iPhone x, xr ..
if ([[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top > 20.0) {
return NO;
}
else {
return YES;
}
}
else {
return NO;
}
}

SwiftUI ScrollView: How to modify .content.offset aka Paging?

Problem
How can I modify the scroll target of a scrollView? I am looking for kind of a replacement for the "classic" scrollView delegate method
override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
...where we can modfify the targeted scrollView.contentOffset via targetContentOffset.pointee for instance to create a custom paging behaviour.
Or in other words: I do want to create a paging effect in a (horizontal) scrollView.
What I have tried ie. is something like this:
ScrollView(.horizontal, showsIndicators: true, content: {
HStack(alignment: VerticalAlignment.top, spacing: 0, content: {
card(title: "1")
card(title: "2")
card(title: "3")
card(title: "4")
})
})
// 3.
.content.offset(x: self.dragState.isDragging == true ? self.originalOffset : self.modifiedOffset, y: 0)
// 4.
.animation(self.dragState.isDragging == true ? nil : Animation.spring())
// 5.
.gesture(horizontalDragGest)
Attempt
This is what I tried (besides a custom scrollView approach):
A scrollView has a content area larger then screen space to enable scrolling at all.
I created a DragGesture() to detect if there is a drag going on. In the .onChanged and .onEnded closures I modified my #State values to create a desired scrollTarget.
Conditionally fed in both the original unchanged and the new modified values into the .content.offset(x: y:) modifier - depending on the dragState as a replacement for missing scrollDelegate methods.
Added animation acting conditionally only when drag has ended.
Attached the gesture to the scrollView.
Long story short. It doesn't work.
I hope I got across what my problem is.
Any solutions out there? Looking forward to any input. Thanks!
I have managed to achieve a paging behaviour with a #Binding index. The solution might look dirty, I'll explain my workarounds.
The first thing I got wrong, was to get alignment to .leading instead of the default .center, otherwise the offset works unusual. Then I combined the binding and a local offset state. This kinda goes against the "Single source of truth" principle, but otherwise I had no idea how to handle external index changes and modify my offset.
So, my code is the following
struct SwiftUIPagerView<Content: View & Identifiable>: View {
#Binding var index: Int
#State private var offset: CGFloat = 0
#State private var isGestureActive: Bool = false
// 1
var pages: [Content]
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .center, spacing: 0) {
ForEach(self.pages) { page in
page
.frame(width: geometry.size.width, height: nil)
}
}
}
// 2
.content.offset(x: self.isGestureActive ? self.offset : -geometry.size.width * CGFloat(self.index))
// 3
.frame(width: geometry.size.width, height: nil, alignment: .leading)
.gesture(DragGesture().onChanged({ value in
// 4
self.isGestureActive = true
// 5
self.offset = value.translation.width + -geometry.size.width * CGFloat(self.index)
}).onEnded({ value in
if -value.predictedEndTranslation.width > geometry.size.width / 2, self.index < self.pages.endIndex - 1 {
self.index += 1
}
if value.predictedEndTranslation.width > geometry.size.width / 2, self.index > 0 {
self.index -= 1
}
// 6
withAnimation { self.offset = -geometry.size.width * CGFloat(self.index) }
// 7
DispatchQueue.main.async { self.isGestureActive = false }
}))
}
}
}
you may just wrap your content, I used it for "Tutorial Views".
this a trick to switch between external and internal state changes
.leading is mandatory if you don't want to translate all offsets to center.
set the state to local state change
calculate the full offset from the gesture delta (*-1) plus the previous index state
at the end set the final index based on the gesture predicted end, while rounding the offset up or down
reset the state to handle external changes to index
I have tested it in the following context
struct WrapperView: View {
#State var index: Int = 0
var body: some View {
VStack {
SwiftUIPagerView(index: $index, pages: (0..<4).map { index in TODOView(extraInfo: "\(index + 1)") })
Picker(selection: self.$index.animation(.easeInOut), label: Text("")) {
ForEach(0..<4) { page in Text("\(page + 1)").tag(page) }
}
.pickerStyle(SegmentedPickerStyle())
.padding()
}
}
}
where TODOView is my custom view that indicates a view to implement.
I hope I get the question right, if not please specify which part should I focus on. Also I welcome any suggestions to remove the isGestureActive state.
#gujci your solution is perfect, for more general usage, make it accept Models and view builder as in (note the I pass the geometry size in the builder) :
struct SwiftUIPagerView<TModel: Identifiable ,TView: View >: View {
#Binding var index: Int
#State private var offset: CGFloat = 0
#State private var isGestureActive: Bool = false
// 1
var pages: [TModel]
var builder : (CGSize, TModel) -> TView
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .center, spacing: 0) {
ForEach(self.pages) { page in
self.builder(geometry.size, page)
}
}
}
// 2
.content.offset(x: self.isGestureActive ? self.offset : -geometry.size.width * CGFloat(self.index))
// 3
.frame(width: geometry.size.width, height: nil, alignment: .leading)
.gesture(DragGesture().onChanged({ value in
// 4
self.isGestureActive = true
// 5
self.offset = value.translation.width + -geometry.size.width * CGFloat(self.index)
}).onEnded({ value in
if -value.predictedEndTranslation.width > geometry.size.width / 2, self.index < self.pages.endIndex - 1 {
self.index += 1
}
if value.predictedEndTranslation.width > geometry.size.width / 2, self.index > 0 {
self.index -= 1
}
// 6
withAnimation { self.offset = -geometry.size.width * CGFloat(self.index) }
// 7
DispatchQueue.main.async { self.isGestureActive = false }
}))
}
}
}
and can be used as :
struct WrapperView: View {
#State var index: Int = 0
#State var items : [(color:Color,name:String)] = [
(.red,"Red"),
(.green,"Green"),
(.yellow,"Yellow"),
(.blue,"Blue")
]
var body: some View {
VStack(spacing: 0) {
SwiftUIPagerView(index: $index, pages: self.items.identify { $0.name }) { size, item in
TODOView(extraInfo: item.model.name)
.frame(width: size.width, height: size.height)
.background(item.model.color)
}
Picker(selection: self.$index.animation(.easeInOut), label: Text("")) {
ForEach(0..<4) { page in Text("\(page + 1)").tag(page) }
}
.pickerStyle(SegmentedPickerStyle())
}.edgesIgnoringSafeArea(.all)
}
}
with the help of some utilities :
struct MakeIdentifiable<TModel,TID:Hashable> : Identifiable {
var id : TID {
return idetifier(model)
}
let model : TModel
let idetifier : (TModel) -> TID
}
extension Array {
func identify<TID: Hashable>(by: #escaping (Element)->TID) -> [MakeIdentifiable<Element, TID>]
{
return self.map { MakeIdentifiable.init(model: $0, idetifier: by) }
}
}
#gujci, thank you for interesting example. I've played with it and removed the isGestureActive state. Full example may be found in my gist.
struct SwiftUIPagerView<Content: View & Identifiable>: View {
#State private var index: Int = 0
#State private var offset: CGFloat = 0
var pages: [Content]
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .center, spacing: 0) {
ForEach(self.pages) { page in
page
.frame(width: geometry.size.width, height: nil)
}
}
}
.content.offset(x: self.offset)
.frame(width: geometry.size.width, height: nil, alignment: .leading)
.gesture(DragGesture()
.onChanged({ value in
self.offset = value.translation.width - geometry.size.width * CGFloat(self.index)
})
.onEnded({ value in
if abs(value.predictedEndTranslation.width) >= geometry.size.width / 2 {
var nextIndex: Int = (value.predictedEndTranslation.width < 0) ? 1 : -1
nextIndex += self.index
self.index = nextIndex.keepIndexInRange(min: 0, max: self.pages.endIndex - 1)
}
withAnimation { self.offset = -geometry.size.width * CGFloat(self.index) }
})
)
}
}
}
As far as I know scrolls in swiftUI doesn't support anything potentially useful such as scrollViewDidScroll or scrollViewWillEndDragging yet. I suggest using either classic UIKit views for making very custom behavior and cool SwiftUI views for anything that is easier. I've tried that a lot and it actually works! Have a look at this guide. Hope that helps
Alternative solution would be to integrate UIKit into SwiftUI using UIViewRepresentative which links UIKit components with SwiftUI. For additional leads and resources, see how Apple suggests you interface with UIKit: Interfacing with UIKit. They have a good example that shows to page between images and track selection index.
Edit: Until they (Apple) implement some sort of content offset that effects the scroll instead of the entire view, this is their suggested solution since they knew the initial release of SwiftUI wouldn't encompass all functionality of UIKit.
Details
Xcode 14
Swift 5.6.1
Requirements
I do not want to use integration with UIKit (clean SwiftUI ONLY)
I do not want to scroll to the any "ID", I want to scroll to the point
Solution
import SwiftUI
#available(iOS 14.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
struct ExtendedScrollView<Content>: View where Content: View {
private let contentProvider: _AligningContentProvider<Content>
// Main Idea from: https://github.com/edudnyk/SolidScroll/blob/main/Sources/SolidScroll/ScrollView.swift
private var config: _ScrollViewConfig
init(config: _ScrollViewConfig = _ScrollViewConfig(),
#ViewBuilder content: () -> Content) {
contentProvider = _AligningContentProvider(content: content(), horizontal: .center, vertical: .center)
self.config = config
}
init(_ axes: Axis.Set = .vertical,
showsIndicators: Bool = true,
#ViewBuilder content: () -> Content) {
var config = _ScrollViewConfig()
config.showsHorizontalIndicator = axes.contains(.horizontal) && showsIndicators
config.showsVerticalIndicator = axes.contains(.vertical) && showsIndicators
self.init(config: config, content: content)
}
init(config: () -> _ScrollViewConfig,
#ViewBuilder content: () -> Content) {
self.init(config: config(), content: content)
}
var body: some View {
_ScrollView(contentProvider: contentProvider, config: config)
}
}
extension _ContainedScrollViewKey: PreferenceKey {}
// MARK: Track ScrollView Scrolling
struct TrackableExtendedScrollView: ViewModifier {
let onChange: (_ScrollViewProxy?) -> Void
func body(content: Content) -> some View {
content
.onPreferenceChange(_ContainedScrollViewKey.self, perform: onChange)
}
}
extension View {
func onScrollChange(perform: #escaping (_ScrollViewProxy?) -> Void) -> some View {
modifier(TrackableExtendedScrollView(onChange: perform))
}
}
Usage Sample
private var gridItemLayout = (0..<40).map { _ in
GridItem(.fixed(50), spacing: 0, alignment: .leading)
}
// ....
ExtendedScrollView() {
LazyHGrid(rows: gridItemLayout) {
ForEach((0..<numberOfRows*numberOfColumns), id: \.self) { index in
let color = (index/numberOfRows)%2 == 0 ? Color(0x94D2BD) : Color(0xE9D8A6)
Text("\(index)")
.frame(width: 50)
.frame(maxHeight: .infinity)
}
}
}
.onScrollChange { proxy in
// let offset = proxy?.contentOffset.y
}
Full Sample
Implementation details
First column and first row are always on the screen
There are 3 "CollectionView":
first row "CollectionView"
first column "CollectionView"
main content "CollectionView"
All "CollectionView" are synced (if you scroll one "CollectionView", another will also be scrolled)
Do not forget to paste The Solution code here
import SwiftUI
import Combine
struct ContentView: View {
private let columWidth: CGFloat = 50
private var gridItemLayout0 = [GridItem(.fixed(50), spacing: 0, alignment: .leading)]
private var gridItemLayout1 = [GridItem(.fixed(50), spacing: 0, alignment: .leading)]
private var gridItemLayout = (0..<40).map { _ in
GridItem(.fixed(50), spacing: 0, alignment: .leading)
}
#State var text: String = "scrolling not detected"
#State private var scrollViewProxy1: _ScrollViewProxy?
#State private var tableContentScrollViewProxy: _ScrollViewProxy?
#State private var tableHeaderScrollViewProxy: _ScrollViewProxy?
private let numberOfColumns = 50
private let numberOfRows = 40
let headerColor = Color(0xEE9B00)
let firstColumnColor = Color(0x0A9396)
let headerTextColor = Color(.white)
let horizontalSpacing: CGFloat = 6
let verticalSpacing: CGFloat = 0
let firstColumnWidth: CGFloat = 100
let columnWidth: CGFloat = 60
var body: some View {
VStack(spacing: 0) {
Text("First column and row are sticked to the content")
.foregroundColor(.gray)
Text(text)
HStack {
Rectangle()
.frame(width: firstColumnWidth-2)
.foregroundColor(.clear)
buildFirstCollectionViewRow()
}
.frame(height: 50)
HStack(alignment: .firstTextBaseline, spacing: horizontalSpacing) {
buildFirstCollectionViewColumn()
buildCollectionViewContent()
}
}
}
#ViewBuilder
private func buildFirstCollectionViewRow() -> some View {
ExtendedScrollView() {
LazyHGrid(rows: gridItemLayout1, spacing: horizontalSpacing) {
ForEach((0..<numberOfColumns), id: \.self) {
let color = $0%2 == 0 ? Color(0x005F73) : Color(0xCA6702)
Text("Value\($0)")
.frame(width: columnWidth)
.frame(maxHeight: .infinity)
.foregroundColor(headerTextColor)
.background(color)
.font(.system(size: 16, weight: .semibold))
}
}
}
.onScrollChange { proxy in
if tableHeaderScrollViewProxy != proxy { tableHeaderScrollViewProxy = proxy }
guard proxy?.isScrolling ?? false else { return }
if tableHeaderScrollViewProxy?.contentOffset.x != tableContentScrollViewProxy?.contentOffset.x,
let offset = proxy?.contentOffset.x {
tableContentScrollViewProxy?.contentOffset.x = offset
}
text = "scrolling: header"
}
}
}
// MARK: Collection View Elements
extension ContentView {
#ViewBuilder
private func buildFirstCollectionViewColumn() -> some View {
ExtendedScrollView() {
LazyHGrid(rows: gridItemLayout, spacing: horizontalSpacing) {
ForEach((0..<numberOfRows), id: \.self) {
Text("multi line text \($0)")
.foregroundColor(.white)
.lineLimit(2)
.frame(width: firstColumnWidth)
.font(.system(size: 16, weight: .semibold))
.frame(maxHeight: .infinity)
.background(firstColumnColor)
.border(.white)
}
}
}
.frame(width: firstColumnWidth)
.onScrollChange { proxy in
if scrollViewProxy1 != proxy { scrollViewProxy1 = proxy }
guard proxy?.isScrolling ?? false else { return }
if scrollViewProxy1?.contentOffset.y != tableContentScrollViewProxy?.contentOffset.y,
let offset = proxy?.contentOffset.y {
tableContentScrollViewProxy?.contentOffset.y = offset
}
text = "scrolling: 1st column"
}
}
#ViewBuilder
private func buildCollectionViewContent() -> some View {
ExtendedScrollView() {
LazyHGrid(rows: gridItemLayout, spacing: horizontalSpacing) {
ForEach((0..<numberOfRows*numberOfColumns), id: \.self) { index in
let color = (index/numberOfRows)%2 == 0 ? Color(0x94D2BD) : Color(0xE9D8A6)
Text("\(index)")
.frame(width: columnWidth)
.frame(maxHeight: .infinity)
.background(color)
.border(.white)
}
}
}
.onScrollChange { proxy in
if tableContentScrollViewProxy != proxy { tableContentScrollViewProxy = proxy }
guard proxy?.isScrolling ?? false else { return }
if scrollViewProxy1?.contentOffset.y != tableContentScrollViewProxy?.contentOffset.y,
let offset = proxy?.contentOffset.y {
self.scrollViewProxy1?.contentOffset.y = offset
}
if tableHeaderScrollViewProxy?.contentOffset.x != tableContentScrollViewProxy?.contentOffset.x,
let offset = proxy?.contentOffset.x {
self.tableHeaderScrollViewProxy?.contentOffset.x = offset
}
text = "scrolling: content"
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension Color {
init(_ hex: UInt, alpha: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xFF) / 255,
green: Double((hex >> 8) & 0xFF) / 255,
blue: Double(hex & 0xFF) / 255,
opacity: alpha
)
}
}
Full Sample Demo

same video in every five cells

I created a UICollectionView named UserProfileController.
I created a UICollectionViewCell named UserProfilePhotoCell.
I created avplayer in UserProfilePhotoCell.
When you click on Avplayer the video starts.
When I click on the 1st AVPlayer on the cell,
The first, fifth, ninth cells play AVplayer.
How can I fix?
Issue Video = https://ibb.co/bMwOAe
import UIKit
import Alamofire
import AVKit
import AVFoundation
class UserProfileController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
let screen = ScreenProp()
var posts = [ProfileVideo]()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Profil Detay"
collectionView?.backgroundColor = .white
collectionView?.register(UserProfileHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerID")
collectionView?.delegate = self
collectionView?.dataSource = self
collectionView?.register(UserProfilePhotoCell.self, forCellWithReuseIdentifier: "cellId")
veriCek()
}
var user: User?
func veriCek(){
//http post başlangıç
// LoadingOverlay.shared.showOverlay(view: UIApplication.shared.keyWindow!)
//
// guard let confirm = phoneInput.text else {return}
let defaults = UserDefaults.standard
let userID = defaults.integer(forKey: "userID")
let acToken = defaults.string(forKey: "acToken")
let parameters: Parameters = [
"userID": userID,
"acToken": acToken ?? ""
]
Alamofire.request("********************************", method: .post, parameters: parameters).validate().responseJSON { response in
switch response.result {
case .success:
print(response.data!)
if let result = response.result.value {
let json = result as! NSDictionary
print(json)
let dataArray = json["sonuclar"] as! NSArray;
print("Data items count: \(dataArray.count)")
for item in dataArray { // loop through data items
let obj = item as! NSDictionary
// let profileVideo = ProfileVideo(dictionary: obj)
// print(profileVideo.artistName)
let artistName = obj.value(forKey: "artistName") as! String
let commentlikes = obj.value(forKey: "commentlikes") as! String
let likeStatus = obj.value(forKey: "likeStatus") as! String
let name = obj.value(forKey: "name") as! String
let profilePics = obj.value(forKey: "profilePics") as! String
let time = obj.value(forKey: "time") as! String
let userID = obj.value(forKey: "userID") as! String
let username = obj.value(forKey: "username") as! String
let videoContent = obj.value(forKey: "videoContent") as! String
let videoID = obj.value(forKey: "videoID") as! String
let videoLikes = obj.value(forKey: "videoLikes") as! String
let videoName = obj.value(forKey: "videoName") as! String
let videoPath = obj.value(forKey: "videoPath") as! String
let view = obj.value(forKey: "view") as! String
let post = ProfileVideo(artistName: artistName, commentlikes: commentlikes, likeStatus: likeStatus, name: name, profilePics: profilePics, time: time, userID: userID, username: username, videoContent: videoContent, videoID: videoID, videoLikes: videoLikes, videoName: videoName, videoPath: videoPath, view: view, status: 0)
self.posts.append(post)
}
self.user = User(dictionary: json as! [String : Any])
self.navigationItem.title = self.user?.username
self.collectionView?.reloadData()
}
case .failure(let error):
print(error)
}
}
// http post bitiş
}
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "headerID", for: indexPath) as! UserProfileHeader
header.user = self.user
return header
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! UserProfilePhotoCell
var dizi = posts[indexPath.item]
print("\(indexPath.item) = \(dizi.status)")
cell.post = dizi
return cell
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
referenceSizeForHeaderInSection section: Int) -> CGSize{
return CGSize(width: view.frame.width ,height: CGFloat(screen.heightHesap(x: 246)))
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = view.frame.width
return CGSize(width: width, height: 500)
}
}
struct User {
let name: String
let username: String
let profileImageUrl: String
let follow: String
let follower: String
let videoCount: String
init(dictionary: [String: Any]) {
self.username = dictionary["username"] as! String
self.profileImageUrl = dictionary["profilePics"] as! String
self.name = dictionary["name"] as! String
self.follow = dictionary["follow"] as! String
self.follower = dictionary["follower"] as! String
self.videoCount = dictionary["videoCount"] as! String
}
}
struct ProfileVideo {
let artistName: String
let commentlikes: String
var likeStatus: String
let name: String
let profilePics: String
let time: String
let userID: String
let username: String
let videoContent: String
let videoID: String
let videoLikes: String
let videoName: String
let videoPath: String
let view: String
var status: Int
}
UserProfilePhotoCell
import UIKit
import AVKit
import AVFoundation
import Alamofire
class UserProfilePhotoCell: UICollectionViewCell {
var screen = ScreenProp()
var playerLayer = AVPlayerLayer()
var player = AVPlayer()
var moviePlayerController = AVPlayerViewController()
let defaults = UserDefaults.standard
var status = 0
let videoHeader: UIImageView = {
let imageView = UIImageView(image: #imageLiteral(resourceName: "videoHeader1"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .red
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
return imageView
}()
let videoFooter: UIImageView = {
let imageView = UIImageView(image:#imageLiteral(resourceName: "videoFooter"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .none
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
return imageView
}()
let clapsDeactive: UIImageView = {
let imageView = UIImageView(image: #imageLiteral(resourceName: "applause"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .none
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
return imageView
}()
let clapsActive: UIImageView = {
let imageView = UIImageView(image: #imageLiteral(resourceName: "applause_on"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .none
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
return imageView
}()
let comment: UIImageView = {
let imageView = UIImageView(image: #imageLiteral(resourceName: "comments"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .none
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
return imageView
}()
let videoName: UILabel = {
let textView = UILabel()
textView.text = "Video Adı";
textView.textColor = CustomColor.white;
textView.font = CustomFont.textStyle16;
textView.textAlignment = .center
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let userName: UILabel = {
let textView = UILabel()
textView.text = "username";
textView.textColor = CustomColor.bblack;
textView.font = CustomFont.textStyle16;
textView.textAlignment = .center
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let location: UILabel = {
let textView = UILabel()
textView.text = "location";
textView.textColor = CustomColor.battleshipGreyTwo;
textView.font = CustomFont.textStyle16;
textView.textAlignment = .center
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let profilePhoto: UIImageView = {
var screen1 = ScreenProp()
let imageView = UIImageView(image: #imageLiteral(resourceName: "profile_photo"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .white
imageView.contentMode = .scaleToFill
imageView.layer.cornerRadius = CGFloat(screen1.heightHesap(x: 37)/2)
imageView.clipsToBounds = true
return imageView
}()
let videoCover: UIImageView = {
var screen1 = ScreenProp()
let imageView = UIImageView(image: #imageLiteral(resourceName: "video-sample"))
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.backgroundColor = .white
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
return imageView
}()
let clapsText: UILabel = {
let textView = UILabel()
textView.text = "0";
textView.textColor = CustomColor.bblack;
textView.font = CustomFont.textStyle3;
textView.textAlignment = .center
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let commentsText: UILabel = {
let textView = UILabel()
textView.text = "0";
textView.textColor = CustomColor.bblack;
textView.font = CustomFont.textStyle3;
textView.textAlignment = .center
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
let content: UITextView = {
let textView = UITextView()
textView.text = "Donec pretium est sit amet ipsum fringilla feugiat. Aliquam erat volutpat. Maecenas scelerisque,";
textView.textColor = CustomColor.cloudyBlue;
textView.font = CustomFont.textStyle3;
textView.textAlignment = .left
textView.isEditable = false
textView.translatesAutoresizingMaskIntoConstraints = false
textView.isScrollEnabled = false
return textView
}()
var set = 0
var ratePlayer = 0
var post: ProfileVideo? {
didSet{
print(post?.status)
if(set == 0){
// kur(str: (post?.videoPath)!)
}
videoName.text = "\(post?.artistName ?? "") - \(post?.videoName ?? "")"
userName.text = post?.username
clapsText.text = post?.videoLikes as? String
commentsText.text = post?.commentlikes
content.text = post?.videoContent
var url = URL(string: post?.profilePics ?? "")
ImageLoader.image(for: url!) { image in
self.profilePhoto.image = image
}
if(post?.likeStatus == "0"){
status = 1
self.clapsDeactive.image = UIImage(named: "applause")
}else{
status = 0
self.clapsDeactive.image = UIImage(named: "applause_on")
}
set = set + 1
}
}
func kur(str: String){
print("asdjvhsagdvjsakd")
let videoURL = URL(string: str)
// var myUrl = post.image // post.image is image url
let fileUrl = videoURL
// aPlayer = AVPlayer(URL: fileUrl as! URL)
player = AVPlayer(url: fileUrl!)
moviePlayerController.player = player
moviePlayerController.view.frame = CGRect(x:0, y:37, width:frame.size.width, height:300)
moviePlayerController.videoGravity = AVLayerVideoGravity.resizeAspectFill.rawValue
moviePlayerController.view.sizeToFit()
moviePlayerController.showsPlaybackControls = true
addSubview(moviePlayerController.view)
//moviePlayerController.player?.play()
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(videoHeader)
addSubview(videoName)
addSubview(profilePhoto)
addSubview(userName)
addSubview(location)
addSubview(videoFooter)
addSubview(clapsDeactive)
addSubview(comment)
addSubview(clapsText)
addSubview(commentsText)
addSubview(content)
addSubview(videoCover)
setup()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(like(tapGestureRecognizer:)))
clapsDeactive.isUserInteractionEnabled = true
clapsDeactive.addGestureRecognizer(tapGestureRecognizer)
let tapGestureRecognizer2 = UITapGestureRecognizer(target: self, action: #selector(playVideo(tapGestureRecognizer:)))
videoCover.isUserInteractionEnabled = true
videoCover.addGestureRecognizer(tapGestureRecognizer2)
}
func setup(){
videoHeader.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: frame.size.width, height: CGFloat(screen.heightHesap(x: 37)))
videoName.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: CGFloat(screen.heightHesap(x: 9)), paddingLeft: CGFloat(screen.widthHesap(x: 40)), paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
profilePhoto.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: CGFloat(screen.heightHesap(x: 360)), paddingLeft: CGFloat(screen.widthHesap(x: 20)), paddingBottom: 0, paddingRight: 0, width: CGFloat(screen.widthHesap(x: 37)), height: CGFloat(screen.heightHesap(x: 37)))
userName.anchor(top: profilePhoto.topAnchor, left: profilePhoto.rightAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: CGFloat(screen.widthHesap(x: 11)), paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
location.anchor(top: userName.bottomAnchor, left: profilePhoto.rightAnchor, bottom: nil, right: nil, paddingTop: 0, paddingLeft: CGFloat(screen.widthHesap(x: 11)), paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
videoFooter.anchor(top: topAnchor, left: nil, bottom: nil, right: rightAnchor, paddingTop: CGFloat(screen.heightHesap(x: 356)), paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: CGFloat(screen.widthHesap(x: 180)), height: CGFloat(screen.heightHesap(x: 50)))
clapsDeactive.anchor(top: videoFooter.topAnchor, left: videoFooter.leftAnchor, bottom: nil, right: nil, paddingTop: CGFloat(screen.heightHesap(x: 9)), paddingLeft: CGFloat(screen.widthHesap(x: 29)), paddingBottom: 0, paddingRight: 0, width: CGFloat(screen.widthHesap(x: 33)), height: CGFloat(screen.heightHesap(x: 28)))
comment.anchor(top: videoFooter.topAnchor, left: videoFooter.leftAnchor, bottom: nil, right: nil, paddingTop: CGFloat(screen.heightHesap(x: 15)), paddingLeft: CGFloat(screen.widthHesap(x: 110)), paddingBottom: 0, paddingRight: 0, width: CGFloat(screen.widthHesap(x: 23)), height: CGFloat(screen.heightHesap(x: 23)))
clapsText.anchor(top: videoFooter.topAnchor, left: videoFooter.leftAnchor, bottom: nil, right: nil, paddingTop: CGFloat(screen.heightHesap(x: 17)), paddingLeft: CGFloat(screen.widthHesap(x: 67)), paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
commentsText.anchor(top: videoFooter.topAnchor, left: videoFooter.leftAnchor, bottom: nil, right: nil, paddingTop: CGFloat(screen.heightHesap(x: 17)), paddingLeft: CGFloat(screen.widthHesap(x: 142)), paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
var hesap = frame.size.width - CGFloat(screen.widthHesap(x: 40))
content.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: rightAnchor, paddingTop: CGFloat(screen.heightHesap(x: 420)), paddingLeft: CGFloat(screen.widthHesap(x: 20)), paddingBottom: 0, paddingRight: CGFloat(screen.widthHesap(x: 20)), width: hesap, height: CGFloat(screen.heightHesap(x: 50)))
videoCover.anchor(top: topAnchor, left: leftAnchor, bottom: nil, right: nil, paddingTop: 37, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: frame.size.width, height: 300)
}
#objc func playVideo(tapGestureRecognizer: UITapGestureRecognizer)
{
kur(str: (post?.videoPath)!)
}
#objc func like(tapGestureRecognizer: UITapGestureRecognizer)
{
let userID = defaults.string(forKey: "userID")
let acToken = defaults.string(forKey: "acToken")
let parameters: Parameters = [
"userID": userID ?? "",
"acToken": acToken ?? "",
"videoID": post?.videoID ?? "",
"status": status,
"videoUserID": post?.userID ?? ""
]
Alamofire.request("***********************************", method: .post, parameters: parameters).validate().responseJSON { response in
switch response.result {
case .success:
print(response.data!)
if let result = response.result.value {
let json = result as! NSDictionary
let statusCode = json["statusCode"]
let status = json["status"]
var like = json["likeCount"] as? String
if(statusCode as? Int == 10){
print("Beğendim.")
self.post?.likeStatus = "1"
print(self.post?.likeStatus)
self.clapsDeactive.image = UIImage(named: "applause_on")
self.clapsText.text = like
}else{
print("Beğenemedim.")
self.post?.likeStatus = "0"
self.clapsDeactive.image = UIImage(named: "applause")
self.clapsText.text = like
}
}
case .failure(let error):
print(error)
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Just use this code for play video in the cell with expect index. It will help you a lot and for other also ,
class UserProfilePhotoCell: UICollectionViewCell {
#IBOutlet var viewForVideo: UIView!
#IBOutlet var btnPlayVideo: UIButton!
var blockVideoPlay : ((_ sender : UIButton)->())?
override func awakeFromNib() {
super.awakeFromNib()
removeSubLayerFromVideo()
self.setupMoviePlayer()
}
#IBAction func btnPlayVideoCLK(_ sender: UIButton) {
if blockVideoPlay != nil {
blockVideoPlay!(sender)
}
}
func removeSubLayerFromVideo() {
if let layerArray = viewForVideo.layer.sublayers, layerArray.isEmpty {
for layer in layerArray {
if layer.name == "\(viewForVideo.tag)" {
layer.removeFromSuperlayer()
}
}
}
}
func setupMoviePlayer(){
self.avPlayer = AVPlayer.init(playerItem: self.videoPlayerItem)
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer?.videoGravity = AVLayerVideoGravity.resizeAspect
avPlayerLayer?.name = "\(viewForVideo.tag)"
avPlayer?.volume = 3
avPlayer?.actionAtItemEnd = .none
avPlayerLayer?.frame = CGRect(x: 0, y: 0, width: UIScreen.width, height: UIScreen.height)
self.viewForVideo.backgroundColor = .clear
self.viewForVideo.layer.insertSublayer(avPlayerLayer!, at: 0)
// This notification is fired when the video ends, you can handle it in the method.
NotificationCenter.default.addObserver(self,
selector: #selector(self.playerItemDidReachEnd(notification:)),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: avPlayer?.currentItem)
}
func stopPlayback(){
if let player = self.avPlayer {
if player.isPlaying {
player.pause()
}
}
}
func startPlayback(){
self.avPlayer?.play()
}
// A notification is fired and seeker is sent to the beginning to loop the video again
#objc func playerItemDidReachEnd(notification: Notification) {
//let p: AVPlayerItem = notification.object as! AVPlayerItem
//p.seek(to: kCMTimeZero)
btnPlayVideo.isSelected = false
self.avPlayer?.currentItem?.seek(to: kCMTimeZero, completionHandler: { (success) in
if success {
self.avPlayer?.pause()
}
})
}
}
For View Controller use this code also you can post via var all data in the cell and from the cell, you can set URL path it works fine make sure when view Will Disappear you have to stop the player
class UserProfileController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
for cell in mediaListCollection.visibleCells {
if let cell = cell as? UserProfilePhotoCell {
cell.stopPlayback()
}
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! UserProfilePhotoCell
let url = URL(fileURLWithPath: path, isDirectory: true)
cell.videoPlayerItem = AVPlayerItem(url: url)
cell.viewForVideo.tag = indexPath.row+1
cell.blockVideoPlay = { (sender) -> Void in
sender.isSelected = !sender.isSelected
if sender.isSelected {
cell.startPlayback()
}
else {
cell.stopPlayback()
}
}
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if let cell = cell as? UserProfilePhotoCell {
cell.stopPlayback()
if cell.btnPlayVideo.isSelected {
cell.btnPlayVideo.isSelected = false
}
}
}
}
Also use this extension for check player is currently playing or not
extension AVPlayer {
var isPlaying: Bool {
return rate != 0 && error == nil
}
}