提交 5e2fbb79 编写于 作者: L libb

add swift demo

Change-Id: I6872f5311c5cdff71722772334b90e22e669a799
上级 ea0268a5
//
// AppDelegate.swift
// MvvmDemo
//
// Created by 徐强强 on 2019/1/31.
// Copyright © 2019年 徐强强. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
self.window = UIWindow(frame: UIScreen.main.bounds)
let nav = UINavigationController()
// let viewModel = MenuSubGroupViewModel(navigator: MenuSubGroupNavigator(navigationController: nav))
// let vc = MenuSubGroupViewController()
// vc.viewModel = viewModel
let vc = ViewController()
vc.view.backgroundColor = UIColor.red
nav.setViewControllers([vc], animated: false)
self.window!.rootViewController = nav
self.window!.backgroundColor = UIColor.white
self.window!.makeKeyAndVisible()
if #available(iOS 11, *) {
// UIScrollView.appearance().contentInsetAdjustmentBehavior = .never
}
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
//
// BaseNavigator.swift
// takeaway
//
// Created by 白天伟 on 2018/6/13.
// Copyright © 2018年 zaihui. All rights reserved.
//
import Foundation
import UIKit
class BaseNavigator {
var navigationController: UINavigationController?
init(navigationController: UINavigationController?) {
self.navigationController = navigationController
}
func pop() {
navigationController?.popViewController(animated: true)
}
func dismiss() {
navigationController?.dismiss(animated: true, completion: nil)
}
}
//
// BaseViewController.swift
// takeaway
//
// Created by 徐强强 on 2018/1/24.
// Copyright © 2018年 zaihui. All rights reserved.
//
import UIKit
import SnapKit
import RxCocoa
import RxSwift
class BaseViewController: UIViewController {
let disposeBag = DisposeBag()
var keyboardAnimationDuration: Double = 0
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
autoLayout()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
self.navigationController?.navigationBar.isTranslucent = false
self.tabBarController?.tabBar.isTranslucent = false
if #available(iOS 11.0, *) {
} else {
self.automaticallyAdjustsScrollViewInsets = false
}
}
func setupUI() {
}
func autoLayout() {
}
}
//
// LoadingTracker.swift
// takeaway
//
// Created by 徐强强 on 2018/7/3.
// Copyright © 2018年 zaihui. All rights reserved.
//
import Foundation
import RxSwift
import RxCocoa
private struct ActivityToken<E> : ObservableConvertibleType, Disposable {
private let _source: Observable<E>
private let _dispose: Cancelable
init(source: Observable<E>, disposeAction: @escaping () -> Void) {
_source = source
_dispose = Disposables.create(with: disposeAction)
}
func dispose() {
_dispose.dispose()
}
func asObservable() -> Observable<E> {
return _source
}
}
/**
Enables monitoring of sequence computation.
If there is at least one sequence computation in progress, `true` will be sent.
When all activities complete `false` will be sent.
*/
public class ActivityIndicator: SharedSequenceConvertibleType {
public typealias E = Bool
public typealias SharingStrategy = DriverSharingStrategy
private let lock = NSRecursiveLock()
private let subject = PublishSubject<Int>()
private var value = 0
private let activity: SharedSequence<SharingStrategy, Bool>
public init() {
activity = subject.asDriver(onErrorJustReturn: 0)
.map { $0 > 0 }
.distinctUntilChanged()
}
fileprivate func trackActivityOfObservable<O: ObservableConvertibleType>(_ source: O) -> Observable<O.E> {
return Observable.using({ () -> ActivityToken<O.E> in
self.increment()
return ActivityToken(source: source.asObservable(), disposeAction: self.decrement)
}) { activity in
return activity.asObservable()
}
}
private func increment() {
lock.lock()
value += 1
subject.onNext(value)
lock.unlock()
}
private func decrement() {
lock.lock()
value -= 1
subject.onNext(value)
lock.unlock()
}
public func asSharedSequence() -> SharedSequence<SharingStrategy, E> {
return activity
}
}
extension ObservableConvertibleType {
public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable<E> {
return activityIndicator.trackActivityOfObservable(self)
}
public func trackActivity(_ activityIndicators: ActivityIndicator...) -> Observable<E> {
return activityIndicators.reduce(self.asObservable(), {$1.trackActivityOfObservable($0)})
}
}
//
// LoadingView.swift
// takeaway
//
// Created by 徐强强 on 2018/7/3.
// Copyright © 2018年 zaihui. All rights reserved.
//
import UIKit
private let padding: CGFloat = 14
private let cornerRadius: CGFloat = 12
private let loadingWidthHeight: CGFloat = 37
private let textFont = UIFont.systemFont(ofSize: 14)
private let hudOffset = CGPoint(x: 0, y: -50)
private let JIdentifier = "JScreenView"
public class LoadingView: UIView {
private var activityView: UIActivityIndicatorView?
private var text: String?
private var hudWidth: CGFloat = 110
private var hudHeight: CGFloat = 110
private let textHudWidth: CGFloat = 130
// MARK: - init
// enable :是否允许用户交互,默认允许。
init(text: String?,
enable: Bool = true,
offset: CGPoint = hudOffset,
superView: UIView) {
self.text = text
super.init(frame: CGRect(origin: .zero, size: CGSize(width: hudWidth, height: hudHeight)))
setupUI()
addLoadingView(offset: offset, superView: superView)
if !enable {
superView.addSubview(screenView)
}
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.8)
self.layer.cornerRadius = cornerRadius
addActivityView()
addLabel()
}
private func addLoadingView(offset: CGPoint, superView: UIView) {
guard self.superview == nil else {
return
}
superView.addSubview(self)
self.alpha = 0
if text != nil {
hudWidth = textHudWidth
}
addConstraint(width: hudWidth, height: hudHeight)
superView.addConstraint(toCenterX: self,
constantX: offset.x,
toCenterY: self,
constantY: offset.y)
}
private func addLabel() {
var labelY: CGFloat = 0.0
labelY = padding * 2 + loadingWidthHeight
if let text = text {
textLabel.text = text
addSubview(textLabel)
addConstraint(to: textLabel, edgeInset: UIEdgeInsets(top: labelY,
left: padding / 2,
bottom: -padding,
right: -padding / 2))
let textSize: CGSize = size(from: text)
hudHeight = textSize.height + labelY + padding
}
}
private func addActivityView() {
activityView = UIActivityIndicatorView(style: .whiteLarge)
activityView?.translatesAutoresizingMaskIntoConstraints = false
activityView?.startAnimating()
addSubview(activityView!)
generalConstraint(at: activityView!)
}
// MARK: - show func
public func show() {
self.animate(hide: false) {
}
}
static func show(text: String?,
enable: Bool = false,
offset: CGPoint = hudOffset,
superView: UIView) {
let loading = LoadingView(text: text,
enable: enable,
offset: offset,
superView: superView)
loading.show()
}
// MARK: - Hide func
public func hide() {
self.animate(hide: true) {
self.removeFromSuperview()
self.screenView.removeFromSuperview()
}
}
static func hide(from superView: UIView) {
for view in superView.subviews {
if view.isKind(of: self) {
view.animate(hide: true, completion: {
view.removeFromSuperview()
})
}
if view.restorationIdentifier == JIdentifier {
view.removeFromSuperview()
}
}
}
// MARK: - method
private func size(from text: String) -> CGSize {
return text.textSizeWithFont(font: textFont, constrainedToSize: CGSize(width: hudWidth - padding, height: CGFloat(MAXFLOAT)))
}
private func generalConstraint(at view: UIView) {
view.addConstraint(width: loadingWidthHeight, height: loadingWidthHeight)
if text != nil {
addConstraint(toCenterX: view, toCenterY: nil)
addConstraint(with: view,
topView: self,
leftView: nil,
bottomView: nil,
rightView: nil,
edgeInset: UIEdgeInsets(top: padding, left: 0, bottom: 0, right: 0))
} else {
addConstraint(toCenterX: view, toCenterY: view)
}
}
// MARK: - setter && getter
private lazy var screenView: UIView = {
let view = UIView()
view.frame = UIScreen.main.bounds
view.isUserInteractionEnabled = true
view.restorationIdentifier = JIdentifier
return view
}()
private lazy var textLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.white
label.font = textFont
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
}
// MARK: - Extension String
extension String {
fileprivate func textSizeWithFont(font: UIFont, constrainedToSize size: CGSize) -> CGSize {
var textSize: CGSize!
if size.equalTo(CGSize.zero) {
let attributes = NSDictionary(object: font, forKey: NSAttributedString.Key.font as NSCopying)
textSize = self.size(withAttributes: attributes as? [NSAttributedString.Key: Any])
} else {
let option = NSStringDrawingOptions.usesLineFragmentOrigin
let attributes = NSDictionary(object: font, forKey: NSAttributedString.Key.font as NSCopying)
let stringRect = self.boundingRect(with: size,
options: option,
attributes: attributes as? [NSAttributedString.Key: Any],
context: nil)
textSize = stringRect.size
}
return textSize
}
}
// MARK: - Extension LoadingView
extension LoadingView {
fileprivate class func asyncAfter(duration: TimeInterval, completion: (() -> Void)?) {
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
completion?()
}
}
}
[
{
"name":"关注"
},
{
"name":"推荐"
},
{
"name":"热点"
},
{
"name":"考得好"
},
{
"name":"教育"
},
{
"name":"春节"
},
{
"name":"游戏"
},
{
"name":"视频"
},
{
"name":"娱乐"
},
{
"name":"科技"
},
{
"name":"国际"
},
{
"name":"体育"
}
]
//
// ConstraintView+Extension.swift
// MvvmDemo
//
// Created by 徐强强 on 2019/2/1.
// Copyright © 2019年 徐强强. All rights reserved.
//
import Foundation
import SnapKit
public extension ConstraintView {
var safeSnp: ConstraintAttributesDSL {
if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.snp
} else {
return self.snp
}
}
}
//
// ObservableConvertibleType+Extension.swift
// takeaway
//
// Created by 白天伟 on 2018/7/13.
// Copyright © 2018年 zaihui. All rights reserved.
//
import Foundation
import RxSwift
import RxCocoa
extension ObservableConvertibleType {
func asDriverOnErrorJustComplete() -> Driver<E> {
return asDriver { _ in
return Driver.empty()
}
}
}
//
// UIButton+Extension.swift
// MvvmDemo
//
// Created by 徐强强 on 2019/2/1.
// Copyright © 2019年 徐强强. All rights reserved.
//
import UIKit
// MARK: - UIButton布局样式
/// UIButton布局样式
///
/// - top: image在上,label在下
/// - left: image在左,label在右
/// - bottom: image在下,label在上
/// - right: image在右,label在左
enum ButtonEdgeInsetsStyle {
case top
case left
case bottom
case right
}
extension UIButton {
func layoutButton(edgeInsetsStyle style: ButtonEdgeInsetsStyle, imageTitleSpace space: CGFloat, isUsingLabelFrameWidth: Bool = false) {
let imageWidth = imageView?.intrinsicContentSize.width ?? 0
let imageHeight = imageView?.intrinsicContentSize.height ?? 0
let labelWidth = isUsingLabelFrameWidth ? (titleLabel?.frame.width ?? 0) : (titleLabel?.intrinsicContentSize.width ?? 0)
let labelHeight = titleLabel?.intrinsicContentSize.height ?? 0
let imageOffsetX = labelWidth / 2 //image中心移动的x距离
let imageOffsetY = imageHeight / 2 + space / 2 //image中心移动的y距离
let labelOffsetX = imageWidth / 2 //label中心移动的x距离
let labelOffsetY = labelHeight / 2 + space / 2 //label中心移动的y距离
let tempWidth = labelWidth > imageWidth ? labelWidth : imageWidth
let changedWidth = labelWidth + imageWidth - tempWidth
let tempHeight = labelHeight > imageHeight ? labelHeight : imageHeight
let changedHeight = labelHeight + imageHeight + space - tempHeight
var newImageEdgeInsets = UIEdgeInsets.zero
var newTitleEdgeInsets = UIEdgeInsets.zero
switch style {
case .top:
newImageEdgeInsets = UIEdgeInsets(top: -imageOffsetY, left: imageOffsetX, bottom: imageOffsetY, right: -imageOffsetX)
newTitleEdgeInsets = UIEdgeInsets(top: labelOffsetY, left: -labelOffsetX, bottom: -labelOffsetY, right: labelOffsetX)
self.contentEdgeInsets = UIEdgeInsets(top: imageOffsetY, left: -changedWidth / 2, bottom: changedHeight - imageOffsetY, right: -changedWidth / 2)
case .left:
newImageEdgeInsets = UIEdgeInsets(top: 0, left: -space / 2, bottom: 0, right: space / 2)
newTitleEdgeInsets = UIEdgeInsets(top: 0, left: space / 2, bottom: 0, right: -space / 2)
self.contentEdgeInsets = UIEdgeInsets(top: 0, left: space / 2, bottom: 0, right: space / 2)
case .bottom:
newImageEdgeInsets = UIEdgeInsets(top: imageOffsetY, left: imageOffsetX, bottom: -imageOffsetY, right: -imageOffsetX)
newTitleEdgeInsets = UIEdgeInsets(top: -labelOffsetY, left: -labelOffsetX, bottom: labelOffsetY, right: labelOffsetX)
self.contentEdgeInsets = UIEdgeInsets(top: changedHeight - imageOffsetY, left: -changedWidth / 2, bottom: imageOffsetY, right: -changedWidth / 2)
case .right:
newImageEdgeInsets = UIEdgeInsets(top: 0, left: labelWidth + space / 2, bottom: 0, right: -labelWidth - space / 2.0)
newTitleEdgeInsets = UIEdgeInsets(top: 0, left: -imageWidth - space / 2.0, bottom: 0, right: imageWidth + space / 2.0)
self.contentEdgeInsets = UIEdgeInsets(top: 0, left: space / 2, bottom: 0, right: space / 2)
}
imageEdgeInsets = newImageEdgeInsets
titleEdgeInsets = newTitleEdgeInsets
}
}
//
// UIView+Extension.swift
// takeaway
//
// Created by jike on 2018/1/24.
// Copyright © 2018年 zaihui. All rights reserved.
//
import UIKit
// MARK: BasicAnimation
extension UIView {
func rotationAnimation(with duration: Double = 2) {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.fromValue = 0
rotationAnimation.toValue = Double.pi * 2
rotationAnimation.repeatCount = MAXFLOAT
rotationAnimation.isCumulative = true
rotationAnimation.duration = duration
rotationAnimation.isRemovedOnCompletion = false
layer.add(rotationAnimation, forKey: "rotationAnimation")
}
func stopRotationAnimation() {
layer.removeAnimation(forKey: "rotationAnimation")
}
}
// MARK: - Extension animate
extension UIView {
func animate(hide: Bool, completion: (() -> Void)? = nil) {
UIView.animate(withDuration: 0.3,
animations: {
if hide {
self.alpha = 0
} else {
self.alpha = 1
}
}) { _ in
completion?()
}
}
}
// MARK: - Extension addConstraint
extension UIView {
func addConstraint(width: CGFloat, height: CGFloat) {
if width > 0 {
addConstraint(NSLayoutConstraint(item: self,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: NSLayoutConstraint.Attribute(rawValue: 0)!,
multiplier: 1,
constant: width))
}
if height > 0 {
addConstraint(NSLayoutConstraint(item: self,
attribute: .height,
relatedBy: .equal,
toItem: nil,
attribute: NSLayoutConstraint.Attribute(rawValue: 0)!,
multiplier: 1,
constant: height))
}
}
func addConstraint(toCenterX xView: UIView?, toCenterY yView: UIView?) {
addConstraint(toCenterX: xView, constantX: 0, toCenterY: yView, constantY: 0)
}
func addConstraint(toCenterX xView: UIView?,
constantX: CGFloat,
toCenterY yView: UIView?,
constantY: CGFloat) {
if let xView = xView {
addConstraint(NSLayoutConstraint(item: xView,
attribute: .centerX,
relatedBy: .equal,
toItem: self,
attribute: .centerX,
multiplier: 1, constant: constantX))
}
if let yView = yView {
addConstraint(NSLayoutConstraint(item: yView,
attribute: .centerY,
relatedBy: .equal,
toItem: self,
attribute: .centerY,
multiplier: 1,
constant: constantY))
}
}
func addConstraint(to view: UIView, edgeInset: UIEdgeInsets) {
addConstraint(with: view,
topView: self,
leftView: self,
bottomView: self,
rightView: self,
edgeInset: edgeInset)
}
func addConstraint(with view: UIView,
topView: UIView?,
leftView: UIView?,
bottomView: UIView?,
rightView: UIView?,
edgeInset: UIEdgeInsets) {
if let topView = topView {
addConstraint(NSLayoutConstraint(item: view,
attribute: .top,
relatedBy: .equal,
toItem: topView,
attribute: .top,
multiplier: 1,
constant: edgeInset.top))
}
if let leftView = leftView {
addConstraint(NSLayoutConstraint(item: view,
attribute: .left,
relatedBy: .equal,
toItem: leftView,
attribute: .left,
multiplier: 1,
constant: edgeInset.left))
}
if let bottomView = bottomView {
addConstraint(NSLayoutConstraint(item: view,
attribute: .bottom,
relatedBy: .equal,
toItem: bottomView,
attribute: .bottom,
multiplier: 1,
constant: edgeInset.bottom))
}
if let rightView = rightView {
addConstraint(NSLayoutConstraint(item: view,
attribute: .right,
relatedBy: .equal,
toItem: rightView,
attribute: .right,
multiplier: 1,
constant: edgeInset.right))
}
}
}
//
// UIViewController+Extension.swift
// takeaway
//
// Created by 徐强强 on 2018/1/23.
// Copyright © 2018年 zaihui. All rights reserved.
//
import UIKit
// MARK: - show and hide loading,noDataView,noNetView
extension UIViewController {
func showLoading(text: String? = nil) {
LoadingView.show(text: text,
enable: false,
superView: self.view)
}
func hideLoading() {
LoadingView.hide(from: self.view)
}
}
//
// UIViewController+Rx.swift
// takeaway
//
// Created by 徐强强 on 2018/8/8.
// Copyright © 2018年 zaihui. All rights reserved.
//
import Foundation
import RxSwift
import RxCocoa
extension Reactive where Base: UIViewController {
var isLoading: Binder<Bool> {
return Binder(base, binding: { (vc, result) in
if result {
vc.showLoading()
} else {
vc.hideLoading()
}
})
}
var viewWillAppear: Driver<Void> {
return sentMessage(#selector(UIViewController.viewWillAppear(_:)))
.map({_ in})
.asDriver(onErrorJustReturn: ())
}
var viewWillDisappear: Driver<Void> {
return sentMessage(#selector(UIViewController.viewWillDisappear(_:)))
.map({_ in})
.asDriver(onErrorJustReturn: ())
}
}
import UIKit
import RxSwift
class LabelButtonCell: UITableViewCell {
var disposeBag = DisposeBag()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
autoLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
disposeBag = DisposeBag()
}
// MARK: - setupUI && autoLayout
private func setupUI() {
self.selectionStyle = .none
contentView.addSubview(titleLabel)
contentView.addSubview(subTitleLabel)
contentView.addSubview(rightButton1)
contentView.addSubview(rightButton2)
}
private func autoLayout() {
titleLabel.snp.makeConstraints { (make) in
make.left.equalTo(14)
make.height.equalToSuperview()
make.centerY.equalToSuperview()
}
rightButton1.snp.makeConstraints { (make) in
make.right.equalTo(-14)
make.height.equalToSuperview()
make.width.equalTo(58)
make.centerY.equalToSuperview()
}
rightButton2.snp.makeConstraints { [unowned self] (make) in
make.right.equalTo(self.rightButton1.snp.left)
make.height.equalTo(18)
make.width.equalTo(58)
make.centerY.equalToSuperview()
}
subTitleLabel.snp.makeConstraints { [unowned self] (make) in
make.left.equalTo(self.titleLabel.snp.right).offset(10)
make.right.equalTo(self.rightButton2.snp.left).offset(-10)
make.height.equalToSuperview()
make.centerY.equalToSuperview()
}
}
// MARK: - setter && getter
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.black
label.font = UIFont.systemFont(ofSize: 16)
label.backgroundColor = .clear
return label
}()
private lazy var subTitleLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.gray
label.font = UIFont.systemFont(ofSize: 14)
label.backgroundColor = .clear
return label
}()
lazy var rightButton1: UIButton = {
let button = UIButton(type: .custom)
button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
button.setTitleColor(UIColor.blue, for: .normal)
button.contentHorizontalAlignment = .right
return button
}()
lazy var rightButton2: UIButton = {
let button = UIButton(type: .custom)
button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
button.setTitleColor(UIColor.blue, for: .normal)
return button
}()
var data: (title: String, subTitle: String, rightText1: String, rightText2: String) = ("", "", "", "") {
didSet {
titleLabel.text = data.title
subTitleLabel.text = data.subTitle
rightButton1.setTitle(data.rightText1, for: .normal)
rightButton2.setTitle(data.rightText2, for: .normal)
}
}
}
//
// MenuEditGroupNavigator.swift
// takeaway
//
// Created by 徐强强 on 2018/7/12.
// Copyright © 2018年 zaihui. All rights reserved.
//
import Foundation
class MenuEditGroupNavigator: BaseNavigator {
}
//
// MenuSortDishesNavigator.swift
// takeaway
//
// Created by 徐强强 on 2018/7/2.
// Copyright © 2018年 zaihui. All rights reserved.
//
class MenuSubGroupNavigator: BaseNavigator {
func toMenuEditGroupVC() -> MenuEditGroupViewController {
let navigator = MenuEditGroupNavigator(navigationController: navigationController)
let viewModel = MenuEditGroupViewModel(navigator: navigator)
let vc = MenuEditGroupViewController()
vc.viewModel = viewModel
navigationController?.pushViewController(vc, animated: true)
return vc
}
}
//
// DishesEditGroupViewController.swift
// takeaway
//
// Created by 徐强强 on 2018/1/17.
// Copyright © 2018年 zaihui. All rights reserved.
//
import UIKit
import RxCocoa
import RxSwift
import SnapKit
class MenuEditGroupViewController: BaseViewController {
var viewModel: MenuEditGroupViewModel!
var saveData = PublishSubject<Void>()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "保存", style: .plain, target: nil, action: nil)
self.bindViewModel()
}
func bindViewModel() {
navigationItem.rightBarButtonItem!.rx.tap
.asDriver()
.drive(saveData)
.disposed(by: disposeBag)
navigationItem.rightBarButtonItem!.rx.tap
.asDriver()
.drive(onNext: { _ in
self.navigationController?.popViewController(animated: true)
})
.disposed(by: disposeBag)
}
}
//
// DishesSubGroupManageViewController.swift
// takeaway
//
// Created by 徐强强 on 2018/1/17.
// Copyright © 2018年 zaihui. All rights reserved.
//
import UIKit
import RxSwift
import RxCocoa
import RxDataSources
class MenuSubGroupViewController: BaseViewController {
var viewModel: MenuSubGroupViewModel!
private let cellDeleteButtonTap = PublishSubject<IndexPath>()
private let cellRenameButtonTap = PublishSubject<IndexPath>()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "分组管理"
self.bindViewModel()
}
// MARK: - setupUI && autoLayout
override func setupUI() {
view.addSubview(tableView)
view.addSubview(tabBar)
tabBar.addSubview(createGroupButton)
}
override func autoLayout() {
tabBar.snp.makeConstraints { (make) in
make.left.right.bottom.equalToSuperview()
make.top.equalTo(view.safeSnp.bottom).offset(-49)
}
createGroupButton.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.height.equalTo(49)
make.top.equalTo(0.5)
}
tableView.snp.makeConstraints { [unowned self] (make) in
make.left.top.right.equalToSuperview()
make.bottom.equalTo(self.tabBar.snp.top)
}
}
func bindViewModel() {
let viewDidLoad = Driver<Void>.just(())
createGroupButton.rx.tap.asDriver()
.drive(onNext: { _ in
self.viewModel.refreshRelay.onNext(Void())
})
.disposed(by: disposeBag)
viewModel.getMenusInfo()
.asDriver(onErrorJustReturn: [])
.drive(tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
RxOpenAPIProvider.rx.request(.newList)
.mapToObject(type: TopModel.self)
// .mapToArray(type: GroupModel.self)
.subscribe(onNext: { (rsps) in
print (rsps)
})
.disposed(by: disposeBag)
// .asObservable()
// .subscribe(onNext: {[weak self] (Void) in
// print("sender")
// self?.viewModel.refreshRelay.onNext(Void)
// })
// .disposed(by: disposeBag)
// let input = MenuSubGroupViewModel.Input(createNewGroup: createGroupButton.rx.tap.asDriver(),
// viewDidLoad: viewDidLoad,
// cellDeleteButtonTap: cellDeleteButtonTap.asDriverOnErrorJustComplete(),
// cellRenameButtonTap: cellRenameButtonTap.asDriverOnErrorJustComplete())
//
// let output = viewModel.transform(input: input)
// output.loading
// .drive(rx.isLoading)
// .disposed(by: disposeBag)
// output.dataSource
// .drive(tableView.rx.items(dataSource: dataSource))
// .disposed(by: disposeBag)
}
// MARK: - setter && getter
private lazy var tabBar: UIView = {
let tabBar = UIView()
tabBar.backgroundColor = .white
let lineView = UIView(frame: CGRect(x: 0,
y: 0,
width: self.view.frame.size.width,
height: 0.5))
lineView.backgroundColor = UIColor.gray
tabBar.addSubview(lineView)
return tabBar
}()
private lazy var createGroupButton: UIButton = {
let button = UIButton(type: .custom)
button.frame = CGRect(x: 0,
y: 0.5,
width: self.view.frame.size.width,
height: 44)
button.setImage(UIImage(named: "goods_manage_add_icon"), for: .normal)
button.setTitle("新建分组", for: .normal)
button.setTitleColor(.black, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
button.layoutButton(edgeInsetsStyle: .left, imageTitleSpace: 8)
return button
}()
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.delegate = self
tableView.separatorStyle = .singleLine
tableView.separatorColor = UIColor.gray
tableView.estimatedSectionFooterHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.showsHorizontalScrollIndicator = false
tableView.showsVerticalScrollIndicator = false
let headerView = UIView(frame: CGRect(x: 0,
y: 0,
width: self.view.frame.size.width,
height: 16))
tableView.tableHeaderView = headerView
tableView.register(LabelButtonCell.self, forCellReuseIdentifier: "LabelButtonCell")
return tableView
}()
private lazy var dataSource: RxTableViewSectionedReloadDataSource<CellSectionModel> = {
return RxTableViewSectionedReloadDataSource<CellSectionModel>(configureCell: { [weak self](_, tableView, indexPath, item) -> UITableViewCell in
let cell: LabelButtonCell = tableView.dequeueReusableCell(withIdentifier: "LabelButtonCell") as! LabelButtonCell
cell.data = (item.name ?? "", "", "删除", "重命名")
cell.rightButton1.rx.tap
.subscribe(onNext: { [weak self] (_) in
self?.cellDeleteButtonTap.onNext(indexPath)
})
.disposed(by: cell.disposeBag)
cell.rightButton2.rx.tap
.subscribe(onNext: { [weak self] (_) in
self?.cellRenameButtonTap.onNext(indexPath)
})
.disposed(by: cell.disposeBag)
return cell
})
}()
}
// MARK: - UITableViewDelegate
extension MenuSubGroupViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
extension MenuSubGroupViewController {
struct CellSectionModel {
var items: [Item]
}
}
extension MenuSubGroupViewController.CellSectionModel: SectionModelType {
typealias Item = GroupModel
init(original: MenuSubGroupViewController.CellSectionModel, items: [Item]) {
self = original
self.items = items
}
}
//
// MenuEditGroupViewModel.swift
// takeaway
//
// Created by 徐强强 on 2018/7/12.
// Copyright © 2018年 zaihui. All rights reserved.
//
import Foundation
class MenuEditGroupViewModel {
private let navigator: MenuEditGroupNavigator
init(navigator: MenuEditGroupNavigator) {
self.navigator = navigator
}
}
extension MenuEditGroupViewModel {
struct Input {
}
struct Output {
}
}
//
// MenuSortDishesViewModel.swift
// takeaway
//
// Created by 徐强强 on 2018/7/2.
// Copyright © 2018年 zaihui. All rights reserved.
//
import Foundation
import RxCocoa
import RxSwift
class MenuSubGroupViewModel {
private let navigator: MenuSubGroupNavigator
private var groups: [GroupModel]?
let refreshRelay = PublishSubject<Void>()
init(navigator: MenuSubGroupNavigator) {
self.navigator = navigator
refreshRelay.asObservable()
.subscribe(onNext: { (Void) in
print("222")
self.navigator.toMenuEditGroupVC()
.saveData
.asDriverOnErrorJustComplete()
}).disposed(by: disposeBag)
}
func transform(input: Input) -> Output {
// .subscribe(onNext: { (Void) in
// print("222")
// self.navigator.toMenuEditGroupVC()
// .saveData
// .asDriverOnErrorJustComplete()
// }).disposed(by: disposeBag)
let loadingTracker = ActivityIndicator()
let createNewGroup = input.createNewGroup
.flatMapLatest { _ in
self.navigator.toMenuEditGroupVC()
.saveData
.asDriverOnErrorJustComplete()
}
let renameGroup = input.cellRenameButtonTap
.flatMapLatest { indexPath in
self.navigator.toMenuEditGroupVC()
.saveData
.asDriverOnErrorJustComplete()
}
let getMenusInfo = Driver.merge(createNewGroup, input.viewDidLoad, renameGroup)
.flatMapLatest { _ in
self.getMenusInfo()
.trackActivity(loadingTracker)
.asDriver(onErrorJustReturn: self.createSectionModel())
}
let deleteSubGroups = input.cellDeleteButtonTap
.flatMapLatest { (indexPath) -> Driver<[MenuSubGroupViewController.CellSectionModel]> in
return self.deleteSubGroups(at: indexPath)
.asDriver(onErrorJustReturn: self.createSectionModel())
}
let dataSource = Driver.merge(getMenusInfo, deleteSubGroups)
let loading = loadingTracker.asDriver()
return Output(dataSource: dataSource)
}
func getMenusInfo() -> Single<[MenuSubGroupViewController.CellSectionModel]> {
return RxOpenAPIProvider.rx.request(.newList)
.mapToObject(type: TopModel.self)
// .mapToArray(type: GroupModel.self)
.asSingle()
.map { result in
self.groups = result?.stories
return self.createSectionModel()
}
}
private func deleteSubGroups(at indexPath: IndexPath) -> Single<[MenuSubGroupViewController.CellSectionModel]> {
groups?.remove(at: indexPath.row);
return Single.just(self.createSectionModel())
}
private func createSectionModel() -> [MenuSubGroupViewController.CellSectionModel] {
if let dishGroups = self.groups, !dishGroups.isEmpty {
return [MenuSubGroupViewController.CellSectionModel(items: dishGroups)]
}
return []
}
}
extension MenuSubGroupViewModel {
struct Input {
let createNewGroup: Driver<Void>
let viewDidLoad: Driver<Void>
let cellDeleteButtonTap: Driver<IndexPath>
let cellRenameButtonTap: Driver<IndexPath>
}
struct Output {
let dataSource: Driver<[MenuSubGroupViewController.CellSectionModel]>
// let loading: Driver<Bool>
}
}
fileprivate var disposeBagContext: UInt8 = 0
/// each HasDisposeBag offers a unique RxSwift DisposeBag instance
public protocol HasDisposeBag: class {
/// a unique RxSwift DisposeBag instance
var disposeBag: DisposeBag { get set }
}
extension HasDisposeBag {
func synchronizedBag<T>( _ action: () -> T) -> T {
objc_sync_enter(self)
let result = action()
objc_sync_exit(self)
return result
}
public var disposeBag: DisposeBag {
get {
return synchronizedBag {
if let disposeObject = objc_getAssociatedObject(self, &disposeBagContext) as? DisposeBag {
return disposeObject
}
let disposeObject = DisposeBag()
objc_setAssociatedObject(self, &disposeBagContext, disposeObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return disposeObject
}
}
set {
synchronizedBag {
objc_setAssociatedObject(self, &disposeBagContext, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
extension MenuSubGroupViewModel: HasDisposeBag {}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
//
// MenuBean.swift
// takeaway
//
// Created by 徐强强 on 2018/3/20.
// Copyright © 2018年 zaihui. All rights reserved.
//
import Foundation
import ObjectMapper
public struct GroupModel: Mappable {
var name: String!
public init?(map: Map) {
}
mutating public func mapping(map: Map) {
name <- map["title"]
}
}
public struct TopModel: Mappable {
var stories: [GroupModel]!
public init?(map: Map) {
}
mutating public func mapping(map: Map) {
stories <- map["stories"]
}
}
import Foundation
import Moya
import Alamofire
// MARK: - Provider setup
//private let requestClosure = {
// (endpoint: Endpoint, done: @escaping MoyaProvider<OpenAPI>.RequestResultClosure) in
//
// do {
// var urlRequest = try endpoint.urlRequest()
// urlRequest.timeoutInterval = 15
// done(.success(urlRequest))
// } catch MoyaError.requestMapping(let url) {
// done(.failure(MoyaError.requestMapping(url)))
// } catch MoyaError.parameterEncoding(let error) {
// done(.failure(MoyaError.parameterEncoding(error)))
// } catch {
// done(.failure(MoyaError.underlying(error, nil)))
// }
//}
//
//private let stubClosure: (_ type: OpenAPI) -> Moya.StubBehavior = { type in
// switch type {
// case .newList:
// return Moya.StubBehavior.delayed(seconds: 3)
// }
//}
//
//private let endpointClosure = { (target: OpenAPI) -> Endpoint in
// var url = target.baseURL.absoluteString+"\(target.path)"
// if target.method == .get {
// var appendStr = ""
// if let para = target.parameters {
// appendStr = para.count > 0 ? "?" : ""
// for (key, value) in para {
// appendStr.append(key + "=\(value)&")
// }
// if appendStr.count > 0 {
// appendStr.remove(at: appendStr.index(before: appendStr.endIndex))
// }
// }
// url.append(appendStr)
// }
//
// var parameters = target.parameters
// if target.method == .get {
// parameters = nil
// }
//
// let endpoint: Endpoint = Endpoint(url: url,
// sampleResponseClosure: {.networkResponse(200, target.sampleData)},
// method: target.method,
// task: target.task,
// httpHeaderFields: nil)
//
// return endpoint
//}
//
//private let manager = Manager(
// configuration: URLSessionConfiguration.default,
// serverTrustPolicyManager: ServerTrustPolicyManager(policies: ["mp.openapi.pre.com": .disableEvaluation])
//)
//
//private let networkActivityPlugin = NetworkActivityPlugin {
// (change, target) -> () in
// switch(change){
//
// case .ended:
// UIApplication.shared.isNetworkActivityIndicatorVisible = false
// case .began:
// UIApplication.shared.isNetworkActivityIndicatorVisible = true
// }
//}
let RxOpenAPIProvider = MoyaProvider<OpenAPI>()
//(endpointClosure:endpointClosure,
// requestClosure:requestClosure,
// stubClosure:stubClosure,
// manager:manager,
// plugins: [networkActivityPlugin])
public enum OpenAPI {
case newList
}
extension OpenAPI: TargetType {
public var sampleData: Data {
guard let path = Bundle.main.path(forResource: "hotList", ofType: "json"), let data = NSData(contentsOfFile: path) else {
return Data()
}
return data as Data
}
public var baseURL: URL {
return URL(string: "https://news-at.zhihu.com/")!
}
public var path: String {
switch self {
case .newList:
return "api/4/news/latest"
}
}
public var method: Moya.Method {
switch self {
case .newList:
return .get
}
}
public var task: Task {
switch self {
default:
if self.method == .get {
return .requestPlain
}
return .requestParameters(parameters: self.parameters!, encoding: self.parameterEncoding)
}
}
public var validate: Bool {
return true
}
public var headers: [String: String]? {
return nil
}
}
extension OpenAPI {
public var parameters: [String: Any]? {
switch self {
default:
return nil
}
}
public var parameterEncoding: ParameterEncoding {
return JSONEncoding.default
}
}
//
// Observable+ObjectMapper.swift
// TFBaseLib_Swift
//
// Created by xiayiyong on 2017/3/23.
// Copyright © 2017年 上海赛可电子商务有限公司. All rights reserved.
//
import Foundation
import Moya
import ObjectMapper
import RxSwift
extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Response {
// MARK: - object
func mapToObject<T: Mappable>(type: T.Type) -> Observable<T?> {
return mapToResponse()
.map {
guard let dict = $0 as? [String:Any], dict.count > 0 else{
return nil
}
guard let obj = Mapper<T>().map(JSON: dict) else {
return nil
}
return obj
}
}
// MARK: - array
// let dictionary = $0 as? [String: AnyObject]
//
// guard let array = dictionary?["stories"] as? [[String: Any]] else{
// return []
// }
func mapToArray<T: Mappable >(type: T.Type) -> Observable<[T]> {
return mapToResponse()
.map {
guard let array = $0 as? [[String: Any]] else{
return []
}
let arr = Mapper<T>().mapArray(JSONArray: array)
return arr
}
}
// MARK: - response
private func mapToResponse() -> Observable<Any?> {
return asObservable().map { response in
guard ((200...209) ~= response.statusCode) else {
return nil
}
guard var json = try? JSONSerialization.jsonObject(with: response.data, options: .allowFragments) else {
return nil
}
let jsonString = String.init(data: response.data, encoding: .utf8)
if let data = jsonString?.data(using: .utf8) {
do {
json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return json
}
}
}
//
// ViewController.swift
// MvvmDemo
//
// Created by 徐强强 on 2019/1/31.
// Copyright © 2019年 徐强强. All rights reserved.
//
import UIKit
import RxDataSources
import RxSwift
class ViewController: UIViewController {
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
view.addSubview(tableView)
tableView.snp.makeConstraints { [unowned self] (make) in
make.left.right.bottom.equalTo(self.view)
make.top.equalTo(88)
}
self.getMenusInfo()
.asDriver(onErrorJustReturn: [])
.drive(tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
func getMenusInfo() -> Single<[ViewController.CellSectionModel]> {
return RxOpenAPIProvider.rx.request(.newList)
.mapToObject(type: TopModel.self)
// .mapToArray(type: GroupModel.self)
.asSingle()
.map { result in
let groups = result?.stories
return self.createSectionModel(groups: groups)
}
}
private func createSectionModel(groups: [GroupModel]?) -> [ViewController.CellSectionModel] {
if let dishGroups = groups, !dishGroups.isEmpty {
return [ViewController.CellSectionModel(items: dishGroups)]
}
return []
}
private lazy var dataSource: RxTableViewSectionedReloadDataSource<CellSectionModel> = {
return RxTableViewSectionedReloadDataSource<CellSectionModel>(configureCell: { [weak self](_, tableView, indexPath, item) -> UITableViewCell in
let cell: LabelButtonCell = tableView.dequeueReusableCell(withIdentifier: "LabelButtonCell") as! LabelButtonCell
cell.data = (item.name ?? "", "", "删除", "重命名")
cell.rightButton1.rx.tap
.subscribe(onNext: { [weak self] (_) in
// self?.cellDeleteButtonTap.onNext(indexPath)
let vc = MenuEditGroupViewController()
self?.navigationController?.pushViewController(vc, animated: true)
})
.disposed(by: cell.disposeBag)
cell.rightButton2.rx.tap
.subscribe(onNext: { [weak self] (_) in
// self?.cellRenameButtonTap.onNext(indexPath)
let vc = MenuEditGroupViewController()
self?.navigationController?.pushViewController(vc, animated: true)
})
.disposed(by: cell.disposeBag)
return cell
})
}()
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.delegate = self
tableView.separatorStyle = .singleLine
tableView.separatorColor = UIColor.gray
tableView.estimatedSectionFooterHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.showsHorizontalScrollIndicator = false
tableView.showsVerticalScrollIndicator = false
let headerView = UIView(frame: CGRect(x: 0,
y: 0,
width: self.view.frame.size.width,
height: 16))
tableView.tableHeaderView = headerView
tableView.register(LabelButtonCell.self, forCellReuseIdentifier: "LabelButtonCell")
return tableView
}()
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
extension ViewController {
struct CellSectionModel {
var items: [Item]
}
}
extension ViewController.CellSectionModel: SectionModelType {
typealias Item = GroupModel
init(original: ViewController.CellSectionModel, items: [Item]) {
self = original
self.items = items
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
//
// MvvmDemoTests.swift
// MvvmDemoTests
//
// Created by 徐强强 on 2019/1/31.
// Copyright © 2019年 徐强强. All rights reserved.
//
import XCTest
@testable import MvvmDemo
class MvvmDemoTests: XCTestCase {
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
//
// MvvmDemoUITests.swift
// MvvmDemoUITests
//
// Created by 徐强强 on 2019/1/31.
// Copyright © 2019年 徐强强. All rights reserved.
//
import XCTest
class MvvmDemoUITests: XCTestCase {
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
XCUIApplication().launch()
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() {
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
}
# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
target 'MvvmDemo' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
pod 'SnapKit', '~> 4.0.0'
pod 'RxCocoa'
pod 'Moya/RxSwift'
pod 'ObjectMapper'
pod 'RxDataSources'
target 'MvvmDemoTests' do
inherit! :search_paths
# Pods for testing
end
target 'MvvmDemoUITests' do
inherit! :search_paths
# Pods for testing
end
end
PODS:
- Alamofire (4.8.2)
- Differentiator (3.1.0)
- Moya/Core (13.0.1):
- Alamofire (~> 4.1)
- Result (~> 4.1)
- Moya/RxSwift (13.0.1):
- Moya/Core
- RxSwift (~> 4.0)
- ObjectMapper (3.5.1)
- Result (4.1.0)
- RxCocoa (4.5.0):
- RxSwift (>= 4.4.2, ~> 4.4)
- RxDataSources (3.1.0):
- Differentiator (~> 3.0)
- RxCocoa (~> 4.0)
- RxSwift (~> 4.0)
- RxSwift (4.5.0)
- SnapKit (4.0.1)
DEPENDENCIES:
- Moya/RxSwift
- ObjectMapper
- RxCocoa
- RxDataSources
- SnapKit (~> 4.0.0)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- Alamofire
- Differentiator
- Moya
- ObjectMapper
- Result
- RxCocoa
- RxDataSources
- RxSwift
- SnapKit
SPEC CHECKSUMS:
Alamofire: ae5c501addb7afdbb13687d7f2f722c78734c2d3
Differentiator: be49ca3408f0ecfc761e4c7763d20c62be01b9ad
Moya: f4a4b80ff2f8a4ffc208dfb31cd91636622fee6e
ObjectMapper: 70187b8941977c62ccfb423caf6b50be405cabf0
Result: bd966fac789cc6c1563440b348ab2598cc24d5c7
RxCocoa: cbf70265dc65a981d4ac982e513c10cf23df24a0
RxDataSources: a843bad90c29817f5923ec8163f4af2de084ceb3
RxSwift: f172070dfd1a93d70a9ab97a5a01166206e1c575
SnapKit: 0de968a9fec17499afa29683b05d0c775b6d1c29
PODFILE CHECKSUM: cfcf34482910926a320289f6084a2355f4301a19
COCOAPODS: 1.7.3
# Mvvm-RxSwift
iOS采用RxSwift实现数据绑定的Mvvm架构
pod install
......@@ -48,7 +48,7 @@ class AudioBoxViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
test()
// test()
buildUI()
buildNavbar()
performBinding()
......@@ -128,6 +128,7 @@ extension AudioBoxViewController {
.subscribeNext(weak: self, { (self) -> ([MusicSheetInfo]) -> Void in
return { (resps) in
print(resps)
self.bannerView.configureWith(value: resps)
}
})
// .subscribeNext(weak: self) { (self) in
......@@ -136,6 +137,18 @@ extension AudioBoxViewController {
// }
// }
.disposed(by: disposeBag)
AudioProvider
.fetchAudioSheets()
.materialize()
.elements()
.subscribeNext(weak: self) { (self) -> ([MusicSheetInfo]) -> Void in
return { (resps) in
self.dataSource.load(audioSheetList: resps)
self.collectionView.reloadData()
}
}
// 去音乐搜索
viewModel.outputs.goAudioSearch
......@@ -218,7 +231,10 @@ extension AudioBoxViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let value = dataSource[indexPath]
if let sheet = value as? MusicSheetInfo {
viewModel.inputs.tappedAudioSheet(sheet)
// viewModel.inputs.tappedAudioSheet(sheet)
let vc = AudioSheetListViewController()
vc.audioSheet = sheet
self.navigationController?.pushViewController(vc, animated: true)
}
}
......@@ -272,7 +288,11 @@ extension AudioBoxViewController: UICollectionViewDelegateFlowLayout {
extension AudioBoxViewController: GLarkBannerViewDelegate {
func bannerViewDidTapped(at audioSheet: MusicSheetInfo) {
viewModel.inputs.tappedAudioSheet(audioSheet)
// viewModel.inputs.tappedAudioSheet(audioSheet)
let vc = AudioSheetListViewController()
vc.audioSheet = audioSheet
self.navigationController?.pushViewController(vc, animated: true)
}
}
......
platform :ios, '9.0'
target 'swiftdemo2' do
use_frameworks!
pod 'MBProgressHUD'
pod 'HandyJSON'
pod 'RxSwift'
pod 'Moya/RxSwift'
pod 'RxDataSources'
pod 'RxSwiftExt', '~> 3.4.0'
end
PODS:
- Alamofire (4.8.2)
- Differentiator (3.1.0)
- HandyJSON (5.0.0)
- MBProgressHUD (1.1.0)
- Moya/Core (13.0.1):
- Alamofire (~> 4.1)
- Result (~> 4.1)
- Moya/RxSwift (13.0.1):
- Moya/Core
- RxSwift (~> 4.0)
- Result (4.1.0)
- RxCocoa (4.5.0):
- RxSwift (>= 4.4.2, ~> 4.4)
- RxDataSources (3.1.0):
- Differentiator (~> 3.0)
- RxCocoa (~> 4.0)
- RxSwift (~> 4.0)
- RxSwift (4.5.0)
- RxSwiftExt (3.4.0):
- RxSwiftExt/Core (= 3.4.0)
- RxSwiftExt/RxCocoa (= 3.4.0)
- RxSwiftExt/Core (3.4.0):
- RxSwift (~> 4.0)
- RxSwiftExt/RxCocoa (3.4.0):
- RxCocoa (~> 4.0)
- RxSwiftExt/Core
DEPENDENCIES:
- HandyJSON
- MBProgressHUD
- Moya/RxSwift
- RxDataSources
- RxSwift
- RxSwiftExt (~> 3.4.0)
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- Alamofire
- Differentiator
- HandyJSON
- MBProgressHUD
- Moya
- Result
- RxCocoa
- RxDataSources
- RxSwift
- RxSwiftExt
SPEC CHECKSUMS:
Alamofire: ae5c501addb7afdbb13687d7f2f722c78734c2d3
Differentiator: be49ca3408f0ecfc761e4c7763d20c62be01b9ad
HandyJSON: 992f2d661716d6a91de82d0056927bc7813b5770
MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
Moya: f4a4b80ff2f8a4ffc208dfb31cd91636622fee6e
Result: bd966fac789cc6c1563440b348ab2598cc24d5c7
RxCocoa: cbf70265dc65a981d4ac982e513c10cf23df24a0
RxDataSources: a843bad90c29817f5923ec8163f4af2de084ceb3
RxSwift: f172070dfd1a93d70a9ab97a5a01166206e1c575
RxSwiftExt: 01f8ecbeeb355698e9c75365ebe908b00dacf45d
PODFILE CHECKSUM: 1741f8e9065b569b43edaa477a31ace2636aaf4e
COCOAPODS: 1.7.3
//
// AppDelegate.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/28.
// Copyright © 2019 admin. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// if #available(iOS 13.0, *) {
// // MARK: UISceneSession Lifecycle
//
// func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// // Called when a new scene session is being created.
// // Use this method to select a configuration to create the new scene with.
// return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
// }
//
// func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// // Called when the user discards a scene session.
// // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
// }
// }
}
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
//
// BSAPI.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/29.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
import RxSwift
import Moya
enum APIManager {
case submenus //精华目录
case jhrecommend(json:String) /// 精华推荐列表
case jhimage(json:String) /// 精华图片专区列表
case jhvideo(json:String) /// 精华视频专区列表
case jhremen(json:String) /// 精华排行专区列表
case jhjoke(json:String) /// 精华笑话专区列表
case detailCommentList(id:String,page:String , json:String) /// 详情评论列表
}
extension String {
static func jhRequest(type:String, json:String) -> APIManager {
switch type {
case "推荐":
return .jhrecommend(json: json)
case "视频":
return .jhvideo(json: json)
case "图片":
return .jhimage(json: json)
case "笑话":
return .jhjoke(json: json)
case "排行":
return .jhremen(json: json)
default:
return .jhrecommend(json: json)
}
}
}
extension APIManager: TargetType {
var baseURL: URL {
switch self {
case .submenus:
return URL.init(string: "http://s.budejie.com")!
case .detailCommentList:
return URL.init(string: "http://c.api.budejie.com")!
default:
return URL.init(string: "http://s.budejie.com")!
}
}
var path: String {
switch self {
case .submenus: return "public/list-appbar/bs0315-iphone-4.5.9"
case .jhimage(let json): return "topic/list/jingxuan/10/bs0315-iphone-4.5.9/\(json)"
case .jhvideo(let json): return "topic/list/jingxuan/41/bs0315-iphone-4.5.9/\(json)"
case .jhremen(let json): return "topic/list/remen/1/bs0315-iphone-4.5.9/\(json)"
case .jhjoke(let json): return "topic/tag-topic/63674/hot/bs0315-iphone-4.5.9/\(json)"
case .jhrecommend(let json): return "topic/list/jingxuan/1/bs0315-iphone-4.5.9/\(json)"
case .detailCommentList(let id ,let page , let json): return "topic/comment_list/\(id)/\(page)/bs0315-iphone-4.5.9/\(json)"
}
}
var method: Moya.Method {
return .get
}
var sampleData: Data {
return "".data(using: String.Encoding.utf8)!
}
var task: Task {
let parameters = ["version": Bundle.main.infoDictionary!["CFBundleShortVersionString"]!]
return .requestParameters(parameters: parameters, encoding: URLEncoding.default)
}
var headers: [String : String]? {
return nil
}
}
let LoadingPlugin = NetworkActivityPlugin { (type, target) in
}
let bsLoadingProvider = MoyaProvider<APIManager>()
//
// BSEntity.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/29.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
import HandyJSON
import RxDataSources
/// 个人信息
struct UEntity: HandyJSON {
var header: [String]? //头像
var uid:String? // uid
var is_vip:Bool? // 是否会员
var sex:String? //性别
var name:String? //昵称
}
/// 置顶的评论
struct Top_commentsEntity: HandyJSON {
var u: UEntity? // 个人信息
var id: String? //id
var content:String? // 评论内容
var passtime: String? //更新时间
var like_count: String? //点赞人数
}
/// 视频
struct VideoEntity: HandyJSON {
var video: [String]?
var thumbnail: [String]? // 封面图
var width: CGFloat?
var height: CGFloat?
}
/// 图片
struct ImageEntity: HandyJSON {
var big: [String]? // 封面图
var width: CGFloat?
var height: CGFloat?
}
/// 动态图
struct GifEntity: HandyJSON {
var images: [String]?
var gif_thumbnail: [String]? //封面图
var width: CGFloat?
var height: CGFloat?
}
struct JHListEntity: HandyJSON {
var status:String?
var cate:String?
var name:String?
var top_comments:[Top_commentsEntity]?
var bookmark:String? //阅读数
var text:String?
var video:VideoEntity?
var u:UEntity?
var passtime:String?
var type:String? //类型: text image video gif
var id:String?
var image:ImageEntity?
var gif:GifEntity?
var comment:String? //评论
var up:String? //点赞
var down:String? //踩
var forward:String? //分享
// var tags:[tagsEntity]? //社区类型
}
struct Info: HandyJSON {
var count:String?
var np:String? //上拉加载需要拼接的随机数据
}
/// 精华推荐列表
struct JHRecommendListEntity: HandyJSON{
var info: Info?
var list:[JHListEntity]?
}
struct GroupedJHSection {
var header: JHListEntity?
var items: [Item]
}
extension GroupedJHSection: SectionModelType {
typealias Item = Top_commentsEntity
init(original: Self, items: [Self.Item]) {
self = original
self.items = items
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
//
// AudioBoxViewModel.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/29.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
import RxSwift
import RxSwiftExt
protocol AudioBoxViewModelInputs {
func beginRefreshing()
}
protocol AudioBoxViewModelOutputs {
var bannerLoaded: Observable<[MusicSheetInfo]> { get }
}
protocol AudioBoxViewModelType {
var inputs: AudioBoxViewModelInputs { get }
var outputs: AudioBoxViewModelOutputs { get }
}
class AudioBoxViewModel: AudioBoxViewModelType
,AudioBoxViewModelInputs
,AudioBoxViewModelOutputs {
var inputs: AudioBoxViewModelInputs {
return self
}
var outputs: AudioBoxViewModelOutputs {
return self
}
init() {
let refresh = refreshRelay
.asObservable()
.share()
let bannerReq = refresh
.flatMap {
AudioProvider
.fetchAudioBanners()
.materialize()
}.share()
bannerLoaded = bannerReq.elements()
// bannerLoaded = refresh.flatMap({
// AudioProvider
// .fetchAudioBanners()
// })
print(bannerLoaded)
}
fileprivate let refreshRelay = PublishSubject<Void>()
func beginRefreshing() {
refreshRelay.onNext(Void())
}
var bannerLoaded: Observable<[MusicSheetInfo]>
}
//
// AudioObjects.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/29.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
public class MusicSheetInfo: NSObject {
public var title: String?
public var imgUrl: String?
public var type: Int = 1
}
//
// AudioProvider.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/29.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
import RxSwift
public struct AudioProvider {}
extension AudioProvider {
public static func fetchAudioBanners() -> Observable<[MusicSheetInfo]> {
let ballad = MusicSheetInfo()
ballad.type = 23
ballad.title = "情歌对唱榜"
ballad.imgUrl = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554616722369&di=cdfdf3fc4c951c44e40b9d63cad0a2a9&imgtype=0&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Farchive%2F82fe4556b587af3350ff80d56bf803eac661d75f.jpg"
let video = MusicSheetInfo()
video.type = 24
video.title = "影视金曲榜"
video.imgUrl = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554618318731&di=88c00b69e53bd3435c89a1193aec0ab7&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201404%2F23%2F20140423102400_LQEFH.thumb.700_0.jpeg"
let net = MusicSheetInfo()
net.type = 25
net.title = "网络歌曲"
net.imgUrl = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554618360373&di=f921a927689004dacc3461b3329ede95&imgtype=0&src=http%3A%2F%2Fimg15.3lian.com%2F2015%2Ff2%2F63%2Fd%2F93.jpg"
return Observable.just([ballad, video, net])
}
}
//
// NotificationMy.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/28.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
public final class ZY<Base> {
public let base: Base
public init(_ base: Base) {
self.base = base
}
}
public protocol ZYCompatible {
associatedtype CompatibleType
var zy: CompatibleType { get }
}
public extension ZYCompatible {
public var zy: ZY<Self> {
get {
return ZY(self)
}
}
}
//extension NotificationCenter: ZYCompatible {}
//
//extension ZY where Base: NotificationCenter {
// func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil) {
// print("1111")
// self.base.post(name: aName, object: anObject, userInfo: aUserInfo)
// }
//
//}
//
// ObservableType+HandyJSON.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/29.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
import RxSwift
import Moya
import HandyJSON
extension Response {
func mapModel<T: HandyJSON>(_ type: T.Type) -> T {
let jsonString = String.init(data: data, encoding: .utf8)
return JSONDeserializer<T>.deserializeFrom(json: jsonString)!
}
}
extension ObservableType where E == Response {
public func mapModel<T: HandyJSON>(_ type: T.Type) -> Observable<T> {
return flatMap { (response) -> Observable<T> in
return Observable.just(response.mapModel(T.self))
}
}
}
extension Observable {
func showErrorToast() -> Observable<Element> {
return self.do(onNext: { (response) in
print("showErrorToast 11")
}, onError: { (error) in
print("showErrorToast 22")
})
}
}
//
// RecommendListCell.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/29.
// Copyright © 2019 admin. All rights reserved.
//
import UIKit
class RecommendListCell: UITableViewCell {
static func getCellHightData(data:Top_commentsEntity) -> CGFloat {
return 60
// let attributedText = self.init().contentAttributedText(data: data)
// let hight = self.init().contextHight(attributedText: attributedText)
// return hight + 8
}
func reloadData(data:Top_commentsEntity) -> Void {
let content = String.init(format: "%@: %@", data.u?.name ?? "" , data.content ?? "")
self.textLabel?.text = content
// let attributedText = self.contentAttributedText(data: data)
// let hight = self.contextHight(attributedText: attributedText)
// labContent.attributedText = attributedText
// bgView.frame = CGRect(x: 15, y: 0, width: kScreenWidth - 30, height: hight + 8)
// labContent.frame = CGRect(x: 15, y: 8, width: kScreenWidth - 60, height: hight)
}
}
//
// RecommendListViewModel.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/29.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
import UIKit
import RxSwift
class RecommendListViewModel: NSObject {
let data = Variable<[GroupedJHSection]>([])
let refreshEnd = PublishSubject<Void>()
var json:String!
override init() {
super.init()
}
func reload(type:String, json:String) {
let api = String.jhRequest(type: type, json: json)
bsLoadingProvider
.rx.request(api)
.asObservable().mapModel(JHRecommendListEntity.self)
.showErrorToast()
.subscribe(onNext: { [weak self] (model) in
print("reload 11")
let jsonprefix:String = model.info?.np ?? ""
let jsonStr = String.init(format: "%@-20.json", jsonprefix)
self?.json = jsonStr
let arr:[JHListEntity] = model.list ?? []
var seconArr:[GroupedJHSection] = []
for entity in arr {
print(entity)
let con:[Top_commentsEntity] = entity.top_comments ?? []
seconArr.append(GroupedJHSection(header: entity, items: con))
}
if json == "0-20.json" {
self?.data.value = seconArr
} else {
self?.data.value += seconArr
}
self?.refreshEnd.onNext(())
}, onError: { [weak self] (error) in
print("reload 22")
self?.refreshEnd.onNext(())
})
}
}
//
// SceneDelegate.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/28.
// Copyright © 2019 admin. All rights reserved.
//
import UIKit
//if #available(iOS 13.0, *) {
//class SceneDelegate: UIResponder, UIWindowSceneDelegate {
//
// var window: UIWindow?
//
//
// func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// guard let _ = (scene as? UIWindowScene) else { return }
// }
//
// func sceneDidDisconnect(_ scene: UIScene) {
// // Called as the scene is being released by the system.
// // This occurs shortly after the scene enters the background, or when its session is discarded.
// // Release any resources associated with this scene that can be re-created the next time the scene connects.
// // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
// }
//
// func sceneDidBecomeActive(_ scene: UIScene) {
// // Called when the scene has moved from an inactive state to an active state.
// // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
// }
//
// func sceneWillResignActive(_ scene: UIScene) {
// // Called when the scene will move from an active state to an inactive state.
// // This may occur due to temporary interruptions (ex. an incoming phone call).
// }
//
// func sceneWillEnterForeground(_ scene: UIScene) {
// // Called as the scene transitions from the background to the foreground.
// // Use this method to undo the changes made on entering the background.
// }
//
// func sceneDidEnterBackground(_ scene: UIScene) {
// // Called as the scene transitions from the foreground to the background.
// // Use this method to save data, release shared resources, and store enough scene-specific state information
// // to restore the scene back to its current state.
// }
//
//
//}
//}
//
// SwiftyHUD.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/28.
// Copyright © 2019 admin. All rights reserved.
//
import Foundation
import MBProgressHUD
public struct SwiftyHUD {
public static func show(message: String?,
duration: TimeInterval = 3,
textColor: UIColor = .white,
bezelAlpha: CGFloat = 0.6,
addedTo view: UIView? = UIApplication.shared.windows[0]) {
guard let sourceView = view else { return }
let hud = MBProgressHUD.showAdded(to: sourceView, animated: true)
hud.isUserInteractionEnabled = false
hud.bezelView.backgroundColor = UIColor.black.withAlphaComponent(bezelAlpha)
hud.mode = .text
hud.label.font = .systemFont(ofSize: 14)
hud.label.textColor = textColor
hud.label.numberOfLines = 0
hud.label.text = message
hud.margin = 8
hud.hide(animated: true, afterDelay: duration)
}
}
//
// ViewController.swift
// swiftdemo2
//
// Created by 佰锐 on 2019/10/28.
// Copyright © 2019 admin. All rights reserved.
//
import UIKit
import RxSwift
import RxDataSources
class ViewController: UIViewController {
private let viewModel: AudioBoxViewModelType = AudioBoxViewModel()
override func viewDidLoad() {
super.viewDidLoad()
performBinding()
beginRefreshing()
}
}
extension ViewController {
private func performBinding() {
viewModel.outputs.bannerLoaded.subscribeNext(weak: self) { (self) -> ([MusicSheetInfo]) -> Void in
return { (resps) in
print(resps)
}
}
}
}
extension ViewController {
private func beginRefreshing() {
viewModel.inputs.beginRefreshing()
}
}
//extension ViewController {
// var type: String!
// let viewModel = RecommendListViewModel.init()
// var dataSource: RxTableViewSectionedReloadDataSource<GroupedJHSection>!
//
// lazy var tableView: UITableView = {
// let tableView = UITableView.init(frame: view.bounds, style: .grouped)
// tableView.register(RecommendListCell.self, forCellReuseIdentifier: "RecommendListCell")
// tableView.backgroundColor = UIColor.yellow
// tableView.separatorStyle = .none
// //tableView.tableFooterView = UIView.init()
// tableView.rowHeight = 50
// return tableView
// }()
//
// override func viewDidLoad() {
// super.viewDidLoad()
// // Do any additional setup after loading the view.
// // let aa = ZY<NotificationCenter>(NotificationCenter.default)
// // aa.post(name: NSNotification.Name("userLogin"), object: nil)
// // NotificationCenter.default.zy.post(name: NSNotification.Name("userLogin"), object: nil)
//
// view.backgroundColor = UIColor.white
// view.addSubview(tableView)
//
// // tableView.snp.makeConstraints { (make) in
// // make.top.left.bottom.right.equalTo(view).offset(0)
// // }
//
// initUI()
// bindModel()
//
//
//
//
// // self.type = "推荐"
// //
// // self.viewModel.json = "0-20.json"
// // self.viewModel.reload(type:self.type ?? "", json: self.viewModel.json ?? "")
// }
//
// func initUI() {
//
//
// }
//
// func bindModel() {
// dataSource = RxTableViewSectionedReloadDataSource<GroupedJHSection>(
// configureCell: { (ds, tableView, index, model) -> UITableViewCell in
// let cell = tableView.dequeueReusableCell(withIdentifier: "RecommendListCell", for: index) as! RecommendListCell
// cell.reloadData(data: model)
// return cell
// })
//
// tableView.rx.setDelegate(self)
//
// viewModel.data
// .asObservable()
// .asDriver(onErrorJustReturn: [])
// .drive(tableView.rx.items(dataSource: dataSource))
//
// viewModel.refreshEnd.subscribe(onNext: { () in
//
// })
//
// }
//
// override func viewWillAppear(_ animated: Bool) {
// super.viewWillAppear(animated)
//
// SwiftyHUD.show(message: "歌曲已存在")
// }
//}
//extension ViewController : UITableViewDelegate {
// func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// let items = dataSource[indexPath.section].items
// let model = items[indexPath.row]
// return RecommendListCell.getCellHightData(data:model)
// }
//}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册